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