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