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