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