1 /* 2 * (C) Copyright 2002 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <stdio_dev.h> 10 #include <watchdog.h> 11 #include <div64.h> 12 #include <post.h> 13 14 #ifdef CONFIG_SYS_POST_HOTKEYS_GPIO 15 #include <asm/gpio.h> 16 #endif 17 18 #ifdef CONFIG_LOGBUFFER 19 #include <logbuff.h> 20 #endif 21 22 DECLARE_GLOBAL_DATA_PTR; 23 24 #define POST_MAX_NUMBER 32 25 26 #define BOOTMODE_MAGIC 0xDEAD0000 27 28 int post_init_f(void) 29 { 30 int res = 0; 31 unsigned int i; 32 33 for (i = 0; i < post_list_size; i++) { 34 struct post_test *test = post_list + i; 35 36 if (test->init_f && test->init_f()) 37 res = -1; 38 } 39 40 gd->post_init_f_time = post_time_ms(0); 41 if (!gd->post_init_f_time) 42 printf("%s: post_time_ms not implemented\n", __FILE__); 43 44 return res; 45 } 46 47 /* 48 * Supply a default implementation for post_hotkeys_pressed() for boards 49 * without hotkey support. We always return 0 here, so that the 50 * long-running tests won't be started. 51 * 52 * Boards with hotkey support can override this weak default function 53 * by defining one in their board specific code. 54 */ 55 int __post_hotkeys_pressed(void) 56 { 57 #ifdef CONFIG_SYS_POST_HOTKEYS_GPIO 58 int ret; 59 unsigned gpio = CONFIG_SYS_POST_HOTKEYS_GPIO; 60 61 ret = gpio_request(gpio, "hotkeys"); 62 if (ret) { 63 printf("POST: gpio hotkey request failed\n"); 64 return 0; 65 } 66 67 gpio_direction_input(gpio); 68 ret = gpio_get_value(gpio); 69 gpio_free(gpio); 70 71 return ret; 72 #endif 73 74 return 0; /* No hotkeys supported */ 75 } 76 int post_hotkeys_pressed(void) 77 __attribute__((weak, alias("__post_hotkeys_pressed"))); 78 79 80 void post_bootmode_init(void) 81 { 82 int bootmode = post_bootmode_get(0); 83 int newword; 84 85 if (post_hotkeys_pressed() && !(bootmode & POST_POWERTEST)) 86 newword = BOOTMODE_MAGIC | POST_SLOWTEST; 87 else if (bootmode == 0) 88 newword = BOOTMODE_MAGIC | POST_POWERON; 89 else if (bootmode == POST_POWERON || bootmode == POST_SLOWTEST) 90 newword = BOOTMODE_MAGIC | POST_NORMAL; 91 else 92 /* Use old value */ 93 newword = post_word_load() & ~POST_COLDBOOT; 94 95 if (bootmode == 0) 96 /* We are booting after power-on */ 97 newword |= POST_COLDBOOT; 98 99 post_word_store(newword); 100 101 /* Reset activity record */ 102 gd->post_log_word = 0; 103 gd->post_log_res = 0; 104 } 105 106 int post_bootmode_get(unsigned int *last_test) 107 { 108 unsigned long word = post_word_load(); 109 int bootmode; 110 111 if ((word & 0xFFFF0000) != BOOTMODE_MAGIC) 112 return 0; 113 114 bootmode = word & 0x7F; 115 116 if (last_test && (bootmode & POST_POWERTEST)) 117 *last_test = (word >> 8) & 0xFF; 118 119 return bootmode; 120 } 121 122 /* POST tests run before relocation only mark status bits .... */ 123 static void post_log_mark_start(unsigned long testid) 124 { 125 gd->post_log_word |= testid; 126 } 127 128 static void post_log_mark_succ(unsigned long testid) 129 { 130 gd->post_log_res |= testid; 131 } 132 133 /* ... and the messages are output once we are relocated */ 134 void post_output_backlog(void) 135 { 136 int j; 137 138 for (j = 0; j < post_list_size; j++) { 139 if (gd->post_log_word & (post_list[j].testid)) { 140 post_log("POST %s ", post_list[j].cmd); 141 if (gd->post_log_res & post_list[j].testid) 142 post_log("PASSED\n"); 143 else { 144 post_log("FAILED\n"); 145 bootstage_error(BOOTSTAGE_ID_POST_FAIL_R); 146 } 147 } 148 } 149 } 150 151 static void post_bootmode_test_on(unsigned int last_test) 152 { 153 unsigned long word = post_word_load(); 154 155 word |= POST_POWERTEST; 156 157 word |= (last_test & 0xFF) << 8; 158 159 post_word_store(word); 160 } 161 162 static void post_bootmode_test_off(void) 163 { 164 unsigned long word = post_word_load(); 165 166 word &= ~POST_POWERTEST; 167 168 post_word_store(word); 169 } 170 171 #ifndef CONFIG_POST_SKIP_ENV_FLAGS 172 static void post_get_env_flags(int *test_flags) 173 { 174 int flag[] = { POST_POWERON, POST_NORMAL, POST_SLOWTEST, 175 POST_CRITICAL }; 176 char *var[] = { "post_poweron", "post_normal", "post_slowtest", 177 "post_critical" }; 178 int varnum = ARRAY_SIZE(var); 179 char list[128]; /* long enough for POST list */ 180 char *name; 181 char *s; 182 int last; 183 int i, j; 184 185 for (i = 0; i < varnum; i++) { 186 if (getenv_f(var[i], list, sizeof(list)) <= 0) 187 continue; 188 189 for (j = 0; j < post_list_size; j++) 190 test_flags[j] &= ~flag[i]; 191 192 last = 0; 193 name = list; 194 while (!last) { 195 while (*name && *name == ' ') 196 name++; 197 if (*name == 0) 198 break; 199 s = name + 1; 200 while (*s && *s != ' ') 201 s++; 202 if (*s == 0) 203 last = 1; 204 else 205 *s = 0; 206 207 for (j = 0; j < post_list_size; j++) { 208 if (strcmp(post_list[j].cmd, name) == 0) { 209 test_flags[j] |= flag[i]; 210 break; 211 } 212 } 213 214 if (j == post_list_size) 215 printf("No such test: %s\n", name); 216 217 name = s + 1; 218 } 219 } 220 } 221 #endif 222 223 static void post_get_flags(int *test_flags) 224 { 225 int j; 226 227 for (j = 0; j < post_list_size; j++) 228 test_flags[j] = post_list[j].flags; 229 230 #ifndef CONFIG_POST_SKIP_ENV_FLAGS 231 post_get_env_flags(test_flags); 232 #endif 233 234 for (j = 0; j < post_list_size; j++) 235 if (test_flags[j] & POST_POWERON) 236 test_flags[j] |= POST_SLOWTEST; 237 } 238 239 void __show_post_progress(unsigned int test_num, int before, int result) 240 { 241 } 242 void show_post_progress(unsigned int, int, int) 243 __attribute__((weak, alias("__show_post_progress"))); 244 245 static int post_run_single(struct post_test *test, 246 int test_flags, int flags, unsigned int i) 247 { 248 if ((flags & test_flags & POST_ALWAYS) && 249 (flags & test_flags & POST_MEM)) { 250 WATCHDOG_RESET(); 251 252 if (!(flags & POST_REBOOT)) { 253 if ((test_flags & POST_REBOOT) && 254 !(flags & POST_MANUAL)) { 255 post_bootmode_test_on( 256 (gd->flags & GD_FLG_POSTFAIL) ? 257 POST_FAIL_SAVE | i : i); 258 } 259 260 if (test_flags & POST_PREREL) 261 post_log_mark_start(test->testid); 262 else 263 post_log("POST %s ", test->cmd); 264 } 265 266 show_post_progress(i, POST_BEFORE, POST_FAILED); 267 268 if (test_flags & POST_PREREL) { 269 if ((*test->test)(flags) == 0) { 270 post_log_mark_succ(test->testid); 271 show_post_progress(i, POST_AFTER, POST_PASSED); 272 } else { 273 show_post_progress(i, POST_AFTER, POST_FAILED); 274 if (test_flags & POST_CRITICAL) 275 gd->flags |= GD_FLG_POSTFAIL; 276 if (test_flags & POST_STOP) 277 gd->flags |= GD_FLG_POSTSTOP; 278 } 279 } else { 280 if ((*test->test)(flags) != 0) { 281 post_log("FAILED\n"); 282 bootstage_error(BOOTSTAGE_ID_POST_FAIL_R); 283 show_post_progress(i, POST_AFTER, POST_FAILED); 284 if (test_flags & POST_CRITICAL) 285 gd->flags |= GD_FLG_POSTFAIL; 286 if (test_flags & POST_STOP) 287 gd->flags |= GD_FLG_POSTSTOP; 288 } else { 289 post_log("PASSED\n"); 290 show_post_progress(i, POST_AFTER, POST_PASSED); 291 } 292 } 293 294 if ((test_flags & POST_REBOOT) && !(flags & POST_MANUAL)) 295 post_bootmode_test_off(); 296 297 return 0; 298 } else { 299 return -1; 300 } 301 } 302 303 int post_run(char *name, int flags) 304 { 305 unsigned int i; 306 int test_flags[POST_MAX_NUMBER]; 307 308 post_get_flags(test_flags); 309 310 if (name == NULL) { 311 unsigned int last; 312 313 if (gd->flags & GD_FLG_POSTSTOP) 314 return 0; 315 316 if (post_bootmode_get(&last) & POST_POWERTEST) { 317 if (last & POST_FAIL_SAVE) { 318 last &= ~POST_FAIL_SAVE; 319 gd->flags |= GD_FLG_POSTFAIL; 320 } 321 if (last < post_list_size && 322 (flags & test_flags[last] & POST_ALWAYS) && 323 (flags & test_flags[last] & POST_MEM)) { 324 325 post_run_single(post_list + last, 326 test_flags[last], 327 flags | POST_REBOOT, last); 328 329 for (i = last + 1; i < post_list_size; i++) { 330 if (gd->flags & GD_FLG_POSTSTOP) 331 break; 332 post_run_single(post_list + i, 333 test_flags[i], 334 flags, i); 335 } 336 } 337 } else { 338 for (i = 0; i < post_list_size; i++) { 339 if (gd->flags & GD_FLG_POSTSTOP) 340 break; 341 post_run_single(post_list + i, 342 test_flags[i], 343 flags, i); 344 } 345 } 346 347 return 0; 348 } else { 349 for (i = 0; i < post_list_size; i++) { 350 if (strcmp(post_list[i].cmd, name) == 0) 351 break; 352 } 353 354 if (i < post_list_size) { 355 WATCHDOG_RESET(); 356 return post_run_single(post_list + i, 357 test_flags[i], 358 flags, i); 359 } else { 360 return -1; 361 } 362 } 363 } 364 365 static int post_info_single(struct post_test *test, int full) 366 { 367 if (test->flags & POST_MANUAL) { 368 if (full) 369 printf("%s - %s\n" 370 " %s\n", test->cmd, test->name, test->desc); 371 else 372 printf(" %-15s - %s\n", test->cmd, test->name); 373 374 return 0; 375 } else { 376 return -1; 377 } 378 } 379 380 int post_info(char *name) 381 { 382 unsigned int i; 383 384 if (name == NULL) { 385 for (i = 0; i < post_list_size; i++) 386 post_info_single(post_list + i, 0); 387 388 return 0; 389 } else { 390 for (i = 0; i < post_list_size; i++) { 391 if (strcmp(post_list[i].cmd, name) == 0) 392 break; 393 } 394 395 if (i < post_list_size) 396 return post_info_single(post_list + i, 1); 397 else 398 return -1; 399 } 400 } 401 402 int post_log(char *format, ...) 403 { 404 va_list args; 405 char printbuffer[CONFIG_SYS_PBSIZE]; 406 407 va_start(args, format); 408 409 /* For this to work, printbuffer must be larger than 410 * anything we ever want to print. 411 */ 412 vsprintf(printbuffer, format, args); 413 va_end(args); 414 415 #ifdef CONFIG_LOGBUFFER 416 /* Send to the logbuffer */ 417 logbuff_log(printbuffer); 418 #else 419 /* Send to the stdout file */ 420 puts(printbuffer); 421 #endif 422 423 return 0; 424 } 425 426 #ifdef CONFIG_NEEDS_MANUAL_RELOC 427 void post_reloc(void) 428 { 429 unsigned int i; 430 431 /* 432 * We have to relocate the test table manually 433 */ 434 for (i = 0; i < post_list_size; i++) { 435 ulong addr; 436 struct post_test *test = post_list + i; 437 438 if (test->name) { 439 addr = (ulong)(test->name) + gd->reloc_off; 440 test->name = (char *)addr; 441 } 442 443 if (test->cmd) { 444 addr = (ulong)(test->cmd) + gd->reloc_off; 445 test->cmd = (char *)addr; 446 } 447 448 if (test->desc) { 449 addr = (ulong)(test->desc) + gd->reloc_off; 450 test->desc = (char *)addr; 451 } 452 453 if (test->test) { 454 addr = (ulong)(test->test) + gd->reloc_off; 455 test->test = (int (*)(int flags)) addr; 456 } 457 458 if (test->init_f) { 459 addr = (ulong)(test->init_f) + gd->reloc_off; 460 test->init_f = (int (*)(void)) addr; 461 } 462 463 if (test->reloc) { 464 addr = (ulong)(test->reloc) + gd->reloc_off; 465 test->reloc = (void (*)(void)) addr; 466 467 test->reloc(); 468 } 469 } 470 } 471 #endif 472 473 474 /* 475 * Some tests (e.g. SYSMON) need the time when post_init_f started, 476 * but we cannot use get_timer() at this point. 477 * 478 * On PowerPC we implement it using the timebase register. 479 */ 480 unsigned long post_time_ms(unsigned long base) 481 { 482 #if defined(CONFIG_PPC) || defined(CONFIG_BLACKFIN) || defined(CONFIG_ARM) 483 return (unsigned long)lldiv(get_ticks(), get_tbclk() / CONFIG_SYS_HZ) 484 - base; 485 #else 486 #warning "Not implemented yet" 487 return 0; /* Not implemented yet */ 488 #endif 489 } 490