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