1 /* 2 * Copyright (c) 2011, Google Inc. All rights reserved. 3 * 4 * See file CREDITS for list of people who contributed to this 5 * project. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 20 * MA 02111-1307 USA 21 */ 22 23 24 /* 25 * This module records the progress of boot and arbitrary commands, and 26 * permits accurate timestamping of each. 27 * 28 * TBD: Pass timings to kernel in the FDT 29 */ 30 31 #include <common.h> 32 #include <libfdt.h> 33 34 DECLARE_GLOBAL_DATA_PTR; 35 36 enum bootstage_flags { 37 BOOTSTAGEF_ERROR = 1 << 0, /* Error record */ 38 BOOTSTAGEF_ALLOC = 1 << 1, /* Allocate an id */ 39 }; 40 41 struct bootstage_record { 42 ulong time_us; 43 const char *name; 44 int flags; /* see enum bootstage_flags */ 45 enum bootstage_id id; 46 }; 47 48 static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} }; 49 static int next_id = BOOTSTAGE_ID_USER; 50 51 ulong bootstage_add_record(enum bootstage_id id, const char *name, 52 int flags) 53 { 54 struct bootstage_record *rec; 55 ulong mark = timer_get_boot_us(); 56 57 if (flags & BOOTSTAGEF_ALLOC) 58 id = next_id++; 59 60 if (id < BOOTSTAGE_ID_COUNT) { 61 rec = &record[id]; 62 63 /* Only record the first event for each */ 64 if (!rec->time_us) { 65 rec->time_us = mark; 66 rec->name = name; 67 rec->flags = flags; 68 rec->id = id; 69 } 70 } 71 72 /* Tell the board about this progress */ 73 show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id); 74 return mark; 75 } 76 77 78 ulong bootstage_mark(enum bootstage_id id) 79 { 80 return bootstage_add_record(id, NULL, 0); 81 } 82 83 ulong bootstage_error(enum bootstage_id id) 84 { 85 return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR); 86 } 87 88 ulong bootstage_mark_name(enum bootstage_id id, const char *name) 89 { 90 int flags = 0; 91 92 if (id == BOOTSTAGE_ID_ALLOC) 93 flags = BOOTSTAGEF_ALLOC; 94 return bootstage_add_record(id, name, flags); 95 } 96 97 static void print_time(unsigned long us_time) 98 { 99 char str[15], *s; 100 int grab = 3; 101 102 /* We don't seem to have %'d in U-Boot */ 103 sprintf(str, "%12lu", us_time); 104 for (s = str + 3; *s; s += grab) { 105 if (s != str + 3) 106 putc(s[-1] != ' ' ? ',' : ' '); 107 printf("%.*s", grab, s); 108 grab = 3; 109 } 110 } 111 112 static uint32_t print_time_record(enum bootstage_id id, 113 struct bootstage_record *rec, uint32_t prev) 114 { 115 print_time(rec->time_us); 116 print_time(rec->time_us - prev); 117 if (rec->name) 118 printf(" %s\n", rec->name); 119 else if (id >= BOOTSTAGE_ID_USER) 120 printf(" user_%d\n", id - BOOTSTAGE_ID_USER); 121 else 122 printf(" id=%d\n", id); 123 return rec->time_us; 124 } 125 126 static int h_compare_record(const void *r1, const void *r2) 127 { 128 const struct bootstage_record *rec1 = r1, *rec2 = r2; 129 130 return rec1->time_us > rec2->time_us ? 1 : -1; 131 } 132 133 void bootstage_report(void) 134 { 135 struct bootstage_record *rec = record; 136 int id; 137 uint32_t prev; 138 139 puts("Timer summary in microseconds:\n"); 140 printf("%11s%11s %s\n", "Mark", "Elapsed", "Stage"); 141 142 /* Fake the first record - we could get it from early boot */ 143 rec->name = "reset"; 144 rec->time_us = 0; 145 prev = print_time_record(BOOTSTAGE_ID_AWAKE, rec, 0); 146 147 /* Sort records by increasing time */ 148 qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record); 149 150 for (id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { 151 if (rec->time_us != 0) 152 prev = print_time_record(rec->id, rec, prev); 153 } 154 if (next_id > BOOTSTAGE_ID_COUNT) 155 printf("(Overflowed internal boot id table by %d entries\n" 156 "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", 157 next_id - BOOTSTAGE_ID_COUNT); 158 } 159 160 ulong __timer_get_boot_us(void) 161 { 162 static ulong base_time; 163 164 /* 165 * We can't implement this properly. Return 0 on the first call and 166 * larger values after that. 167 */ 168 if (base_time) 169 return get_timer(base_time) * 1000; 170 base_time = get_timer(0); 171 return 0; 172 } 173 174 ulong timer_get_boot_us(void) 175 __attribute__((weak, alias("__timer_get_boot_us"))); 176