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
post_init_f(void)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 */
post_hotkeys_pressed(void)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
post_bootmode_init(void)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
post_bootmode_get(unsigned int * last_test)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 .... */
post_log_mark_start(unsigned long testid)115 static void post_log_mark_start(unsigned long testid)
116 {
117 gd->post_log_word |= testid;
118 }
119
post_log_mark_succ(unsigned long testid)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 */
post_output_backlog(void)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
post_bootmode_test_on(unsigned int last_test)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
post_bootmode_test_off(void)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
post_get_env_flags(int * test_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
post_get_flags(int * test_flags)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
show_post_progress(unsigned int test_num,int before,int result)231 __weak void show_post_progress(unsigned int test_num, int before, int result)
232 {
233 }
234
post_run_single(struct post_test * test,int test_flags,int flags,unsigned int i)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
post_run(char * name,int flags)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
post_info_single(struct post_test * test,int full)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
post_info(char * name)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
post_log(char * format,...)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
post_reloc(void)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 */
post_time_ms(unsigned long base)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 }
475