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