2005-04-17 02:20:36 +04:00
/*
*
* Procedures for interfacing to the RTAS on CHRP machines .
*
* Peter Bergner , IBM March 2001.
* Copyright ( C ) 2001 IBM .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <stdarg.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/spinlock.h>
# include <linux/module.h>
# include <linux/init.h>
2006-01-11 23:17:48 +03:00
# include <linux/capability.h>
2005-11-07 08:41:59 +03:00
# include <linux/delay.h>
2005-04-17 02:20:36 +04:00
# include <asm/prom.h>
# include <asm/rtas.h>
2006-01-31 09:17:47 +03:00
# include <asm/hvcall.h>
2005-04-17 02:20:36 +04:00
# include <asm/semaphore.h>
# include <asm/machdep.h>
2006-03-28 16:15:54 +04:00
# include <asm/firmware.h>
2005-04-17 02:20:36 +04:00
# include <asm/page.h>
# include <asm/param.h>
# include <asm/system.h>
# include <asm/delay.h>
# include <asm/uaccess.h>
2005-10-26 11:05:24 +04:00
# include <asm/lmb.h>
2006-01-11 03:54:09 +03:00
# include <asm/udbg.h>
2006-03-23 02:00:08 +03:00
# include <asm/syscalls.h>
2005-04-17 02:20:36 +04:00
2005-10-26 11:05:24 +04:00
struct rtas_t rtas = {
2005-04-17 02:20:36 +04:00
. lock = SPIN_LOCK_UNLOCKED
} ;
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas ) ;
2005-04-17 02:20:36 +04:00
2006-01-14 03:39:24 +03:00
struct rtas_suspend_me_data {
long waiting ;
struct rtas_args * args ;
} ;
2005-04-17 02:20:36 +04:00
DEFINE_SPINLOCK ( rtas_data_buf_lock ) ;
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_data_buf_lock ) ;
2005-10-26 11:05:24 +04:00
char rtas_data_buf [ RTAS_DATA_BUF_SIZE ] __cacheline_aligned ;
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_data_buf ) ;
2005-04-17 02:20:36 +04:00
unsigned long rtas_rmo_buf ;
2005-11-03 06:41:19 +03:00
/*
* If non - NULL , this gets called when the kernel terminates .
* This is done like this so rtas_flash can be a module .
*/
void ( * rtas_flash_term_hook ) ( int ) ;
EXPORT_SYMBOL ( rtas_flash_term_hook ) ;
2005-10-26 11:05:24 +04:00
/*
* call_rtas_display_status and call_rtas_display_status_delay
* are designed only for very early low - level debugging , which
* is why the token is hard - coded to 10.
*/
2006-01-11 03:54:09 +03:00
static void call_rtas_display_status ( char c )
2005-04-17 02:20:36 +04:00
{
struct rtas_args * args = & rtas . args ;
unsigned long s ;
if ( ! rtas . base )
return ;
spin_lock_irqsave ( & rtas . lock , s ) ;
args - > token = 10 ;
args - > nargs = 1 ;
args - > nret = 1 ;
args - > rets = ( rtas_arg_t * ) & ( args - > args [ 1 ] ) ;
2006-01-11 03:54:09 +03:00
args - > args [ 0 ] = ( unsigned char ) c ;
2005-04-17 02:20:36 +04:00
enter_rtas ( __pa ( args ) ) ;
spin_unlock_irqrestore ( & rtas . lock , s ) ;
}
2006-01-11 03:54:09 +03:00
static void call_rtas_display_status_delay ( char c )
2005-04-17 02:20:36 +04:00
{
static int pending_newline = 0 ; /* did last write end with unprinted newline? */
static int width = 16 ;
if ( c = = ' \n ' ) {
while ( width - - > 0 )
call_rtas_display_status ( ' ' ) ;
width = 16 ;
2005-11-07 08:41:59 +03:00
mdelay ( 500 ) ;
2005-04-17 02:20:36 +04:00
pending_newline = 1 ;
} else {
if ( pending_newline ) {
call_rtas_display_status ( ' \r ' ) ;
call_rtas_display_status ( ' \n ' ) ;
}
pending_newline = 0 ;
if ( width - - ) {
call_rtas_display_status ( c ) ;
udelay ( 10000 ) ;
}
}
}
2006-06-23 12:20:16 +04:00
void __init udbg_init_rtas_panel ( void )
2006-01-11 03:54:09 +03:00
{
udbg_putc = call_rtas_display_status_delay ;
}
2006-06-23 12:20:16 +04:00
# ifdef CONFIG_UDBG_RTAS_CONSOLE
/* If you think you're dying before early_init_dt_scan_rtas() does its
* work , you can hard code the token values for your firmware here and
* hardcode rtas . base / entry etc .
*/
static unsigned int rtas_putchar_token = RTAS_UNKNOWN_SERVICE ;
static unsigned int rtas_getchar_token = RTAS_UNKNOWN_SERVICE ;
static void udbg_rtascon_putc ( char c )
{
int tries ;
if ( ! rtas . base )
return ;
/* Add CRs before LFs */
if ( c = = ' \n ' )
udbg_rtascon_putc ( ' \r ' ) ;
/* if there is more than one character to be displayed, wait a bit */
for ( tries = 0 ; tries < 16 ; tries + + ) {
if ( rtas_call ( rtas_putchar_token , 1 , 1 , NULL , c ) = = 0 )
break ;
udelay ( 1000 ) ;
}
}
static int udbg_rtascon_getc_poll ( void )
{
int c ;
if ( ! rtas . base )
return - 1 ;
if ( rtas_call ( rtas_getchar_token , 0 , 2 , & c ) )
return - 1 ;
return c ;
}
static int udbg_rtascon_getc ( void )
{
int c ;
while ( ( c = udbg_rtascon_getc_poll ( ) ) = = - 1 )
;
return c ;
}
void __init udbg_init_rtas_console ( void )
{
udbg_putc = udbg_rtascon_putc ;
udbg_getc = udbg_rtascon_getc ;
udbg_getc_poll = udbg_rtascon_getc_poll ;
}
# endif /* CONFIG_UDBG_RTAS_CONSOLE */
2005-10-26 11:05:24 +04:00
void rtas_progress ( char * s , unsigned short hex )
2005-06-23 03:43:28 +04:00
{
struct device_node * root ;
2006-07-12 09:35:54 +04:00
int width ;
const int * p ;
2005-06-23 03:43:28 +04:00
char * os ;
static int display_character , set_indicator ;
2006-07-12 09:35:54 +04:00
static int display_width , display_lines , form_feed ;
2007-02-17 22:11:19 +03:00
static const int * row_width ;
2005-06-23 03:43:28 +04:00
static DEFINE_SPINLOCK ( progress_lock ) ;
2005-06-23 10:09:41 +04:00
static int current_line ;
2005-06-23 03:43:28 +04:00
static int pending_newline = 0 ; /* did last write end with unprinted newline? */
if ( ! rtas . base )
return ;
2005-06-23 10:09:41 +04:00
if ( display_width = = 0 ) {
display_width = 0x10 ;
if ( ( root = find_path_device ( " /rtas " ) ) ) {
2007-04-03 16:26:41 +04:00
if ( ( p = of_get_property ( root ,
2005-06-23 10:09:41 +04:00
" ibm,display-line-length " , NULL ) ) )
display_width = * p ;
2007-04-03 16:26:41 +04:00
if ( ( p = of_get_property ( root ,
2005-06-23 10:09:41 +04:00
" ibm,form-feed " , NULL ) ) )
form_feed = * p ;
2007-04-03 16:26:41 +04:00
if ( ( p = of_get_property ( root ,
2005-06-23 10:09:41 +04:00
" ibm,display-number-of-lines " , NULL ) ) )
display_lines = * p ;
2007-04-03 16:26:41 +04:00
row_width = of_get_property ( root ,
2005-06-23 10:09:41 +04:00
" ibm,display-truncation-length " , NULL ) ;
}
2005-06-23 03:43:28 +04:00
display_character = rtas_token ( " display-character " ) ;
set_indicator = rtas_token ( " set-indicator " ) ;
}
if ( display_character = = RTAS_UNKNOWN_SERVICE ) {
/* use hex display if available */
if ( set_indicator ! = RTAS_UNKNOWN_SERVICE )
rtas_call ( set_indicator , 3 , 1 , NULL , 6 , 0 , hex ) ;
return ;
}
spin_lock ( & progress_lock ) ;
/*
* Last write ended with newline , but we didn ' t print it since
* it would just clear the bottom line of output . Print it now
* instead .
*
2005-06-23 10:09:41 +04:00
* If no newline is pending and form feed is supported , clear the
* display with a form feed ; otherwise , print a CR to start output
* at the beginning of the line .
2005-06-23 03:43:28 +04:00
*/
if ( pending_newline ) {
rtas_call ( display_character , 1 , 1 , NULL , ' \r ' ) ;
rtas_call ( display_character , 1 , 1 , NULL , ' \n ' ) ;
pending_newline = 0 ;
} else {
2005-06-23 10:09:41 +04:00
current_line = 0 ;
if ( form_feed )
rtas_call ( display_character , 1 , 1 , NULL ,
( char ) form_feed ) ;
else
rtas_call ( display_character , 1 , 1 , NULL , ' \r ' ) ;
2005-06-23 03:43:28 +04:00
}
2005-06-23 10:09:41 +04:00
if ( row_width )
width = row_width [ current_line ] ;
else
width = display_width ;
2005-06-23 03:43:28 +04:00
os = s ;
while ( * os ) {
if ( * os = = ' \n ' | | * os = = ' \r ' ) {
/* If newline is the last character, save it
* until next call to avoid bumping up the
* display output .
*/
if ( * os = = ' \n ' & & ! os [ 1 ] ) {
pending_newline = 1 ;
2005-06-23 10:09:41 +04:00
current_line + + ;
if ( current_line > display_lines - 1 )
current_line = display_lines - 1 ;
2005-06-23 03:43:28 +04:00
spin_unlock ( & progress_lock ) ;
return ;
}
/* RTAS wants CR-LF, not just LF */
if ( * os = = ' \n ' ) {
rtas_call ( display_character , 1 , 1 , NULL , ' \r ' ) ;
rtas_call ( display_character , 1 , 1 , NULL , ' \n ' ) ;
} else {
/* CR might be used to re-draw a line, so we'll
* leave it alone and not add LF .
*/
rtas_call ( display_character , 1 , 1 , NULL , * os ) ;
}
2005-06-23 10:09:41 +04:00
if ( row_width )
width = row_width [ current_line ] ;
else
width = display_width ;
2005-06-23 03:43:28 +04:00
} else {
width - - ;
rtas_call ( display_character , 1 , 1 , NULL , * os ) ;
}
os + + ;
/* if we overwrite the screen length */
if ( width < = 0 )
while ( ( * os ! = 0 ) & & ( * os ! = ' \n ' ) & & ( * os ! = ' \r ' ) )
os + + ;
}
spin_unlock ( & progress_lock ) ;
}
2005-11-03 06:41:19 +03:00
EXPORT_SYMBOL ( rtas_progress ) ; /* needed by rtas_flash module */
2005-06-23 03:43:28 +04:00
2005-10-26 11:05:24 +04:00
int rtas_token ( const char * service )
2005-04-17 02:20:36 +04:00
{
2006-07-12 09:35:54 +04:00
const int * tokp ;
2005-10-26 11:05:24 +04:00
if ( rtas . dev = = NULL )
2005-04-17 02:20:36 +04:00
return RTAS_UNKNOWN_SERVICE ;
2007-04-03 16:26:41 +04:00
tokp = of_get_property ( rtas . dev , service , NULL ) ;
2005-04-17 02:20:36 +04:00
return tokp ? * tokp : RTAS_UNKNOWN_SERVICE ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_token ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 03:50:45 +03:00
int rtas_service_present ( const char * service )
{
return rtas_token ( service ) ! = RTAS_UNKNOWN_SERVICE ;
}
EXPORT_SYMBOL ( rtas_service_present ) ;
2005-10-26 11:05:24 +04:00
# ifdef CONFIG_RTAS_ERROR_LOGGING
2005-04-17 02:20:36 +04:00
/*
* Return the firmware - specified size of the error log buffer
* for all rtas calls that require an error buffer argument .
* This includes ' check - exception ' and ' rtas - last - error ' .
*/
int rtas_get_error_log_max ( void )
{
static int rtas_error_log_max ;
if ( rtas_error_log_max )
return rtas_error_log_max ;
rtas_error_log_max = rtas_token ( " rtas-error-log-max " ) ;
if ( ( rtas_error_log_max = = RTAS_UNKNOWN_SERVICE ) | |
( rtas_error_log_max > RTAS_ERROR_LOG_MAX ) ) {
2005-10-26 11:05:24 +04:00
printk ( KERN_WARNING " RTAS: bad log buffer size %d \n " ,
rtas_error_log_max ) ;
2005-04-17 02:20:36 +04:00
rtas_error_log_max = RTAS_ERROR_LOG_MAX ;
}
return rtas_error_log_max ;
}
2005-10-26 11:05:24 +04:00
EXPORT_SYMBOL ( rtas_get_error_log_max ) ;
2005-04-17 02:20:36 +04:00
2005-10-26 11:05:24 +04:00
char rtas_err_buf [ RTAS_ERROR_LOG_MAX ] ;
int rtas_last_error_token ;
2005-04-17 02:20:36 +04:00
/** Return a copy of the detailed error text associated with the
* most recent failed call to rtas . Because the error text
* might go stale if there are any other intervening rtas calls ,
* this routine must be called atomically with whatever produced
* the error ( i . e . with rtas . lock still held from the previous call ) .
*/
2005-10-26 11:05:24 +04:00
static char * __fetch_rtas_last_error ( char * altbuf )
2005-04-17 02:20:36 +04:00
{
struct rtas_args err_args , save_args ;
u32 bufsz ;
2005-10-26 11:05:24 +04:00
char * buf = NULL ;
if ( rtas_last_error_token = = - 1 )
return NULL ;
2005-04-17 02:20:36 +04:00
bufsz = rtas_get_error_log_max ( ) ;
2005-10-26 11:05:24 +04:00
err_args . token = rtas_last_error_token ;
2005-04-17 02:20:36 +04:00
err_args . nargs = 2 ;
err_args . nret = 1 ;
err_args . args [ 0 ] = ( rtas_arg_t ) __pa ( rtas_err_buf ) ;
err_args . args [ 1 ] = bufsz ;
err_args . args [ 2 ] = 0 ;
save_args = rtas . args ;
rtas . args = err_args ;
enter_rtas ( __pa ( & rtas . args ) ) ;
err_args = rtas . args ;
rtas . args = save_args ;
2005-10-26 11:05:24 +04:00
/* Log the error in the unlikely case that there was one. */
if ( unlikely ( err_args . args [ 2 ] = = 0 ) ) {
if ( altbuf ) {
buf = altbuf ;
} else {
buf = rtas_err_buf ;
if ( mem_init_done )
buf = kmalloc ( RTAS_ERROR_LOG_MAX , GFP_ATOMIC ) ;
}
if ( buf )
memcpy ( buf , rtas_err_buf , RTAS_ERROR_LOG_MAX ) ;
}
return buf ;
2005-04-17 02:20:36 +04:00
}
2005-10-26 11:05:24 +04:00
# define get_errorlog_buffer() kmalloc(RTAS_ERROR_LOG_MAX, GFP_KERNEL)
# else /* CONFIG_RTAS_ERROR_LOGGING */
# define __fetch_rtas_last_error(x) NULL
# define get_errorlog_buffer() NULL
# endif
2005-04-17 02:20:36 +04:00
int rtas_call ( int token , int nargs , int nret , int * outputs , . . . )
{
va_list list ;
2005-10-26 11:05:24 +04:00
int i ;
2005-04-17 02:20:36 +04:00
unsigned long s ;
struct rtas_args * rtas_args ;
2005-10-26 11:05:24 +04:00
char * buff_copy = NULL ;
2005-04-17 02:20:36 +04:00
int ret ;
2006-06-23 12:20:10 +04:00
if ( ! rtas . entry | | token = = RTAS_UNKNOWN_SERVICE )
2005-04-17 02:20:36 +04:00
return - 1 ;
/* Gotta do something different here, use global lock for now... */
spin_lock_irqsave ( & rtas . lock , s ) ;
rtas_args = & rtas . args ;
rtas_args - > token = token ;
rtas_args - > nargs = nargs ;
rtas_args - > nret = nret ;
rtas_args - > rets = ( rtas_arg_t * ) & ( rtas_args - > args [ nargs ] ) ;
va_start ( list , outputs ) ;
2005-10-26 11:05:24 +04:00
for ( i = 0 ; i < nargs ; + + i )
2005-04-17 02:20:36 +04:00
rtas_args - > args [ i ] = va_arg ( list , rtas_arg_t ) ;
va_end ( list ) ;
for ( i = 0 ; i < nret ; + + i )
rtas_args - > rets [ i ] = 0 ;
enter_rtas ( __pa ( rtas_args ) ) ;
/* A -1 return code indicates that the last command couldn't
be completed due to a hardware error . */
if ( rtas_args - > rets [ 0 ] = = - 1 )
2005-10-26 11:05:24 +04:00
buff_copy = __fetch_rtas_last_error ( NULL ) ;
2005-04-17 02:20:36 +04:00
if ( nret > 1 & & outputs ! = NULL )
for ( i = 0 ; i < nret - 1 ; + + i )
outputs [ i ] = rtas_args - > rets [ i + 1 ] ;
ret = ( nret > 0 ) ? rtas_args - > rets [ 0 ] : 0 ;
/* Gotta do something different here, use global lock for now... */
spin_unlock_irqrestore ( & rtas . lock , s ) ;
if ( buff_copy ) {
log_error ( buff_copy , ERR_TYPE_RTAS_LOG , 0 ) ;
if ( mem_init_done )
kfree ( buff_copy ) ;
}
return ret ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_call ) ;
2005-04-17 02:20:36 +04:00
2006-06-06 01:31:48 +04:00
/* For RTAS_BUSY (-2), delay for 1 millisecond. For an extended busy status
* code of 990 n , perform the hinted delay of 10 ^ n ( last digit ) milliseconds .
2005-04-17 02:20:36 +04:00
*/
2006-06-06 01:31:48 +04:00
unsigned int rtas_busy_delay_time ( int status )
2005-04-17 02:20:36 +04:00
{
2006-06-06 01:31:48 +04:00
int order ;
unsigned int ms = 0 ;
if ( status = = RTAS_BUSY ) {
ms = 1 ;
} else if ( status > = 9900 & & status < = 9905 ) {
order = status - 9900 ;
for ( ms = 1 ; order > 0 ; order - - )
ms * = 10 ;
}
2005-04-17 02:20:36 +04:00
2006-06-06 01:31:48 +04:00
return ms ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_busy_delay_time ) ;
2005-04-17 02:20:36 +04:00
2006-06-06 01:31:48 +04:00
/* For an RTAS busy status code, perform the hinted delay. */
unsigned int rtas_busy_delay ( int status )
{
unsigned int ms ;
2005-04-17 02:20:36 +04:00
2006-06-06 01:31:48 +04:00
might_sleep ( ) ;
ms = rtas_busy_delay_time ( status ) ;
if ( ms )
msleep ( ms ) ;
return ms ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_busy_delay ) ;
2005-04-17 02:20:36 +04:00
int rtas_error_rc ( int rtas_rc )
{
int rc ;
switch ( rtas_rc ) {
case - 1 : /* Hardware Error */
rc = - EIO ;
break ;
case - 3 : /* Bad indicator/domain/etc */
rc = - EINVAL ;
break ;
case - 9000 : /* Isolation error */
rc = - EFAULT ;
break ;
case - 9001 : /* Outstanding TCE/PTE */
rc = - EEXIST ;
break ;
case - 9002 : /* No usable slot */
rc = - ENODEV ;
break ;
default :
printk ( KERN_ERR " %s: unexpected RTAS error %d \n " ,
__FUNCTION__ , rtas_rc ) ;
rc = - ERANGE ;
break ;
}
return rc ;
}
int rtas_get_power_level ( int powerdomain , int * level )
{
int token = rtas_token ( " get-power-level " ) ;
int rc ;
if ( token = = RTAS_UNKNOWN_SERVICE )
return - ENOENT ;
while ( ( rc = rtas_call ( token , 1 , 2 , level , powerdomain ) ) = = RTAS_BUSY )
udelay ( 1 ) ;
if ( rc < 0 )
return rtas_error_rc ( rc ) ;
return rc ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_get_power_level ) ;
2005-04-17 02:20:36 +04:00
int rtas_set_power_level ( int powerdomain , int level , int * setlevel )
{
int token = rtas_token ( " set-power-level " ) ;
int rc ;
if ( token = = RTAS_UNKNOWN_SERVICE )
return - ENOENT ;
2006-06-06 01:31:48 +04:00
do {
2005-04-17 02:20:36 +04:00
rc = rtas_call ( token , 2 , 2 , setlevel , powerdomain , level ) ;
2006-06-06 01:31:48 +04:00
} while ( rtas_busy_delay ( rc ) ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
return rtas_error_rc ( rc ) ;
return rc ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_set_power_level ) ;
2005-04-17 02:20:36 +04:00
int rtas_get_sensor ( int sensor , int index , int * state )
{
int token = rtas_token ( " get-sensor-state " ) ;
int rc ;
if ( token = = RTAS_UNKNOWN_SERVICE )
return - ENOENT ;
2006-06-06 01:31:48 +04:00
do {
2005-04-17 02:20:36 +04:00
rc = rtas_call ( token , 2 , 2 , state , sensor , index ) ;
2006-06-06 01:31:48 +04:00
} while ( rtas_busy_delay ( rc ) ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
return rtas_error_rc ( rc ) ;
return rc ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_get_sensor ) ;
2005-04-17 02:20:36 +04:00
int rtas_set_indicator ( int indicator , int index , int new_value )
{
int token = rtas_token ( " set-indicator " ) ;
int rc ;
if ( token = = RTAS_UNKNOWN_SERVICE )
return - ENOENT ;
2006-06-06 01:31:48 +04:00
do {
2005-04-17 02:20:36 +04:00
rc = rtas_call ( token , 3 , 1 , NULL , indicator , index , new_value ) ;
2006-06-06 01:31:48 +04:00
} while ( rtas_busy_delay ( rc ) ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
return rtas_error_rc ( rc ) ;
return rc ;
}
2006-06-23 12:20:11 +04:00
EXPORT_SYMBOL ( rtas_set_indicator ) ;
2005-04-17 02:20:36 +04:00
2006-07-28 01:29:00 +04:00
/*
* Ignoring RTAS extended delay
*/
int rtas_set_indicator_fast ( int indicator , int index , int new_value )
{
int rc ;
int token = rtas_token ( " set-indicator " ) ;
if ( token = = RTAS_UNKNOWN_SERVICE )
return - ENOENT ;
rc = rtas_call ( token , 3 , 1 , NULL , indicator , index , new_value ) ;
WARN_ON ( rc = = - 2 | | ( rc > = 9900 & & rc < = 9905 ) ) ;
if ( rc < 0 )
return rtas_error_rc ( rc ) ;
return rc ;
}
2005-10-26 11:05:24 +04:00
void rtas_restart ( char * cmd )
2005-04-17 02:20:36 +04:00
{
2005-11-03 06:41:19 +03:00
if ( rtas_flash_term_hook )
rtas_flash_term_hook ( SYS_RESTART ) ;
2005-04-17 02:20:36 +04:00
printk ( " RTAS system-reboot returned %d \n " ,
rtas_call ( rtas_token ( " system-reboot " ) , 0 , 1 , NULL ) ) ;
for ( ; ; ) ;
}
2005-10-26 11:05:24 +04:00
void rtas_power_off ( void )
2005-04-17 02:20:36 +04:00
{
2005-11-03 06:41:19 +03:00
if ( rtas_flash_term_hook )
rtas_flash_term_hook ( SYS_POWER_OFF ) ;
2005-04-17 02:20:36 +04:00
/* allow power on only with power button press */
printk ( " RTAS power-off returned %d \n " ,
rtas_call ( rtas_token ( " power-off " ) , 2 , 1 , NULL , - 1 , - 1 ) ) ;
for ( ; ; ) ;
}
2005-10-26 11:05:24 +04:00
void rtas_halt ( void )
2005-04-17 02:20:36 +04:00
{
2005-11-03 06:41:19 +03:00
if ( rtas_flash_term_hook )
rtas_flash_term_hook ( SYS_HALT ) ;
/* allow power on only with power button press */
printk ( " RTAS power-off returned %d \n " ,
rtas_call ( rtas_token ( " power-off " ) , 2 , 1 , NULL , - 1 , - 1 ) ) ;
for ( ; ; ) ;
2005-04-17 02:20:36 +04:00
}
/* Must be in the RMO region, so we place it here */
static char rtas_os_term_buf [ 2048 ] ;
void rtas_os_term ( char * str )
{
int status ;
2006-08-21 20:11:32 +04:00
if ( panic_timeout )
return ;
2005-04-17 02:20:36 +04:00
if ( RTAS_UNKNOWN_SERVICE = = rtas_token ( " ibm,os-term " ) )
return ;
snprintf ( rtas_os_term_buf , 2048 , " OS panic: %s " , str ) ;
do {
status = rtas_call ( rtas_token ( " ibm,os-term " ) , 1 , 1 , NULL ,
__pa ( rtas_os_term_buf ) ) ;
2006-06-06 01:31:48 +04:00
} while ( rtas_busy_delay ( status ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-06 01:31:48 +04:00
if ( status ! = 0 )
printk ( KERN_EMERG " ibm,os-term call failed %d \n " ,
2005-04-17 02:20:36 +04:00
status ) ;
}
2006-01-14 03:39:24 +03:00
static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE ;
# ifdef CONFIG_PPC_PSERIES
static void rtas_percpu_suspend_me ( void * info )
{
2006-02-03 10:18:39 +03:00
int i ;
2006-01-14 03:39:24 +03:00
long rc ;
long flags ;
struct rtas_suspend_me_data * data =
( struct rtas_suspend_me_data * ) info ;
/*
* We use " waiting " to indicate our state . As long
* as it is > 0 , we are still trying to all join up .
* If it goes to 0 , we have successfully joined up and
2006-03-30 16:49:40 +04:00
* one thread got H_CONTINUE . If any error happens ,
2006-01-14 03:39:24 +03:00
* we set it to < 0.
*/
local_irq_save ( flags ) ;
do {
rc = plpar_hcall_norets ( H_JOIN ) ;
smp_rmb ( ) ;
2006-03-30 16:49:40 +04:00
} while ( rc = = H_SUCCESS & & data - > waiting > 0 ) ;
if ( rc = = H_SUCCESS )
2006-01-14 03:39:24 +03:00
goto out ;
2006-03-30 16:49:40 +04:00
if ( rc = = H_CONTINUE ) {
2006-01-14 03:39:24 +03:00
data - > waiting = 0 ;
2006-02-03 10:18:46 +03:00
data - > args - > args [ data - > args - > nargs ] =
rtas_call ( ibm_suspend_me_token , 0 , 1 , NULL ) ;
2006-03-29 02:50:51 +04:00
for_each_possible_cpu ( i )
2006-02-03 10:18:39 +03:00
plpar_hcall_norets ( H_PROD , i ) ;
2006-01-14 03:39:24 +03:00
} else {
data - > waiting = - EBUSY ;
2006-03-30 16:49:40 +04:00
printk ( KERN_ERR " Error on H_JOIN hypervisor call \n " ) ;
2006-01-14 03:39:24 +03:00
}
out :
local_irq_restore ( flags ) ;
return ;
}
static int rtas_ibm_suspend_me ( struct rtas_args * args )
{
int i ;
2006-06-13 04:49:20 +04:00
long state ;
long rc ;
2006-07-19 02:01:28 +04:00
unsigned long retbuf [ PLPAR_HCALL_BUFSIZE ] ;
2006-01-14 03:39:24 +03:00
struct rtas_suspend_me_data data ;
2006-06-13 04:49:20 +04:00
/* Make sure the state is valid */
2006-07-19 02:01:28 +04:00
rc = plpar_hcall ( H_VASI_STATE , retbuf ,
( ( u64 ) args - > args [ 0 ] < < 32 ) | args - > args [ 1 ] ) ;
state = retbuf [ 0 ] ;
2006-06-13 04:49:20 +04:00
if ( rc ) {
printk ( KERN_ERR " rtas_ibm_suspend_me: vasi_state returned %ld \n " , rc ) ;
return rc ;
} else if ( state = = H_VASI_ENABLED ) {
args - > args [ args - > nargs ] = RTAS_NOT_SUSPENDABLE ;
return 0 ;
} else if ( state ! = H_VASI_SUSPENDING ) {
printk ( KERN_ERR " rtas_ibm_suspend_me: vasi_state returned state %ld \n " ,
state ) ;
args - > args [ args - > nargs ] = - 1 ;
return 0 ;
}
2006-01-14 03:39:24 +03:00
data . waiting = 1 ;
data . args = args ;
/* Call function on all CPUs. One of us will make the
* rtas call
*/
if ( on_each_cpu ( rtas_percpu_suspend_me , & data , 1 , 0 ) )
data . waiting = - EINVAL ;
if ( data . waiting ! = 0 )
printk ( KERN_ERR " Error doing global join \n " ) ;
/* Prod each CPU. This won't hurt, and will wake
2006-03-30 16:49:40 +04:00
* anyone we successfully put to sleep with H_JOIN .
2006-01-14 03:39:24 +03:00
*/
2006-03-29 02:50:51 +04:00
for_each_possible_cpu ( i )
2006-01-14 03:39:24 +03:00
plpar_hcall_norets ( H_PROD , i ) ;
return data . waiting ;
}
# else /* CONFIG_PPC_PSERIES */
static int rtas_ibm_suspend_me ( struct rtas_args * args )
{
return - ENOSYS ;
}
# endif
2005-04-17 02:20:36 +04:00
asmlinkage int ppc_rtas ( struct rtas_args __user * uargs )
{
struct rtas_args args ;
unsigned long flags ;
2005-10-26 11:05:24 +04:00
char * buff_copy , * errbuf = NULL ;
2005-04-17 02:20:36 +04:00
int nargs ;
2006-01-14 03:39:24 +03:00
int rc ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( copy_from_user ( & args , uargs , 3 * sizeof ( u32 ) ) ! = 0 )
return - EFAULT ;
nargs = args . nargs ;
if ( nargs > ARRAY_SIZE ( args . args )
| | args . nret > ARRAY_SIZE ( args . args )
| | nargs + args . nret > ARRAY_SIZE ( args . args ) )
return - EINVAL ;
/* Copy in args. */
if ( copy_from_user ( args . args , uargs - > args ,
nargs * sizeof ( rtas_arg_t ) ) ! = 0 )
return - EFAULT ;
2006-01-14 03:39:24 +03:00
if ( args . token = = RTAS_UNKNOWN_SERVICE )
return - EINVAL ;
/* Need to handle ibm,suspend_me call specially */
if ( args . token = = ibm_suspend_me_token ) {
rc = rtas_ibm_suspend_me ( & args ) ;
if ( rc )
return rc ;
goto copy_return ;
}
2005-10-26 11:05:24 +04:00
buff_copy = get_errorlog_buffer ( ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & rtas . lock , flags ) ;
rtas . args = args ;
enter_rtas ( __pa ( & rtas . args ) ) ;
args = rtas . args ;
args . rets = & args . args [ nargs ] ;
/* A -1 return code indicates that the last command couldn't
be completed due to a hardware error . */
2005-10-26 11:05:24 +04:00
if ( args . rets [ 0 ] = = - 1 )
errbuf = __fetch_rtas_last_error ( buff_copy ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & rtas . lock , flags ) ;
if ( buff_copy ) {
2005-10-26 11:05:24 +04:00
if ( errbuf )
log_error ( errbuf , ERR_TYPE_RTAS_LOG , 0 ) ;
2005-04-17 02:20:36 +04:00
kfree ( buff_copy ) ;
}
2006-01-14 03:39:24 +03:00
copy_return :
2005-04-17 02:20:36 +04:00
/* Copy out args. */
if ( copy_to_user ( uargs - > args + nargs ,
args . args + nargs ,
args . nret * sizeof ( rtas_arg_t ) ) ! = 0 )
return - EFAULT ;
return 0 ;
}
/*
2006-01-10 02:10:13 +03:00
* Call early during boot , before mem init or bootmem , to retrieve the RTAS
2005-04-17 02:20:36 +04:00
* informations from the device - tree and allocate the RMO buffer for userland
* accesses .
*/
void __init rtas_initialize ( void )
{
2005-10-26 11:05:24 +04:00
unsigned long rtas_region = RTAS_INSTANTIATE_MAX ;
2005-04-17 02:20:36 +04:00
/* Get RTAS dev node and fill up our "rtas" structure with infos
* about it .
*/
rtas . dev = of_find_node_by_name ( NULL , " rtas " ) ;
if ( rtas . dev ) {
2006-07-12 09:35:54 +04:00
const u32 * basep , * entryp , * sizep ;
2005-04-17 02:20:36 +04:00
2007-04-03 16:26:41 +04:00
basep = of_get_property ( rtas . dev , " linux,rtas-base " , NULL ) ;
sizep = of_get_property ( rtas . dev , " rtas-size " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( basep ! = NULL & & sizep ! = NULL ) {
rtas . base = * basep ;
rtas . size = * sizep ;
2007-04-03 16:26:41 +04:00
entryp = of_get_property ( rtas . dev ,
2006-07-12 09:35:54 +04:00
" linux,rtas-entry " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( entryp = = NULL ) /* Ugh */
rtas . entry = rtas . base ;
else
rtas . entry = * entryp ;
} else
rtas . dev = NULL ;
}
2005-10-26 11:05:24 +04:00
if ( ! rtas . dev )
return ;
2005-04-17 02:20:36 +04:00
/* If RTAS was found, allocate the RMO buffer for it and look for
* the stop - self token if any
*/
2005-10-26 11:05:24 +04:00
# ifdef CONFIG_PPC64
2006-03-28 16:15:54 +04:00
if ( machine_is ( pseries ) & & firmware_has_feature ( FW_FEATURE_LPAR ) ) {
2005-10-26 11:05:24 +04:00
rtas_region = min ( lmb . rmo_size , RTAS_INSTANTIATE_MAX ) ;
2006-01-14 03:39:24 +03:00
ibm_suspend_me_token = rtas_token ( " ibm,suspend-me " ) ;
}
2005-10-26 11:05:24 +04:00
# endif
rtas_rmo_buf = lmb_alloc_base ( RTAS_RMOBUF_MAX , PAGE_SIZE , rtas_region ) ;
2005-04-17 02:20:36 +04:00
2005-10-26 11:05:24 +04:00
# ifdef CONFIG_RTAS_ERROR_LOGGING
rtas_last_error_token = rtas_token ( " rtas-last-error " ) ;
# endif
2005-04-17 02:20:36 +04:00
}
2006-06-23 12:20:13 +04:00
int __init early_init_dt_scan_rtas ( unsigned long node ,
const char * uname , int depth , void * data )
{
u32 * basep , * entryp , * sizep ;
if ( depth ! = 1 | | strcmp ( uname , " rtas " ) ! = 0 )
return 0 ;
basep = of_get_flat_dt_prop ( node , " linux,rtas-base " , NULL ) ;
entryp = of_get_flat_dt_prop ( node , " linux,rtas-entry " , NULL ) ;
sizep = of_get_flat_dt_prop ( node , " rtas-size " , NULL ) ;
if ( basep & & entryp & & sizep ) {
rtas . base = * basep ;
rtas . entry = * entryp ;
rtas . size = * sizep ;
}
2006-06-23 12:20:16 +04:00
# ifdef CONFIG_UDBG_RTAS_CONSOLE
basep = of_get_flat_dt_prop ( node , " put-term-char " , NULL ) ;
if ( basep )
rtas_putchar_token = * basep ;
basep = of_get_flat_dt_prop ( node , " get-term-char " , NULL ) ;
if ( basep )
rtas_getchar_token = * basep ;
2006-08-17 08:12:14 +04:00
if ( rtas_putchar_token ! = RTAS_UNKNOWN_SERVICE & &
rtas_getchar_token ! = RTAS_UNKNOWN_SERVICE )
udbg_init_rtas_console ( ) ;
2006-06-23 12:20:16 +04:00
# endif
2006-06-23 12:20:13 +04:00
/* break now */
return 1 ;
}