/*
* ( C ) Copyright 2002
* Wolfgang Denk , DENX Software Engineering , wd @ denx . de .
*
* SPDX - License - Identifier : GPL - 2.0 +
*/
# include <common.h>
# include <stdio_dev.h>
# include <watchdog.h>
# include <div64.h>
# include <post.h>
# ifdef CONFIG_SYS_POST_HOTKEYS_GPIO
# include <asm/gpio.h>
# endif
# ifdef CONFIG_LOGBUFFER
# include <logbuff.h>
# endif
DECLARE_GLOBAL_DATA_PTR ;
# define POST_MAX_NUMBER 32
# define BOOTMODE_MAGIC 0xDEAD0000
int post_init_f ( void )
{
int res = 0 ;
unsigned int i ;
for ( i = 0 ; i < post_list_size ; i + + ) {
struct post_test * test = post_list + i ;
if ( test - > init_f & & test - > init_f ( ) )
res = - 1 ;
}
gd - > post_init_f_time = post_time_ms ( 0 ) ;
if ( ! gd - > post_init_f_time )
printf ( " %s: post_time_ms not implemented \n " , __FILE__ ) ;
return res ;
}
/*
* Supply a default implementation for post_hotkeys_pressed ( ) for boards
* without hotkey support . We always return 0 here , so that the
* long - running tests won ' t be started .
*
* Boards with hotkey support can override this weak default function
* by defining one in their board specific code .
*/
__weak int post_hotkeys_pressed ( void )
{
# ifdef CONFIG_SYS_POST_HOTKEYS_GPIO
int ret ;
unsigned gpio = CONFIG_SYS_POST_HOTKEYS_GPIO ;
ret = gpio_request ( gpio , " hotkeys " ) ;
if ( ret ) {
printf ( " POST: gpio hotkey request failed \n " ) ;
return 0 ;
}
gpio_direction_input ( gpio ) ;
ret = gpio_get_value ( gpio ) ;
gpio_free ( gpio ) ;
return ret ;
# endif
return 0 ; /* No hotkeys supported */
}
void post_bootmode_init ( void )
{
int bootmode = post_bootmode_get ( 0 ) ;
int newword ;
if ( post_hotkeys_pressed ( ) & & ! ( bootmode & POST_POWERTEST ) )
newword = BOOTMODE_MAGIC | POST_SLOWTEST ;
else if ( bootmode = = 0 )
newword = BOOTMODE_MAGIC | POST_POWERON ;
else if ( bootmode = = POST_POWERON | | bootmode = = POST_SLOWTEST )
newword = BOOTMODE_MAGIC | POST_NORMAL ;
else
/* Use old value */
newword = post_word_load ( ) & ~ POST_COLDBOOT ;
if ( bootmode = = 0 )
/* We are booting after power-on */
newword | = POST_COLDBOOT ;
post_word_store ( newword ) ;
/* Reset activity record */
gd - > post_log_word = 0 ;
gd - > post_log_res = 0 ;
}
int post_bootmode_get ( unsigned int * last_test )
{
unsigned long word = post_word_load ( ) ;
int bootmode ;
if ( ( word & 0xFFFF0000 ) ! = BOOTMODE_MAGIC )
return 0 ;
bootmode = word & 0x7F ;
if ( last_test & & ( bootmode & POST_POWERTEST ) )
* last_test = ( word > > 8 ) & 0xFF ;
return bootmode ;
}
/* POST tests run before relocation only mark status bits .... */
static void post_log_mark_start ( unsigned long testid )
{
gd - > post_log_word | = testid ;
}
static void post_log_mark_succ ( unsigned long testid )
{
gd - > post_log_res | = testid ;
}
/* ... and the messages are output once we are relocated */
void post_output_backlog ( void )
{
int j ;
for ( j = 0 ; j < post_list_size ; j + + ) {
if ( gd - > post_log_word & ( post_list [ j ] . testid ) ) {
post_log ( " POST %s " , post_list [ j ] . cmd ) ;
if ( gd - > post_log_res & post_list [ j ] . testid )
post_log ( " PASSED \n " ) ;
else {
post_log ( " FAILED \n " ) ;
bootstage_error ( BOOTSTAGE_ID_POST_FAIL_R ) ;
}
}
}
}
static void post_bootmode_test_on ( unsigned int last_test )
{
unsigned long word = post_word_load ( ) ;
word | = POST_POWERTEST ;
word | = ( last_test & 0xFF ) < < 8 ;
post_word_store ( word ) ;
}
static void post_bootmode_test_off ( void )
{
unsigned long word = post_word_load ( ) ;
word & = ~ POST_POWERTEST ;
post_word_store ( word ) ;
}
# ifndef CONFIG_POST_SKIP_ENV_FLAGS
static void post_get_env_flags ( int * test_flags )
{
int flag [ ] = { POST_POWERON , POST_NORMAL , POST_SLOWTEST ,
POST_CRITICAL } ;
char * var [ ] = { " post_poweron " , " post_normal " , " post_slowtest " ,
" post_critical " } ;
int varnum = ARRAY_SIZE ( var ) ;
char list [ 128 ] ; /* long enough for POST list */
char * name ;
char * s ;
int last ;
int i , j ;
for ( i = 0 ; i < varnum ; i + + ) {
if ( getenv_f ( var [ i ] , list , sizeof ( list ) ) < = 0 )
continue ;
for ( j = 0 ; j < post_list_size ; j + + )
test_flags [ j ] & = ~ flag [ i ] ;
last = 0 ;
name = list ;
while ( ! last ) {
while ( * name & & * name = = ' ' )
name + + ;
if ( * name = = 0 )
break ;
s = name + 1 ;
while ( * s & & * s ! = ' ' )
s + + ;
if ( * s = = 0 )
last = 1 ;
else
* s = 0 ;
for ( j = 0 ; j < post_list_size ; j + + ) {
if ( strcmp ( post_list [ j ] . cmd , name ) = = 0 ) {
test_flags [ j ] | = flag [ i ] ;
break ;
}
}
if ( j = = post_list_size )
printf ( " No such test: %s \n " , name ) ;
name = s + 1 ;
}
}
}
# endif
static void post_get_flags ( int * test_flags )
{
int j ;
for ( j = 0 ; j < post_list_size ; j + + )
test_flags [ j ] = post_list [ j ] . flags ;
# ifndef CONFIG_POST_SKIP_ENV_FLAGS
post_get_env_flags ( test_flags ) ;
# endif
for ( j = 0 ; j < post_list_size ; j + + )
if ( test_flags [ j ] & POST_POWERON )
test_flags [ j ] | = POST_SLOWTEST ;
}
__weak void show_post_progress ( unsigned int test_num , int before , int result )
{
}
static int post_run_single ( struct post_test * test ,
int test_flags , int flags , unsigned int i )
{
if ( ( flags & test_flags & POST_ALWAYS ) & &
( flags & test_flags & POST_MEM ) ) {
WATCHDOG_RESET ( ) ;
if ( ! ( flags & POST_REBOOT ) ) {
if ( ( test_flags & POST_REBOOT ) & &
! ( flags & POST_MANUAL ) ) {
post_bootmode_test_on (
( gd - > flags & GD_FLG_POSTFAIL ) ?
POST_FAIL_SAVE | i : i ) ;
}
if ( test_flags & POST_PREREL )
post_log_mark_start ( test - > testid ) ;
else
post_log ( " POST %s " , test - > cmd ) ;
}
show_post_progress ( i , POST_BEFORE , POST_FAILED ) ;
if ( test_flags & POST_PREREL ) {
if ( ( * test - > test ) ( flags ) = = 0 ) {
post_log_mark_succ ( test - > testid ) ;
show_post_progress ( i , POST_AFTER , POST_PASSED ) ;
} else {
show_post_progress ( i , POST_AFTER , POST_FAILED ) ;
if ( test_flags & POST_CRITICAL )
gd - > flags | = GD_FLG_POSTFAIL ;
if ( test_flags & POST_STOP )
gd - > flags | = GD_FLG_POSTSTOP ;
}
} else {
if ( ( * test - > test ) ( flags ) ! = 0 ) {
post_log ( " FAILED \n " ) ;
bootstage_error ( BOOTSTAGE_ID_POST_FAIL_R ) ;
show_post_progress ( i , POST_AFTER , POST_FAILED ) ;
if ( test_flags & POST_CRITICAL )
gd - > flags | = GD_FLG_POSTFAIL ;
if ( test_flags & POST_STOP )
gd - > flags | = GD_FLG_POSTSTOP ;
} else {
post_log ( " PASSED \n " ) ;
show_post_progress ( i , POST_AFTER , POST_PASSED ) ;
}
}
if ( ( test_flags & POST_REBOOT ) & & ! ( flags & POST_MANUAL ) )
post_bootmode_test_off ( ) ;
return 0 ;
} else {
return - 1 ;
}
}
int post_run ( char * name , int flags )
{
unsigned int i ;
int test_flags [ POST_MAX_NUMBER ] ;
post_get_flags ( test_flags ) ;
if ( name = = NULL ) {
unsigned int last ;
if ( gd - > flags & GD_FLG_POSTSTOP )
return 0 ;
if ( post_bootmode_get ( & last ) & POST_POWERTEST ) {
if ( last & POST_FAIL_SAVE ) {
last & = ~ POST_FAIL_SAVE ;
gd - > flags | = GD_FLG_POSTFAIL ;
}
if ( last < post_list_size & &
( flags & test_flags [ last ] & POST_ALWAYS ) & &
( flags & test_flags [ last ] & POST_MEM ) ) {
post_run_single ( post_list + last ,
test_flags [ last ] ,
flags | POST_REBOOT , last ) ;
for ( i = last + 1 ; i < post_list_size ; i + + ) {
if ( gd - > flags & GD_FLG_POSTSTOP )
break ;
post_run_single ( post_list + i ,
test_flags [ i ] ,
flags , i ) ;
}
}
} else {
for ( i = 0 ; i < post_list_size ; i + + ) {
if ( gd - > flags & GD_FLG_POSTSTOP )
break ;
post_run_single ( post_list + i ,
test_flags [ i ] ,
flags , i ) ;
}
}
return 0 ;
} else {
for ( i = 0 ; i < post_list_size ; i + + ) {
if ( strcmp ( post_list [ i ] . cmd , name ) = = 0 )
break ;
}
if ( i < post_list_size ) {
WATCHDOG_RESET ( ) ;
return post_run_single ( post_list + i ,
test_flags [ i ] ,
flags , i ) ;
} else {
return - 1 ;
}
}
}
static int post_info_single ( struct post_test * test , int full )
{
if ( test - > flags & POST_MANUAL ) {
if ( full )
printf ( " %s - %s \n "
" %s \n " , test - > cmd , test - > name , test - > desc ) ;
else
printf ( " %-15s - %s \n " , test - > cmd , test - > name ) ;
return 0 ;
} else {
return - 1 ;
}
}
int post_info ( char * name )
{
unsigned int i ;
if ( name = = NULL ) {
for ( i = 0 ; i < post_list_size ; i + + )
post_info_single ( post_list + i , 0 ) ;
return 0 ;
} else {
for ( i = 0 ; i < post_list_size ; i + + ) {
if ( strcmp ( post_list [ i ] . cmd , name ) = = 0 )
break ;
}
if ( i < post_list_size )
return post_info_single ( post_list + i , 1 ) ;
else
return - 1 ;
}
}
int post_log ( char * format , . . . )
{
va_list args ;
char printbuffer [ CONFIG_SYS_PBSIZE ] ;
va_start ( args , format ) ;
/* For this to work, printbuffer must be larger than
* anything we ever want to print .
*/
vsprintf ( printbuffer , format , args ) ;
va_end ( args ) ;
# ifdef CONFIG_LOGBUFFER
/* Send to the logbuffer */
logbuff_log ( printbuffer ) ;
# else
/* Send to the stdout file */
puts ( printbuffer ) ;
# endif
return 0 ;
}
# ifdef CONFIG_NEEDS_MANUAL_RELOC
void post_reloc ( void )
{
unsigned int i ;
/*
* We have to relocate the test table manually
*/
for ( i = 0 ; i < post_list_size ; i + + ) {
ulong addr ;
struct post_test * test = post_list + i ;
if ( test - > name ) {
addr = ( ulong ) ( test - > name ) + gd - > reloc_off ;
test - > name = ( char * ) addr ;
}
if ( test - > cmd ) {
addr = ( ulong ) ( test - > cmd ) + gd - > reloc_off ;
test - > cmd = ( char * ) addr ;
}
if ( test - > desc ) {
addr = ( ulong ) ( test - > desc ) + gd - > reloc_off ;
test - > desc = ( char * ) addr ;
}
if ( test - > test ) {
addr = ( ulong ) ( test - > test ) + gd - > reloc_off ;
test - > test = ( int ( * ) ( int flags ) ) addr ;
}
if ( test - > init_f ) {
addr = ( ulong ) ( test - > init_f ) + gd - > reloc_off ;
test - > init_f = ( int ( * ) ( void ) ) addr ;
}
if ( test - > reloc ) {
addr = ( ulong ) ( test - > reloc ) + gd - > reloc_off ;
test - > reloc = ( void ( * ) ( void ) ) addr ;
test - > reloc ( ) ;
}
}
}
# endif
/*
* Some tests ( e . g . SYSMON ) need the time when post_init_f started ,
* but we cannot use get_timer ( ) at this point .
*
* On PowerPC we implement it using the timebase register .
*/
unsigned long post_time_ms ( unsigned long base )
{
# if defined(CONFIG_PPC) || defined(CONFIG_BLACKFIN) || defined(CONFIG_ARM)
return ( unsigned long ) lldiv ( get_ticks ( ) , get_tbclk ( ) / CONFIG_SYS_HZ )
- base ;
# else
# warning "Not implemented yet"
return 0 ; /* Not implemented yet */
# endif
}