Merge branch 'master' of git://git.denx.de/u-boot-video
[kernel/u-boot.git] / post / post.c
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 }