xref: /openbmc/u-boot/post/post.c (revision 3b57fe0a)
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 <console.h>
26 #include <watchdog.h>
27 #include <post.h>
28 
29 #ifdef CONFIG_LOGBUFFER
30 #include <logbuff.h>
31 #endif
32 
33 #ifdef CONFIG_POST
34 
35 #define POST_MAX_NUMBER		32
36 
37 #define BOOTMODE_MAGIC	0xDEAD0000
38 
39 int post_init_f (void)
40 {
41 	DECLARE_GLOBAL_DATA_PTR;
42 
43 	int res = 0;
44 	unsigned int i;
45 
46 	for (i = 0; i < post_list_size; i++) {
47 		struct post_test *test = post_list + i;
48 
49 		if (test->init_f && test->init_f()) {
50 			res = -1;
51 		}
52 	}
53 
54 	gd->post_init_f_time = post_time_ms(0);
55 	if (!gd->post_init_f_time)
56 	{
57 		printf("post/post.c: post_time_ms seems not to be implemented\n");
58 	}
59 
60 	return res;
61 }
62 
63 void post_bootmode_init (void)
64 {
65 	DECLARE_GLOBAL_DATA_PTR;
66 	int bootmode = post_bootmode_get (0);
67 
68 	if (bootmode == 0) {
69 		bootmode = POST_POWERON;
70 	} else if (bootmode == POST_POWERON) {
71 		bootmode = POST_POWERNORMAL;
72 	} else {
73 		return;
74 	}
75 
76 	post_word_store (BOOTMODE_MAGIC | bootmode);
77 	/* Reset activity record */
78 	gd->post_log_word = 0;
79 }
80 
81 int post_bootmode_get (unsigned int *last_test)
82 {
83 	unsigned long word = post_word_load ();
84 	int bootmode;
85 
86 	if ((word & 0xFFFF0000) != BOOTMODE_MAGIC) {
87 		return 0;
88 	}
89 
90 	bootmode = word & 0xFF;
91 
92 	if (last_test && (bootmode & POST_POWERTEST)) {
93 		*last_test = (word >> 8) & 0xFF;
94 	}
95 
96 	return bootmode;
97 }
98 
99 void post_bootmode_clear (void)
100 {
101 	post_word_store (0);
102 }
103 
104 /* POST tests run before relocation only mark status bits .... */
105 static void post_log_mark_start ( unsigned long testid )
106 {
107 	DECLARE_GLOBAL_DATA_PTR;
108 	gd->post_log_word |= (testid)<<16;
109 }
110 
111 static void post_log_mark_succ ( unsigned long testid )
112 {
113 	DECLARE_GLOBAL_DATA_PTR;
114 	gd->post_log_word |= testid;
115 }
116 
117 /* ... and the messages are output once we are relocated */
118 void post_output_backlog ( void )
119 {
120 	DECLARE_GLOBAL_DATA_PTR;
121 	int j;
122 
123 	for (j = 0; j < post_list_size; j++) {
124 		if (gd->post_log_word & (post_list[j].testid<<16)) {
125 			post_log ("POST %s ", post_list[j].cmd);
126 			if (gd->post_log_word & post_list[j].testid)
127 				post_log ("PASSED\n");
128 			else
129 				post_log ("FAILED\n");
130 		}
131 	}
132 }
133 
134 static void post_bootmode_test_on (unsigned int last_test)
135 {
136 	unsigned long word = post_word_load ();
137 
138 	word |= POST_POWERTEST;
139 
140 	word |= (last_test & 0xFF) << 8;
141 
142 	post_word_store (word);
143 }
144 
145 static void post_bootmode_test_off (void)
146 {
147 	unsigned long word = post_word_load ();
148 
149 	word &= ~POST_POWERTEST;
150 
151 	post_word_store (word);
152 }
153 
154 static void post_get_flags (int *test_flags)
155 {
156 	int flag[] = { POST_POWERON, POST_POWERNORMAL, POST_POWERFAIL };
157 	char *var[] = { "post_poweron", "post_normal", "post_shutdown" };
158 	int varnum = sizeof (var) / sizeof (var[0]);
159 	char list[128];			/* long enough for POST list */
160 	char *name;
161 	char *s;
162 	int last;
163 	int i, j;
164 
165 	for (j = 0; j < post_list_size; j++) {
166 		test_flags[j] = post_list[j].flags;
167 	}
168 
169 	for (i = 0; i < varnum; i++) {
170 		if (getenv_r (var[i], list, sizeof (list)) <= 0)
171 			continue;
172 
173 		for (j = 0; j < post_list_size; j++) {
174 			test_flags[j] &= ~flag[i];
175 		}
176 
177 		last = 0;
178 		name = list;
179 		while (!last) {
180 			while (*name && *name == ' ')
181 				name++;
182 			if (*name == 0)
183 				break;
184 			s = name + 1;
185 			while (*s && *s != ' ')
186 				s++;
187 			if (*s == 0)
188 				last = 1;
189 			else
190 				*s = 0;
191 
192 			for (j = 0; j < post_list_size; j++) {
193 				if (strcmp (post_list[j].cmd, name) == 0) {
194 					test_flags[j] |= flag[i];
195 					break;
196 				}
197 			}
198 
199 			if (j == post_list_size) {
200 				printf ("No such test: %s\n", name);
201 			}
202 
203 			name = s + 1;
204 		}
205 	}
206 }
207 
208 static int post_run_single (struct post_test *test,
209 				int test_flags, int flags, unsigned int i)
210 {
211 	if ((flags & test_flags & POST_ALWAYS) &&
212 		(flags & test_flags & POST_MEM)) {
213 		WATCHDOG_RESET ();
214 
215 		if (!(flags & POST_REBOOT)) {
216 			if ((test_flags & POST_REBOOT) && !(flags & POST_MANUAL)) {
217 				post_bootmode_test_on (i);
218 			}
219 
220 			if (test_flags & POST_PREREL)
221 				post_log_mark_start ( test->testid );
222 			else
223 			post_log ("POST %s ", test->cmd);
224 		}
225 
226 		if (test_flags & POST_PREREL) {
227 			if ((*test->test) (flags) == 0)
228 				post_log_mark_succ ( test->testid );
229 		} else {
230 		if ((*test->test) (flags) != 0)
231 			post_log ("FAILED\n");
232 		else
233 			post_log ("PASSED\n");
234 		}
235 
236 		if ((test_flags & POST_REBOOT) && !(flags & POST_MANUAL)) {
237 			post_bootmode_test_off ();
238 		}
239 
240 		return 0;
241 	} else {
242 		return -1;
243 	}
244 }
245 
246 int post_run (char *name, int flags)
247 {
248 	unsigned int i;
249 	int test_flags[POST_MAX_NUMBER];
250 
251 	post_get_flags (test_flags);
252 
253 	if (name == NULL) {
254 		unsigned int last;
255 
256 		if (post_bootmode_get (&last) & POST_POWERTEST) {
257 			if (last < post_list_size &&
258 				(flags & test_flags[last] & POST_ALWAYS) &&
259 				(flags & test_flags[last] & POST_MEM)) {
260 
261 				post_run_single (post_list + last,
262 						 test_flags[last],
263 						 flags | POST_REBOOT, last);
264 
265 				for (i = last + 1; i < post_list_size; i++) {
266 					post_run_single (post_list + i,
267 							 test_flags[i],
268 							 flags, i);
269 				}
270 			}
271 		} else {
272 			for (i = 0; i < post_list_size; i++) {
273 				post_run_single (post_list + i,
274 						 test_flags[i],
275 						 flags, i);
276 			}
277 		}
278 
279 		return 0;
280 	} else {
281 		for (i = 0; i < post_list_size; i++) {
282 			if (strcmp (post_list[i].cmd, name) == 0)
283 				break;
284 		}
285 
286 		if (i < post_list_size) {
287 			return post_run_single (post_list + i,
288 						test_flags[i],
289 						flags, i);
290 		} else {
291 			return -1;
292 		}
293 	}
294 }
295 
296 static int post_info_single (struct post_test *test, int full)
297 {
298 	if (test->flags & POST_MANUAL) {
299 		if (full)
300 			printf ("%s - %s\n"
301 				"  %s\n", test->cmd, test->name, test->desc);
302 		else
303 			printf ("  %-15s - %s\n", test->cmd, test->name);
304 
305 		return 0;
306 	} else {
307 		return -1;
308 	}
309 }
310 
311 int post_info (char *name)
312 {
313 	unsigned int i;
314 
315 	if (name == NULL) {
316 		for (i = 0; i < post_list_size; i++) {
317 			post_info_single (post_list + i, 0);
318 		}
319 
320 		return 0;
321 	} else {
322 		for (i = 0; i < post_list_size; i++) {
323 			if (strcmp (post_list[i].cmd, name) == 0)
324 				break;
325 		}
326 
327 		if (i < post_list_size) {
328 			return post_info_single (post_list + i, 1);
329 		} else {
330 			return -1;
331 		}
332 	}
333 }
334 
335 int post_log (char *format, ...)
336 {
337 	va_list args;
338 	uint i;
339 	char printbuffer[CFG_PBSIZE];
340 
341 	va_start (args, format);
342 
343 	/* For this to work, printbuffer must be larger than
344 	 * anything we ever want to print.
345 	 */
346 	i = vsprintf (printbuffer, format, args);
347 	va_end (args);
348 
349 #ifdef CONFIG_LOGBUFFER
350 	/* Send to the logbuffer */
351 	logbuff_log (printbuffer);
352 #else
353 	/* Send to the stdout file */
354 	puts (printbuffer);
355 #endif
356 
357 	return 0;
358 }
359 
360 void post_reloc (void)
361 {
362 	DECLARE_GLOBAL_DATA_PTR;
363 
364 	unsigned int i;
365 
366 	/*
367 	 * We have to relocate the test table manually
368 	 */
369 	for (i = 0; i < post_list_size; i++) {
370 		ulong addr;
371 		struct post_test *test = post_list + i;
372 
373 		if (test->name) {
374 			addr = (ulong) (test->name) + gd->reloc_off;
375 			test->name = (char *) addr;
376 		}
377 
378 		if (test->cmd) {
379 			addr = (ulong) (test->cmd) + gd->reloc_off;
380 			test->cmd = (char *) addr;
381 		}
382 
383 		if (test->desc) {
384 			addr = (ulong) (test->desc) + gd->reloc_off;
385 			test->desc = (char *) addr;
386 		}
387 
388 		if (test->test) {
389 			addr = (ulong) (test->test) + gd->reloc_off;
390 			test->test = (int (*)(int flags)) addr;
391 		}
392 
393 		if (test->init_f) {
394 			addr = (ulong) (test->init_f) + gd->reloc_off;
395 			test->init_f = (int (*)(void)) addr;
396 		}
397 
398 		if (test->reloc) {
399 			addr = (ulong) (test->reloc) + gd->reloc_off;
400 			test->reloc = (void (*)(void)) addr;
401 
402 			test->reloc();
403 		}
404 	}
405 }
406 
407 
408 /*
409  * Some tests (e.g. SYSMON) need the time when post_init_f started,
410  * but we cannot use get_timer() at this point.
411  *
412  * On PowerPC we implement it using the timebase register.
413  */
414 unsigned long post_time_ms (unsigned long base)
415 {
416 #ifdef CONFIG_PPC
417 	return (unsigned long)get_ticks () / (get_tbclk () / CFG_HZ) - base;
418 #else
419 	return 0; /* Not implemented yet */
420 #endif
421 }
422 
423 #endif /* CONFIG_POST */
424