2015-07-08 11:20:04 +03:00
/*
* Copyright IBM Corp . 2015
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com >
*/
# include <linux/kernel.h>
# include <asm/ebcdic.h>
# include <asm/irq.h>
# include <asm/lowcore.h>
# include <asm/processor.h>
# include <asm/sclp.h>
2015-11-24 18:28:55 +03:00
# define EVTYP_VT220MSG_MASK 0x00000040
# define EVTYP_MSG_MASK 0x40000000
2016-06-30 11:24:18 +03:00
static char _sclp_work_area [ 4096 ] __aligned ( PAGE_SIZE ) __section ( data ) ;
static bool have_vt220 __section ( data ) ;
static bool have_linemode __section ( data ) ;
2015-07-08 11:20:04 +03:00
2017-01-27 17:54:57 +03:00
int sclp_init_state __section ( data ) = sclp_init_state_uninitialized ;
2015-07-08 11:20:04 +03:00
static void _sclp_wait_int ( void )
{
2017-01-11 11:14:45 +03:00
unsigned long psw_mask , addr , flags ;
2015-07-08 11:20:04 +03:00
psw_t psw_ext_save , psw_wait ;
2017-01-11 11:14:45 +03:00
union ctlreg0 cr0 , cr0_new ;
2015-07-08 11:20:04 +03:00
2017-01-11 11:14:45 +03:00
raw_local_irq_save ( flags ) ;
__ctl_store ( cr0 . val , 0 , 0 ) ;
cr0_new . val = cr0 . val & ~ CR0_IRQ_SUBCLASS_MASK ;
cr0_new . lap = 0 ;
cr0_new . sssm = 1 ;
__ctl_load ( cr0_new . val , 0 , 0 ) ;
2015-07-08 11:20:04 +03:00
psw_ext_save = S390_lowcore . external_new_psw ;
2015-11-04 16:16:57 +03:00
psw_mask = __extract_psw ( ) ;
2015-07-08 11:20:04 +03:00
S390_lowcore . external_new_psw . mask = psw_mask ;
psw_wait . mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT ;
S390_lowcore . ext_int_code = 0 ;
do {
asm volatile (
" larl %[addr],0f \n "
" stg %[addr],%[psw_wait_addr] \n "
" stg %[addr],%[psw_ext_addr] \n "
" lpswe %[psw_wait] \n "
" 0: \n "
: [ addr ] " =&d " ( addr ) ,
[ psw_wait_addr ] " =Q " ( psw_wait . addr ) ,
[ psw_ext_addr ] " =Q " ( S390_lowcore . external_new_psw . addr )
: [ psw_wait ] " Q " ( psw_wait )
: " cc " , " memory " ) ;
} while ( S390_lowcore . ext_int_code ! = EXT_IRQ_SERVICE_SIG ) ;
S390_lowcore . external_new_psw = psw_ext_save ;
2017-01-11 11:14:45 +03:00
__ctl_load ( cr0 . val , 0 , 0 ) ;
raw_local_irq_restore ( flags ) ;
2015-07-08 11:20:04 +03:00
}
static int _sclp_servc ( unsigned int cmd , char * sccb )
{
unsigned int cc ;
do {
asm volatile (
" .insn rre,0xb2200000,%1,%2 \n "
" ipm %0 \n "
: " =d " ( cc ) : " d " ( cmd ) , " a " ( sccb )
: " cc " , " memory " ) ;
cc > > = 28 ;
if ( cc = = 3 )
return - EINVAL ;
_sclp_wait_int ( ) ;
} while ( cc ! = 0 ) ;
return ( * ( unsigned short * ) ( sccb + 6 ) = = 0x20 ) ? 0 : - EIO ;
}
static int _sclp_setup ( int disable )
{
static unsigned char init_sccb [ ] = {
0x00 , 0x1c ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x04 ,
2015-11-24 18:28:55 +03:00
0x80 , 0x00 , 0x00 , 0x00 , 0x40 , 0x00 , 0x00 , 0x40 ,
2015-07-08 11:20:04 +03:00
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
unsigned int * masks ;
int rc ;
memcpy ( _sclp_work_area , init_sccb , 28 ) ;
masks = ( unsigned int * ) ( _sclp_work_area + 12 ) ;
if ( disable )
memset ( masks , 0 , 16 ) ;
/* SCLP write mask */
rc = _sclp_servc ( 0x00780005 , _sclp_work_area ) ;
if ( rc )
return rc ;
2015-11-24 18:28:55 +03:00
have_vt220 = masks [ 2 ] & EVTYP_VT220MSG_MASK ;
have_linemode = masks [ 2 ] & EVTYP_MSG_MASK ;
2015-07-08 11:20:04 +03:00
return 0 ;
}
2015-11-24 18:28:55 +03:00
/* Output multi-line text using SCLP Message interface. */
2017-01-11 11:14:52 +03:00
static void _sclp_print_lm ( const char * str , unsigned int len )
2015-07-08 11:20:04 +03:00
{
static unsigned char write_head [ ] = {
/* sccb header */
0x00 , 0x52 , /* 0 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 2 */
/* evbuf */
0x00 , 0x4a , /* 8 */
0x02 , 0x00 , 0x00 , 0x00 , /* 10 */
/* mdb */
0x00 , 0x44 , /* 14 */
0x00 , 0x01 , /* 16 */
0xd4 , 0xc4 , 0xc2 , 0x40 , /* 18 */
0x00 , 0x00 , 0x00 , 0x01 , /* 22 */
/* go */
0x00 , 0x38 , /* 26 */
0x00 , 0x01 , /* 28 */
0x00 , 0x00 , 0x00 , 0x00 , /* 30 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 34 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 42 */
0x00 , 0x00 , 0x00 , 0x00 , /* 50 */
0x00 , 0x00 , /* 54 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 56 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 64 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 72 */
0x00 , 0x00 , /* 80 */
} ;
static unsigned char write_mto [ ] = {
/* mto */
0x00 , 0x0a , /* 0 */
0x00 , 0x04 , /* 2 */
0x10 , 0x00 , /* 4 */
0x00 , 0x00 , 0x00 , 0x00 /* 6 */
} ;
2017-01-11 13:07:39 +03:00
unsigned char * ptr , * end_ptr , ch ;
2017-01-11 11:14:52 +03:00
unsigned int count , num ;
2015-07-08 11:20:04 +03:00
2017-01-11 11:14:52 +03:00
num = 0 ;
2015-07-08 11:20:04 +03:00
memcpy ( _sclp_work_area , write_head , sizeof ( write_head ) ) ;
ptr = _sclp_work_area + sizeof ( write_head ) ;
2017-01-11 13:07:39 +03:00
end_ptr = _sclp_work_area + sizeof ( _sclp_work_area ) - 1 ;
2015-07-08 11:20:04 +03:00
do {
2017-01-11 13:07:39 +03:00
if ( ptr + sizeof ( write_mto ) > end_ptr )
break ;
2015-07-08 11:20:04 +03:00
memcpy ( ptr , write_mto , sizeof ( write_mto ) ) ;
2017-01-11 11:14:52 +03:00
for ( count = sizeof ( write_mto ) ; num < len ; count + + ) {
num + + ;
ch = * str + + ;
2015-07-08 11:20:04 +03:00
if ( ch = = 0x0a )
break ;
2017-01-11 13:07:39 +03:00
if ( ptr > end_ptr )
break ;
2015-07-08 11:20:04 +03:00
ptr [ count ] = _ascebc [ ch ] ;
}
/* Update length fields in mto, mdb, evbuf and sccb */
* ( unsigned short * ) ptr = count ;
* ( unsigned short * ) ( _sclp_work_area + 14 ) + = count ;
* ( unsigned short * ) ( _sclp_work_area + 8 ) + = count ;
* ( unsigned short * ) ( _sclp_work_area + 0 ) + = count ;
ptr + = count ;
2017-01-11 11:14:52 +03:00
} while ( num < len ) ;
2015-07-08 11:20:04 +03:00
/* SCLP write data */
2015-11-24 18:28:55 +03:00
_sclp_servc ( 0x00760005 , _sclp_work_area ) ;
2015-07-08 11:20:04 +03:00
}
2015-11-24 18:28:55 +03:00
/* Output multi-line text (plus a newline) using SCLP VT220
* interface .
*/
2017-01-11 11:14:52 +03:00
static void _sclp_print_vt220 ( const char * str , unsigned int len )
2015-07-08 11:20:04 +03:00
{
2015-11-24 18:28:55 +03:00
static unsigned char const write_head [ ] = {
/* sccb header */
0x00 , 0x0e ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
/* evbuf header */
0x00 , 0x06 ,
0x1a , 0x00 , 0x00 , 0x00 ,
} ;
2015-07-08 11:20:04 +03:00
2015-11-24 18:28:55 +03:00
if ( sizeof ( write_head ) + len > = sizeof ( _sclp_work_area ) )
len = sizeof ( _sclp_work_area ) - sizeof ( write_head ) - 1 ;
memcpy ( _sclp_work_area , write_head , sizeof ( write_head ) ) ;
memcpy ( _sclp_work_area + sizeof ( write_head ) , str , len ) ;
_sclp_work_area [ sizeof ( write_head ) + len ] = ' \n ' ;
/* Update length fields in evbuf and sccb headers */
* ( unsigned short * ) ( _sclp_work_area + 8 ) + = len + 1 ;
* ( unsigned short * ) ( _sclp_work_area + 0 ) + = len + 1 ;
/* SCLP write data */
( void ) _sclp_servc ( 0x00760005 , _sclp_work_area ) ;
}
/* Output one or more lines of text on the SCLP console (VT220 and /
* or line - mode ) . All lines get terminated ; no need for a trailing LF .
*/
2017-01-11 11:14:52 +03:00
void __sclp_print_early ( const char * str , unsigned int len )
2015-11-24 18:28:55 +03:00
{
2017-01-27 17:54:57 +03:00
if ( sclp_init_state ! = sclp_init_state_uninitialized )
return ;
2015-11-24 18:28:55 +03:00
if ( _sclp_setup ( 0 ) ! = 0 )
return ;
if ( have_linemode )
2017-01-11 11:14:52 +03:00
_sclp_print_lm ( str , len ) ;
2015-11-24 18:28:55 +03:00
if ( have_vt220 )
2017-01-11 11:14:52 +03:00
_sclp_print_vt220 ( str , len ) ;
2015-11-24 18:28:55 +03:00
_sclp_setup ( 1 ) ;
2015-07-08 11:20:04 +03:00
}
2017-01-11 11:14:52 +03:00
void _sclp_print_early ( const char * str )
{
__sclp_print_early ( str , strlen ( str ) ) ;
}