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 <bootstage.h> 9#include <env.h> 10#include <log.h> 11#include <malloc.h> 12#include <stdio_dev.h> 13#include <time.h> 14#include <watchdog.h> 15#include <div64.h> 16#include <post.h> 17#include <asm/global_data.h> 18 19#ifdef CFG_SYS_POST_HOTKEYS_GPIO 20#include <asm/gpio.h> 21#endif 22 23DECLARE_GLOBAL_DATA_PTR; 24 25#define POST_MAX_NUMBER 32 26 27#define BOOTMODE_MAGIC 0xDEAD0000 28 29int post_init_f(void) 30{ 31 int res = 0; 32 unsigned int i; 33 34 for (i = 0; i < post_list_size; i++) { 35 struct post_test *test = post_list + i; 36 37 if (test->init_f && test->init_f()) 38 res = -1; 39 } 40 41 gd->post_init_f_time = post_time_ms(0); 42 if (!gd->post_init_f_time) 43 printf("%s: post_time_ms not implemented\n", __FILE__); 44 45 return res; 46} 47 48/* 49 * Supply a default implementation for post_hotkeys_pressed() for boards 50 * without hotkey support. We always return 0 here, so that the 51 * long-running tests won't be started. 52 * 53 * Boards with hotkey support can override this weak default function 54 * by defining one in their board specific code. 55 */ 56__weak int post_hotkeys_pressed(void) 57{ 58#ifdef CFG_SYS_POST_HOTKEYS_GPIO 59 int ret; 60 unsigned gpio = CFG_SYS_POST_HOTKEYS_GPIO; 61 62 ret = gpio_request(gpio, "hotkeys"); 63 if (ret) { 64 printf("POST: gpio hotkey request failed\n"); 65 return 0; 66 } 67 68 gpio_direction_input(gpio); 69 ret = gpio_get_value(gpio); 70 gpio_free(gpio); 71 72 return ret; 73#endif 74 75 return 0; /* No hotkeys supported */ 76} 77 78void post_bootmode_init(void) 79{ 80 int bootmode = post_bootmode_get(0); 81 int newword; 82 83 if (post_hotkeys_pressed() && !(bootmode & POST_POWERTEST)) 84 newword = BOOTMODE_MAGIC | POST_SLOWTEST; 85 else if (bootmode == 0) 86 newword = BOOTMODE_MAGIC | POST_POWERON; 87 else if (bootmode == POST_POWERON || bootmode == POST_SLOWTEST) 88 newword = BOOTMODE_MAGIC | POST_NORMAL; 89 else 90 /* Use old value */ 91 newword = post_word_load() & ~POST_COLDBOOT; 92 93 if (bootmode == 0) 94 /* We are booting after power-on */ 95 newword |= POST_COLDBOOT; 96 97 post_word_store(newword); 98 99 /* Reset activity record */ 100 gd->post_log_word = 0; 101 gd->post_log_res = 0; 102} 103 104int post_bootmode_get(unsigned int *last_test) 105{ 106 unsigned long word = post_word_load(); 107 int bootmode; 108 109 if ((word & 0xFFFF0000) != BOOTMODE_MAGIC) 110 return 0; 111 112 bootmode = word & 0x7F; 113 114 if (last_test && (bootmode & POST_POWERTEST)) 115 *last_test = (word >> 8) & 0xFF; 116 117 return bootmode; 118} 119 120/* POST tests run before relocation only mark status bits .... */ 121static void post_log_mark_start(unsigned long testid) 122{ 123 gd->post_log_word |= testid; 124} 125 126static void post_log_mark_succ(unsigned long testid) 127{ 128 gd->post_log_res |= testid; 129} 130 131/* ... and the messages are output once we are relocated */ 132int post_output_backlog(void) 133{ 134 int j; 135 136 for (j = 0; j < post_list_size; j++) { 137 if (gd->post_log_word & (post_list[j].testid)) { 138 post_log("POST %s ", post_list[j].cmd); 139 if (gd->post_log_res & post_list[j].testid) 140 post_log("PASSED\n"); 141 else { 142 post_log("FAILED\n"); 143 bootstage_error(BOOTSTAGE_ID_POST_FAIL_R); 144 } 145 } 146 } 147 148 return 0; 149} 150 151static 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 162static 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 CFG_POST_SKIP_ENV_FLAGS 172static 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 (env_get_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 == ' ') 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 223static 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 CFG_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__weak void show_post_progress(unsigned int test_num, int before, int result) 240{ 241} 242 243static int post_run_single(struct post_test *test, 244 int test_flags, int flags, unsigned int i) 245{ 246 if ((flags & test_flags & POST_ALWAYS) && 247 (flags & test_flags & POST_MEM)) { 248 schedule(); 249 250 if (!(flags & POST_REBOOT)) { 251 if ((test_flags & POST_REBOOT) && 252 !(flags & POST_MANUAL)) { 253 post_bootmode_test_on( 254 (gd->flags & GD_FLG_POSTFAIL) ? 255 POST_FAIL_SAVE | i : i); 256 } 257 258 if (test_flags & POST_PREREL) 259 post_log_mark_start(test->testid); 260 else 261 post_log("POST %s ", test->cmd); 262 } 263 264 show_post_progress(i, POST_BEFORE, POST_FAILED); 265 266 if (test_flags & POST_PREREL) { 267 if ((*test->test)(flags) == 0) { 268 post_log_mark_succ(test->testid); 269 show_post_progress(i, POST_AFTER, POST_PASSED); 270 } else { 271 show_post_progress(i, POST_AFTER, POST_FAILED); 272 if (test_flags & POST_CRITICAL) 273 gd->flags |= GD_FLG_POSTFAIL; 274 if (test_flags & POST_STOP) 275 gd->flags |= GD_FLG_POSTSTOP; 276 } 277 } else { 278 if ((*test->test)(flags) != 0) { 279 post_log("FAILED\n"); 280 bootstage_error(BOOTSTAGE_ID_POST_FAIL_R); 281 show_post_progress(i, POST_AFTER, POST_FAILED); 282 if (test_flags & POST_CRITICAL) 283 gd->flags |= GD_FLG_POSTFAIL; 284 if (test_flags & POST_STOP) 285 gd->flags |= GD_FLG_POSTSTOP; 286 } else { 287 post_log("PASSED\n"); 288 show_post_progress(i, POST_AFTER, POST_PASSED); 289 } 290 } 291 292 if ((test_flags & POST_REBOOT) && !(flags & POST_MANUAL)) 293 post_bootmode_test_off(); 294 295 return 0; 296 } else { 297 return -1; 298 } 299} 300 301int post_run(char *name, int flags) 302{ 303 unsigned int i; 304 int test_flags[POST_MAX_NUMBER]; 305 306 post_get_flags(test_flags); 307 308 if (name == NULL) { 309 unsigned int last; 310 311 if (gd->flags & GD_FLG_POSTSTOP) 312 return 0; 313 314 if (post_bootmode_get(&last) & POST_POWERTEST) { 315 if (last & POST_FAIL_SAVE) { 316 last &= ~POST_FAIL_SAVE; 317 gd->flags |= GD_FLG_POSTFAIL; 318 } 319 if (last < post_list_size && 320 (flags & test_flags[last] & POST_ALWAYS) && 321 (flags & test_flags[last] & POST_MEM)) { 322 323 post_run_single(post_list + last, 324 test_flags[last], 325 flags | POST_REBOOT, last); 326 327 for (i = last + 1; i < post_list_size; i++) { 328 if (gd->flags & GD_FLG_POSTSTOP) 329 break; 330 post_run_single(post_list + i, 331 test_flags[i], 332 flags, i); 333 } 334 } 335 } else { 336 for (i = 0; i < post_list_size; i++) { 337 if (gd->flags & GD_FLG_POSTSTOP) 338 break; 339 post_run_single(post_list + i, 340 test_flags[i], 341 flags, i); 342 } 343 } 344 345 return 0; 346 } else { 347 for (i = 0; i < post_list_size; i++) { 348 if (strcmp(post_list[i].cmd, name) == 0) 349 break; 350 } 351 352 if (i < post_list_size) { 353 schedule(); 354 return post_run_single(post_list + i, 355 test_flags[i], 356 flags, i); 357 } else { 358 return -1; 359 } 360 } 361} 362 363static int post_info_single(struct post_test *test, int full) 364{ 365 if (test->flags & POST_MANUAL) { 366 if (full) 367 printf("%s - %s\n" 368 " %s\n", test->cmd, test->name, test->desc); 369 else 370 printf(" %-15s - %s\n", test->cmd, test->name); 371 372 return 0; 373 } else { 374 return -1; 375 } 376} 377 378int post_info(char *name) 379{ 380 unsigned int i; 381 382 if (name == NULL) { 383 for (i = 0; i < post_list_size; i++) 384 post_info_single(post_list + i, 0); 385 386 return 0; 387 } else { 388 for (i = 0; i < post_list_size; i++) { 389 if (strcmp(post_list[i].cmd, name) == 0) 390 break; 391 } 392 393 if (i < post_list_size) 394 return post_info_single(post_list + i, 1); 395 else 396 return -1; 397 } 398} 399 400int post_log(char *format, ...) 401{ 402 va_list args; 403 char printbuffer[CONFIG_SYS_PBSIZE]; 404 405 va_start(args, format); 406 407 /* For this to work, printbuffer must be larger than 408 * anything we ever want to print. 409 */ 410 vsprintf(printbuffer, format, args); 411 va_end(args); 412 413 /* Send to the stdout file */ 414 puts(printbuffer); 415 416 return 0; 417} 418 419/* 420 * Some tests (e.g. SYSMON) need the time when post_init_f started, 421 * but we cannot use get_timer() at this point. 422 * 423 * On PowerPC we implement it using the timebase register. 424 */ 425unsigned long post_time_ms(unsigned long base) 426{ 427#if defined(CONFIG_PPC) || defined(CONFIG_ARM) 428 return (unsigned long)lldiv(get_ticks(), get_tbclk() / CONFIG_SYS_HZ) 429 - base; 430#else 431#warning "Not implemented yet" 432 return 0; /* Not implemented yet */ 433#endif 434} 435