2013-11-13 13:38:27 +04:00
/*
* SCLP early driver
*
* Copyright IBM Corp . 2013
*/
# define KMSG_COMPONENT "sclp_early"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2013-11-13 13:38:27 +04:00
# include <asm/ctl_reg.h>
2013-11-13 13:38:27 +04:00
# include <asm/sclp.h>
# include <asm/ipl.h>
# include "sclp_sdias.h"
# include "sclp.h"
2013-11-13 13:38:27 +04:00
# define SCLP_CMDW_READ_SCP_INFO 0x00020001
# define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
struct read_info_sccb {
struct sccb_header header ; /* 0-7 */
u16 rnmax ; /* 8-9 */
u8 rnsize ; /* 10 */
2014-03-10 17:50:16 +04:00
u8 _reserved0 [ 16 - 11 ] ; /* 11-15 */
u16 ncpurl ; /* 16-17 */
2013-12-30 15:54:14 +04:00
u16 cpuoff ; /* 18-19 */
u8 _reserved7 [ 24 - 20 ] ; /* 20-23 */
2013-11-13 13:38:27 +04:00
u8 loadparm [ 8 ] ; /* 24-31 */
u8 _reserved1 [ 48 - 32 ] ; /* 32-47 */
u64 facilities ; /* 48-55 */
2014-03-15 21:16:26 +04:00
u8 _reserved2a [ 76 - 56 ] ; /* 56-75 */
u32 ibc ; /* 76-79 */
u8 _reserved2b [ 84 - 80 ] ; /* 80-83 */
2013-11-13 13:38:27 +04:00
u8 fac84 ; /* 84 */
u8 fac85 ; /* 85 */
u8 _reserved3 [ 91 - 86 ] ; /* 86-90 */
u8 flags ; /* 91 */
u8 _reserved4 [ 100 - 92 ] ; /* 92-99 */
u32 rnsize2 ; /* 100-103 */
u64 rnmax2 ; /* 104-111 */
2014-03-10 17:50:16 +04:00
u8 _reserved5 [ 120 - 112 ] ; /* 112-119 */
u16 hcpua ; /* 120-121 */
u8 _reserved6 [ 4096 - 122 ] ; /* 122-4095 */
2013-11-13 13:38:27 +04:00
} __packed __aligned ( PAGE_SIZE ) ;
2013-12-05 22:03:50 +04:00
static char sccb_early [ PAGE_SIZE ] __aligned ( PAGE_SIZE ) __initdata ;
2013-12-05 22:28:39 +04:00
static unsigned int sclp_con_has_vt220 __initdata ;
static unsigned int sclp_con_has_linemode __initdata ;
2013-11-13 13:38:27 +04:00
static unsigned long sclp_hsa_size ;
2014-03-10 17:50:16 +04:00
static unsigned int sclp_max_cpu ;
2013-12-05 21:46:51 +04:00
static struct sclp_ipl_info sclp_ipl_info ;
2013-12-30 15:54:14 +04:00
static unsigned char sclp_siif ;
2014-03-15 21:16:26 +04:00
static u32 sclp_ibc ;
2013-11-13 13:38:27 +04:00
2013-11-13 13:38:27 +04:00
u64 sclp_facilities ;
u8 sclp_fac84 ;
unsigned long long sclp_rzm ;
unsigned long long sclp_rnmax ;
static int __init sclp_cmd_sync_early ( sclp_cmdw_t cmd , void * sccb )
{
int rc ;
__ctl_set_bit ( 0 , 9 ) ;
rc = sclp_service_call ( cmd , sccb ) ;
if ( rc )
goto out ;
__load_psw_mask ( PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA |
PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT ) ;
local_irq_disable ( ) ;
out :
/* Contents of the sccb might have changed. */
barrier ( ) ;
__ctl_clear_bit ( 0 , 9 ) ;
return rc ;
}
2013-12-05 22:03:50 +04:00
static int __init sclp_read_info_early ( struct read_info_sccb * sccb )
2013-11-13 13:38:27 +04:00
{
2013-12-05 21:46:51 +04:00
int rc , i ;
2013-11-13 13:38:27 +04:00
sclp_cmdw_t commands [ ] = { SCLP_CMDW_READ_SCP_INFO_FORCED ,
SCLP_CMDW_READ_SCP_INFO } ;
for ( i = 0 ; i < ARRAY_SIZE ( commands ) ; i + + ) {
do {
memset ( sccb , 0 , sizeof ( * sccb ) ) ;
sccb - > header . length = sizeof ( * sccb ) ;
sccb - > header . function_code = 0x80 ;
sccb - > header . control_mask [ 2 ] = 0x80 ;
rc = sclp_cmd_sync_early ( commands [ i ] , sccb ) ;
} while ( rc = = - EBUSY ) ;
if ( rc )
break ;
2013-12-05 21:46:51 +04:00
if ( sccb - > header . response_code = = 0x10 )
return 0 ;
2013-11-13 13:38:27 +04:00
if ( sccb - > header . response_code ! = 0x1f0 )
break ;
}
2013-12-05 21:46:51 +04:00
return - EIO ;
2013-11-13 13:38:27 +04:00
}
2013-12-05 22:13:36 +04:00
static void __init sclp_facilities_detect ( struct read_info_sccb * sccb )
2013-11-13 13:38:27 +04:00
{
2013-12-30 15:54:14 +04:00
struct sclp_cpu_entry * cpue ;
u16 boot_cpu_address , cpu ;
2013-12-05 22:03:50 +04:00
if ( sclp_read_info_early ( sccb ) )
2013-11-13 13:38:27 +04:00
return ;
sclp_facilities = sccb - > facilities ;
sclp_fac84 = sccb - > fac84 ;
if ( sccb - > fac85 & 0x02 )
S390_lowcore . machine_flags | = MACHINE_FLAG_ESOP ;
sclp_rnmax = sccb - > rnmax ? sccb - > rnmax : sccb - > rnmax2 ;
sclp_rzm = sccb - > rnsize ? sccb - > rnsize : sccb - > rnsize2 ;
sclp_rzm < < = 20 ;
2014-03-15 21:16:26 +04:00
sclp_ibc = sccb - > ibc ;
2013-12-05 21:46:51 +04:00
2014-03-10 17:50:16 +04:00
if ( ! sccb - > hcpua ) {
if ( MACHINE_IS_VM )
sclp_max_cpu = 64 ;
else
sclp_max_cpu = sccb - > ncpurl ;
} else {
sclp_max_cpu = sccb - > hcpua + 1 ;
}
2013-12-30 15:54:14 +04:00
boot_cpu_address = stap ( ) ;
cpue = ( void * ) sccb + sccb - > cpuoff ;
for ( cpu = 0 ; cpu < sccb - > ncpurl ; cpue + + , cpu + + ) {
if ( boot_cpu_address ! = cpue - > address )
continue ;
sclp_siif = cpue - > siif ;
break ;
}
2013-12-05 21:46:51 +04:00
/* Save IPL information */
sclp_ipl_info . is_valid = 1 ;
if ( sccb - > flags & 0x2 )
sclp_ipl_info . has_dump = 1 ;
memcpy ( & sclp_ipl_info . loadparm , & sccb - > loadparm , LOADPARM_LEN ) ;
2013-11-13 13:38:27 +04:00
}
bool __init sclp_has_linemode ( void )
{
2013-12-05 22:28:39 +04:00
return ! ! sclp_con_has_linemode ;
2013-11-13 13:38:27 +04:00
}
bool __init sclp_has_vt220 ( void )
{
2013-12-05 22:28:39 +04:00
return ! ! sclp_con_has_vt220 ;
2013-11-13 13:38:27 +04:00
}
unsigned long long sclp_get_rnmax ( void )
{
return sclp_rnmax ;
}
unsigned long long sclp_get_rzm ( void )
{
return sclp_rzm ;
}
2014-03-10 17:50:16 +04:00
unsigned int sclp_get_max_cpu ( void )
{
return sclp_max_cpu ;
}
2013-12-30 15:54:14 +04:00
int sclp_has_siif ( void )
{
return sclp_siif ;
}
EXPORT_SYMBOL ( sclp_has_siif ) ;
2014-03-15 21:16:26 +04:00
unsigned int sclp_get_ibc ( void )
{
return sclp_ibc ;
}
EXPORT_SYMBOL ( sclp_get_ibc ) ;
2013-11-13 13:38:27 +04:00
/*
* This function will be called after sclp_facilities_detect ( ) , which gets
2013-12-05 21:46:51 +04:00
* called from early . c code . The sclp_facilities_detect ( ) function retrieves
* and saves the IPL information .
2013-11-13 13:38:27 +04:00
*/
void __init sclp_get_ipl_info ( struct sclp_ipl_info * info )
{
2013-12-05 21:46:51 +04:00
* info = sclp_ipl_info ;
2013-11-13 13:38:27 +04:00
}
2013-11-13 13:38:27 +04:00
static int __init sclp_cmd_early ( sclp_cmdw_t cmd , void * sccb )
{
int rc ;
do {
rc = sclp_cmd_sync_early ( cmd , sccb ) ;
} while ( rc = = - EBUSY ) ;
if ( rc )
return - EIO ;
if ( ( ( struct sccb_header * ) sccb ) - > response_code ! = 0x0020 )
return - EIO ;
return 0 ;
}
static void __init sccb_init_eq_size ( struct sdias_sccb * sccb )
{
memset ( sccb , 0 , sizeof ( * sccb ) ) ;
sccb - > hdr . length = sizeof ( * sccb ) ;
sccb - > evbuf . hdr . length = sizeof ( struct sdias_evbuf ) ;
sccb - > evbuf . hdr . type = EVTYP_SDIAS ;
sccb - > evbuf . event_qual = SDIAS_EQ_SIZE ;
sccb - > evbuf . data_id = SDIAS_DI_FCP_DUMP ;
sccb - > evbuf . event_id = 4712 ;
sccb - > evbuf . dbs = 1 ;
}
2013-12-05 22:13:36 +04:00
static int __init sclp_set_event_mask ( struct init_sccb * sccb ,
unsigned long receive_mask ,
2013-11-13 13:38:27 +04:00
unsigned long send_mask )
{
memset ( sccb , 0 , sizeof ( * sccb ) ) ;
sccb - > header . length = sizeof ( * sccb ) ;
sccb - > mask_length = sizeof ( sccb_mask_t ) ;
sccb - > receive_mask = receive_mask ;
sccb - > send_mask = send_mask ;
return sclp_cmd_early ( SCLP_CMDW_WRITE_EVENT_MASK , sccb ) ;
}
2013-12-05 22:13:36 +04:00
static long __init sclp_hsa_size_init ( struct sdias_sccb * sccb )
2013-11-13 13:38:27 +04:00
{
sccb_init_eq_size ( sccb ) ;
if ( sclp_cmd_early ( SCLP_CMDW_WRITE_EVENT_DATA , sccb ) )
return - EIO ;
2014-02-24 17:30:00 +04:00
if ( sccb - > evbuf . blk_cnt = = 0 )
return 0 ;
return ( sccb - > evbuf . blk_cnt - 1 ) * PAGE_SIZE ;
2013-11-13 13:38:27 +04:00
}
2013-12-05 22:13:36 +04:00
static long __init sclp_hsa_copy_wait ( struct sccb_header * sccb )
2013-11-13 13:38:27 +04:00
{
memset ( sccb , 0 , PAGE_SIZE ) ;
sccb - > length = PAGE_SIZE ;
if ( sclp_cmd_early ( SCLP_CMDW_READ_EVENT_DATA , sccb ) )
return - EIO ;
2014-02-24 17:30:00 +04:00
if ( ( ( struct sdias_sccb * ) sccb ) - > evbuf . blk_cnt = = 0 )
return 0 ;
2013-11-13 13:38:27 +04:00
return ( ( ( struct sdias_sccb * ) sccb ) - > evbuf . blk_cnt - 1 ) * PAGE_SIZE ;
}
unsigned long sclp_get_hsa_size ( void )
{
return sclp_hsa_size ;
}
2013-12-05 22:13:36 +04:00
static void __init sclp_hsa_size_detect ( void * sccb )
2013-11-13 13:38:27 +04:00
{
long size ;
/* First try synchronous interface (LPAR) */
2013-12-05 22:13:36 +04:00
if ( sclp_set_event_mask ( sccb , 0 , 0x40000010 ) )
2013-11-13 13:38:27 +04:00
return ;
2013-12-05 22:13:36 +04:00
size = sclp_hsa_size_init ( sccb ) ;
2013-11-13 13:38:27 +04:00
if ( size < 0 )
return ;
if ( size ! = 0 )
goto out ;
/* Then try asynchronous interface (z/VM) */
2013-12-05 22:13:36 +04:00
if ( sclp_set_event_mask ( sccb , 0x00000010 , 0x40000010 ) )
2013-11-13 13:38:27 +04:00
return ;
2013-12-05 22:13:36 +04:00
size = sclp_hsa_size_init ( sccb ) ;
2013-11-13 13:38:27 +04:00
if ( size < 0 )
return ;
2013-12-05 22:13:36 +04:00
size = sclp_hsa_copy_wait ( sccb ) ;
2013-11-13 13:38:27 +04:00
if ( size < 0 )
return ;
out :
sclp_hsa_size = size ;
}
2013-11-13 13:38:27 +04:00
2013-12-05 22:28:39 +04:00
static unsigned int __init sclp_con_check_linemode ( struct init_sccb * sccb )
{
if ( ! ( sccb - > sclp_send_mask & ( EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK ) ) )
return 0 ;
if ( ! ( sccb - > sclp_receive_mask & ( EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK ) ) )
return 0 ;
return 1 ;
}
static void __init sclp_console_detect ( struct init_sccb * sccb )
{
if ( sccb - > header . response_code ! = 0x20 )
return ;
if ( sccb - > sclp_send_mask & EVTYP_VT220MSG_MASK )
sclp_con_has_vt220 = 1 ;
if ( sclp_con_check_linemode ( sccb ) )
sclp_con_has_linemode = 1 ;
}
2013-11-13 13:38:27 +04:00
void __init sclp_early_detect ( void )
{
2013-12-05 22:13:36 +04:00
void * sccb = & sccb_early ;
sclp_facilities_detect ( sccb ) ;
sclp_hsa_size_detect ( sccb ) ;
2013-12-05 22:28:39 +04:00
/* Turn off SCLP event notifications. Also save remote masks in the
* sccb . These are sufficient to detect sclp console capabilities .
*/
2013-12-05 22:13:36 +04:00
sclp_set_event_mask ( sccb , 0 , 0 ) ;
2013-12-05 22:28:39 +04:00
sclp_console_detect ( sccb ) ;
2013-11-13 13:38:27 +04:00
}