2005-04-16 15:20:36 -07:00
/*
* S390 version
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 1999 , 2007
2005-04-16 15:20:36 -07:00
* Author ( s ) : Martin Schwidefsky ( schwidefsky @ de . ibm . com ) ,
* Christian Borntraeger ( cborntra @ de . ibm . com ) ,
*/
2008-12-25 13:39:34 +01:00
# define KMSG_COMPONENT "cpcmd"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/stddef.h>
# include <linux/string.h>
# include <asm/ebcdic.h>
# include <asm/cpcmd.h>
2007-02-05 21:16:54 +01:00
# include <asm/io.h>
2005-04-16 15:20:36 -07:00
static DEFINE_SPINLOCK ( cpcmd_lock ) ;
2005-06-25 14:55:32 -07:00
static char cpcmd_buf [ 241 ] ;
2005-04-16 15:20:36 -07:00
2007-10-12 16:11:46 +02:00
static int diag8_noresponse ( int cmdlen )
{
register unsigned long reg2 asm ( " 2 " ) = ( addr_t ) cpcmd_buf ;
register unsigned long reg3 asm ( " 3 " ) = cmdlen ;
asm volatile (
# ifndef CONFIG_64BIT
" diag %1,%0,0x8 \n "
# else /* CONFIG_64BIT */
" sam31 \n "
" diag %1,%0,0x8 \n "
" sam64 \n "
# endif /* CONFIG_64BIT */
: " +d " ( reg3 ) : " d " ( reg2 ) : " cc " ) ;
return reg3 ;
}
static int diag8_response ( int cmdlen , char * response , int * rlen )
{
register unsigned long reg2 asm ( " 2 " ) = ( addr_t ) cpcmd_buf ;
register unsigned long reg3 asm ( " 3 " ) = ( addr_t ) response ;
register unsigned long reg4 asm ( " 4 " ) = cmdlen | 0x40000000L ;
register unsigned long reg5 asm ( " 5 " ) = * rlen ;
asm volatile (
# ifndef CONFIG_64BIT
" diag %2,%0,0x8 \n "
" brc 8,1f \n "
" ar %1,%4 \n "
# else /* CONFIG_64BIT */
" sam31 \n "
" diag %2,%0,0x8 \n "
" sam64 \n "
" brc 8,1f \n "
" agr %1,%4 \n "
# endif /* CONFIG_64BIT */
" 1: \n "
: " +d " ( reg4 ) , " +d " ( reg5 )
: " d " ( reg2 ) , " d " ( reg3 ) , " d " ( * rlen ) : " cc " ) ;
* rlen = reg5 ;
return reg4 ;
}
2005-04-16 15:20:36 -07:00
/*
2006-12-04 15:40:30 +01:00
* __cpcmd has some restrictions over cpcmd
* - the response buffer must reside below 2 GB ( if any )
* - __cpcmd is unlocked and therefore not SMP - safe
2005-04-16 15:20:36 -07:00
*/
2005-06-25 14:55:32 -07:00
int __cpcmd ( const char * cmd , char * response , int rlen , int * response_code )
2005-04-16 15:20:36 -07:00
{
2007-10-12 16:11:46 +02:00
int cmdlen ;
int rc ;
int response_len ;
2005-04-16 15:20:36 -07:00
cmdlen = strlen ( cmd ) ;
BUG_ON ( cmdlen > 240 ) ;
2005-06-25 14:55:32 -07:00
memcpy ( cpcmd_buf , cmd , cmdlen ) ;
2005-04-16 15:20:36 -07:00
ASCEBC ( cpcmd_buf , cmdlen ) ;
2007-10-12 16:11:46 +02:00
if ( response ) {
2005-04-16 15:20:36 -07:00
memset ( response , 0 , rlen ) ;
2007-10-12 16:11:46 +02:00
response_len = rlen ;
rc = diag8_response ( cmdlen , response , & rlen ) ;
EBCASC ( response , response_len ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-10-12 16:11:46 +02:00
rc = diag8_noresponse ( cmdlen ) ;
2005-04-16 15:20:36 -07:00
}
2007-10-12 16:11:46 +02:00
if ( response_code )
* response_code = rc ;
return rlen ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( __cpcmd ) ;
2005-06-25 14:55:32 -07:00
int cpcmd ( const char * cmd , char * response , int rlen , int * response_code )
2005-04-16 15:20:36 -07:00
{
char * lowbuf ;
2005-06-25 14:55:32 -07:00
int len ;
2006-12-04 15:40:30 +01:00
unsigned long flags ;
2005-06-25 14:55:32 -07:00
2007-02-05 21:16:54 +01:00
if ( ( virt_to_phys ( response ) ! = ( unsigned long ) response ) | |
( ( ( unsigned long ) response + rlen ) > > 31 ) ) {
2005-04-16 15:20:36 -07:00
lowbuf = kmalloc ( rlen , GFP_KERNEL | GFP_DMA ) ;
if ( ! lowbuf ) {
2008-12-25 13:39:34 +01:00
pr_warning ( " The cpcmd kernel function failed to "
" allocate a response buffer \n " ) ;
2005-06-25 14:55:32 -07:00
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
2006-12-04 15:40:30 +01:00
spin_lock_irqsave ( & cpcmd_lock , flags ) ;
2005-06-25 14:55:32 -07:00
len = __cpcmd ( cmd , lowbuf , rlen , response_code ) ;
2006-12-04 15:40:30 +01:00
spin_unlock_irqrestore ( & cpcmd_lock , flags ) ;
2005-04-16 15:20:36 -07:00
memcpy ( response , lowbuf , rlen ) ;
kfree ( lowbuf ) ;
2007-02-05 21:16:54 +01:00
} else {
spin_lock_irqsave ( & cpcmd_lock , flags ) ;
len = __cpcmd ( cmd , response , rlen , response_code ) ;
spin_unlock_irqrestore ( & cpcmd_lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
2005-06-25 14:55:32 -07:00
return len ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( cpcmd ) ;