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