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