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