uboot/post/post.c
<<
>>
Prefs
   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 CONFIG_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 CONFIG_SYS_POST_HOTKEYS_GPIO
  59        int ret;
  60        unsigned gpio = CONFIG_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 CONFIG_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 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__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                WATCHDOG_RESET();
 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                        WATCHDOG_RESET();
 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#ifdef CONFIG_NEEDS_MANUAL_RELOC
 420void post_reloc(void)
 421{
 422        unsigned int i;
 423
 424        /*
 425         * We have to relocate the test table manually
 426         */
 427        for (i = 0; i < post_list_size; i++) {
 428                ulong addr;
 429                struct post_test *test = post_list + i;
 430
 431                if (test->name) {
 432                        addr = (ulong)(test->name) + gd->reloc_off;
 433                        test->name = (char *)addr;
 434                }
 435
 436                if (test->cmd) {
 437                        addr = (ulong)(test->cmd) + gd->reloc_off;
 438                        test->cmd = (char *)addr;
 439                }
 440
 441                if (test->desc) {
 442                        addr = (ulong)(test->desc) + gd->reloc_off;
 443                        test->desc = (char *)addr;
 444                }
 445
 446                if (test->test) {
 447                        addr = (ulong)(test->test) + gd->reloc_off;
 448                        test->test = (int (*)(int flags)) addr;
 449                }
 450
 451                if (test->init_f) {
 452                        addr = (ulong)(test->init_f) + gd->reloc_off;
 453                        test->init_f = (int (*)(void)) addr;
 454                }
 455
 456                if (test->reloc) {
 457                        addr = (ulong)(test->reloc) + gd->reloc_off;
 458                        test->reloc = (void (*)(void)) addr;
 459
 460                        test->reloc();
 461                }
 462        }
 463}
 464#endif
 465
 466
 467/*
 468 * Some tests (e.g. SYSMON) need the time when post_init_f started,
 469 * but we cannot use get_timer() at this point.
 470 *
 471 * On PowerPC we implement it using the timebase register.
 472 */
 473unsigned long post_time_ms(unsigned long base)
 474{
 475#if defined(CONFIG_PPC) || defined(CONFIG_ARM)
 476        return (unsigned long)lldiv(get_ticks(), get_tbclk() / CONFIG_SYS_HZ)
 477                - base;
 478#else
 479#warning "Not implemented yet"
 480        return 0; /* Not implemented yet */
 481#endif
 482}
 483