xref: /openbmc/u-boot/post/post.c (revision 39665bee)
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 (env_get_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 }
485