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