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