xref: /openbmc/u-boot/post/post.c (revision 3ebd892f)
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 
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  */
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 
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 
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 .... */
115 static void post_log_mark_start(unsigned long testid)
116 {
117 	gd->post_log_word |= testid;
118 }
119 
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 */
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 
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 
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
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 
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 
231 __weak void show_post_progress(unsigned int test_num, int before, int result)
232 {
233 }
234 
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 
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 
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 
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 
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
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  */
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