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