mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-23 22:14:54 +00:00
Merge branch 'agust@denx.de-next' of git://git.denx.de/u-boot-staging
This commit is contained in:
commit
bd23b22bad
6 changed files with 517 additions and 19 deletions
25
README
25
README
|
@ -2391,6 +2391,31 @@ The following options need to be configured:
|
||||||
29,916,167 26,005,792 bootm_start
|
29,916,167 26,005,792 bootm_start
|
||||||
30,361,327 445,160 start_kernel
|
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
|
||||||
|
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:
|
Legacy uImage format:
|
||||||
|
|
||||||
Arg Where When
|
Arg Where When
|
||||||
|
|
|
@ -96,6 +96,9 @@ static void announce_and_cleanup(void)
|
||||||
{
|
{
|
||||||
printf("\nStarting kernel ...\n\n");
|
printf("\nStarting kernel ...\n\n");
|
||||||
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
|
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
|
||||||
|
#ifdef CONFIG_BOOTSTAGE_FDT
|
||||||
|
bootstage_fdt_add_report();
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_BOOTSTAGE_REPORT
|
#ifdef CONFIG_BOOTSTAGE_REPORT
|
||||||
bootstage_report();
|
bootstage_report();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -68,6 +68,7 @@ COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o
|
||||||
COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o
|
COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o
|
||||||
COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o
|
COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o
|
||||||
COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.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_CACHE) += cmd_cache.o
|
||||||
COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o
|
COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o
|
||||||
COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o
|
COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o
|
||||||
|
|
|
@ -33,13 +33,9 @@
|
||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
enum bootstage_flags {
|
|
||||||
BOOTSTAGEF_ERROR = 1 << 0, /* Error record */
|
|
||||||
BOOTSTAGEF_ALLOC = 1 << 1, /* Allocate an id */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bootstage_record {
|
struct bootstage_record {
|
||||||
ulong time_us;
|
ulong time_us;
|
||||||
|
uint32_t start_us;
|
||||||
const char *name;
|
const char *name;
|
||||||
int flags; /* see enum bootstage_flags */
|
int flags; /* see enum bootstage_flags */
|
||||||
enum bootstage_id id;
|
enum bootstage_id id;
|
||||||
|
@ -48,11 +44,22 @@ struct bootstage_record {
|
||||||
static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} };
|
static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} };
|
||||||
static int next_id = BOOTSTAGE_ID_USER;
|
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,
|
ulong bootstage_add_record(enum bootstage_id id, const char *name,
|
||||||
int flags)
|
int flags, ulong mark)
|
||||||
{
|
{
|
||||||
struct bootstage_record *rec;
|
struct bootstage_record *rec;
|
||||||
ulong mark = timer_get_boot_us();
|
|
||||||
|
|
||||||
if (flags & BOOTSTAGEF_ALLOC)
|
if (flags & BOOTSTAGEF_ALLOC)
|
||||||
id = next_id++;
|
id = next_id++;
|
||||||
|
@ -77,12 +84,13 @@ ulong bootstage_add_record(enum bootstage_id id, const char *name,
|
||||||
|
|
||||||
ulong bootstage_mark(enum bootstage_id id)
|
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)
|
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)
|
ulong bootstage_mark_name(enum bootstage_id id, const char *name)
|
||||||
|
@ -91,7 +99,26 @@ ulong bootstage_mark_name(enum bootstage_id id, const char *name)
|
||||||
|
|
||||||
if (id == BOOTSTAGE_ID_ALLOC)
|
if (id == BOOTSTAGE_ID_ALLOC)
|
||||||
flags = BOOTSTAGEF_ALLOC;
|
flags = BOOTSTAGEF_ALLOC;
|
||||||
return bootstage_add_record(id, name, flags);
|
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)
|
static void print_time(unsigned long us_time)
|
||||||
|
@ -109,17 +136,41 @@ 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,
|
static uint32_t print_time_record(enum bootstage_id id,
|
||||||
struct bootstage_record *rec, uint32_t prev)
|
struct bootstage_record *rec, uint32_t prev)
|
||||||
{
|
{
|
||||||
print_time(rec->time_us);
|
char buf[20];
|
||||||
print_time(rec->time_us - prev);
|
|
||||||
if (rec->name)
|
if (prev == -1U) {
|
||||||
printf(" %s\n", rec->name);
|
printf("%11s", "");
|
||||||
else if (id >= BOOTSTAGE_ID_USER)
|
print_time(rec->time_us);
|
||||||
printf(" user_%d\n", id - BOOTSTAGE_ID_USER);
|
} else {
|
||||||
else
|
print_time(rec->time_us);
|
||||||
printf(" id=%d\n", id);
|
print_time(rec->time_us - prev);
|
||||||
|
}
|
||||||
|
printf(" %s\n", get_record_name(buf, sizeof(buf), rec));
|
||||||
|
|
||||||
return rec->time_us;
|
return rec->time_us;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +181,70 @@ static int h_compare_record(const void *r1, const void *r2)
|
||||||
return rec1->time_us > rec2->time_us ? 1 : -1;
|
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)
|
void bootstage_report(void)
|
||||||
{
|
{
|
||||||
struct bootstage_record *rec = record;
|
struct bootstage_record *rec = record;
|
||||||
|
@ -148,13 +263,19 @@ void bootstage_report(void)
|
||||||
qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record);
|
qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record);
|
||||||
|
|
||||||
for (id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) {
|
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);
|
prev = print_time_record(rec->id, rec, prev);
|
||||||
}
|
}
|
||||||
if (next_id > BOOTSTAGE_ID_COUNT)
|
if (next_id > BOOTSTAGE_ID_COUNT)
|
||||||
printf("(Overflowed internal boot id table by %d entries\n"
|
printf("(Overflowed internal boot id table by %d entries\n"
|
||||||
"- please increase CONFIG_BOOTSTAGE_USER_COUNT\n",
|
"- please increase CONFIG_BOOTSTAGE_USER_COUNT\n",
|
||||||
next_id - BOOTSTAGE_ID_COUNT);
|
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)
|
ulong __timer_get_boot_us(void)
|
||||||
|
@ -173,3 +294,150 @@ ulong __timer_get_boot_us(void)
|
||||||
|
|
||||||
ulong timer_get_boot_us(void)
|
ulong timer_get_boot_us(void)
|
||||||
__attribute__((weak, alias("__timer_get_boot_us")));
|
__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;
|
||||||
|
}
|
||||||
|
|
116
common/cmd_bootstage.c
Normal file
116
common/cmd_bootstage.c
Normal file
|
@ -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"
|
||||||
|
);
|
|
@ -31,6 +31,12 @@
|
||||||
#define CONFIG_BOOTSTAGE_USER_COUNT 20
|
#define CONFIG_BOOTSTAGE_USER_COUNT 20
|
||||||
#endif
|
#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
|
* 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
|
* state that we are at, and the action that we are about to perform. For
|
||||||
|
@ -181,6 +187,7 @@ enum bootstage_id {
|
||||||
* rough boot timing information.
|
* rough boot timing information.
|
||||||
*/
|
*/
|
||||||
BOOTSTAGE_ID_AWAKE,
|
BOOTSTAGE_ID_AWAKE,
|
||||||
|
BOOTSTAGE_ID_START_SPL,
|
||||||
BOOTSTAGE_ID_START_UBOOT_F,
|
BOOTSTAGE_ID_START_UBOOT_F,
|
||||||
BOOTSTAGE_ID_START_UBOOT_R,
|
BOOTSTAGE_ID_START_UBOOT_R,
|
||||||
BOOTSTAGE_ID_USB_START,
|
BOOTSTAGE_ID_USB_START,
|
||||||
|
@ -192,11 +199,15 @@ enum bootstage_id {
|
||||||
BOOTSTAGE_ID_MAIN_LOOP,
|
BOOTSTAGE_ID_MAIN_LOOP,
|
||||||
BOOTSTAGE_KERNELREAD_START,
|
BOOTSTAGE_KERNELREAD_START,
|
||||||
BOOTSTAGE_KERNELREAD_STOP,
|
BOOTSTAGE_KERNELREAD_STOP,
|
||||||
|
BOOTSTAGE_ID_BOARD_INIT,
|
||||||
|
BOOTSTAGE_ID_BOARD_INIT_DONE,
|
||||||
|
|
||||||
BOOTSTAGE_ID_CPU_AWAKE,
|
BOOTSTAGE_ID_CPU_AWAKE,
|
||||||
BOOTSTAGE_ID_MAIN_CPU_AWAKE,
|
BOOTSTAGE_ID_MAIN_CPU_AWAKE,
|
||||||
BOOTSTAGE_ID_MAIN_CPU_READY,
|
BOOTSTAGE_ID_MAIN_CPU_READY,
|
||||||
|
|
||||||
|
BOOTSTAGE_ID_ACCUM_LCD,
|
||||||
|
|
||||||
/* a few spare for the user, from here */
|
/* a few spare for the user, from here */
|
||||||
BOOTSTAGE_ID_USER,
|
BOOTSTAGE_ID_USER,
|
||||||
BOOTSTAGE_ID_COUNT = BOOTSTAGE_ID_USER + CONFIG_BOOTSTAGE_USER_COUNT,
|
BOOTSTAGE_ID_COUNT = BOOTSTAGE_ID_USER + CONFIG_BOOTSTAGE_USER_COUNT,
|
||||||
|
@ -225,6 +236,17 @@ void show_boot_progress(int val);
|
||||||
#if defined(CONFIG_BOOTSTAGE) && !defined(CONFIG_SPL_BUILD)
|
#if defined(CONFIG_BOOTSTAGE) && !defined(CONFIG_SPL_BUILD)
|
||||||
/* This is the full bootstage implementation */
|
/* 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.
|
* Mark a time stamp for the current boot stage.
|
||||||
*/
|
*/
|
||||||
|
@ -234,9 +256,64 @@ ulong bootstage_error(enum bootstage_id id);
|
||||||
|
|
||||||
ulong bootstage_mark_name(enum bootstage_id id, const char *name);
|
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 */
|
/* Print a report about boot time */
|
||||||
void bootstage_report(void);
|
void bootstage_report(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add bootstage information to the device tree
|
||||||
|
*
|
||||||
|
* @return 0 if ok, -ve on error
|
||||||
|
*/
|
||||||
|
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
|
#else
|
||||||
/*
|
/*
|
||||||
* This is a dummy implementation which just calls show_boot_progress(),
|
* This is a dummy implementation which just calls show_boot_progress(),
|
||||||
|
@ -260,7 +337,15 @@ static inline ulong bootstage_mark_name(enum bootstage_id id, const char *name)
|
||||||
return 0;
|
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 /* CONFIG_BOOTSTAGE */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue