2013-11-13 10:38:27 +01:00
/*
* SCLP early driver
*
* Copyright IBM Corp . 2013
*/
# define KMSG_COMPONENT "sclp_early"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2013-11-13 10:38:27 +01:00
# include <asm/ctl_reg.h>
2013-11-13 10:38:27 +01:00
# include <asm/sclp.h>
# include <asm/ipl.h>
# include "sclp_sdias.h"
# include "sclp.h"
2013-11-13 10:38:27 +01: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 */
2015-01-14 17:52:10 +01:00
u8 _pad_11 [ 16 - 11 ] ; /* 11-15 */
2014-03-10 14:50:16 +01:00
u16 ncpurl ; /* 16-17 */
2013-12-30 12:54:14 +01:00
u16 cpuoff ; /* 18-19 */
2015-01-14 17:52:10 +01:00
u8 _pad_20 [ 24 - 20 ] ; /* 20-23 */
2013-11-13 10:38:27 +01:00
u8 loadparm [ 8 ] ; /* 24-31 */
2015-01-14 17:52:10 +01:00
u8 _pad_32 [ 42 - 32 ] ; /* 32-41 */
u8 fac42 ; /* 42 */
u8 fac43 ; /* 43 */
u8 _pad_44 [ 48 - 44 ] ; /* 44-47 */
2013-11-13 10:38:27 +01:00
u64 facilities ; /* 48-55 */
2015-01-14 17:52:10 +01:00
u8 _pad_56 [ 66 - 56 ] ; /* 56-65 */
u8 fac66 ; /* 66 */
u8 _pad_67 [ 76 - 67 ] ; /* 67-83 */
2014-03-15 18:16:26 +01:00
u32 ibc ; /* 76-79 */
2015-01-14 17:52:10 +01:00
u8 _pad80 [ 84 - 80 ] ; /* 80-83 */
2013-11-13 10:38:27 +01:00
u8 fac84 ; /* 84 */
u8 fac85 ; /* 85 */
2015-01-14 17:52:10 +01:00
u8 _pad_86 [ 91 - 86 ] ; /* 86-90 */
2013-11-13 10:38:27 +01:00
u8 flags ; /* 91 */
2015-01-14 17:52:10 +01:00
u8 _pad_92 [ 100 - 92 ] ; /* 92-99 */
2013-11-13 10:38:27 +01:00
u32 rnsize2 ; /* 100-103 */
u64 rnmax2 ; /* 104-111 */
2015-01-14 17:52:10 +01:00
u8 _pad_112 [ 120 - 112 ] ; /* 112-119 */
2014-03-10 14:50:16 +01:00
u16 hcpua ; /* 120-121 */
2015-01-14 17:52:10 +01:00
u8 _pad_122 [ 4096 - 122 ] ; /* 122-4095 */
2013-11-13 10:38:27 +01:00
} __packed __aligned ( PAGE_SIZE ) ;
2013-12-05 19:03:50 +01:00
static char sccb_early [ PAGE_SIZE ] __aligned ( PAGE_SIZE ) __initdata ;
2013-12-05 19:28:39 +01:00
static unsigned int sclp_con_has_vt220 __initdata ;
static unsigned int sclp_con_has_linemode __initdata ;
2013-11-13 10:38:27 +01:00
static unsigned long sclp_hsa_size ;
2014-03-10 14:50:16 +01:00
static unsigned int sclp_max_cpu ;
2013-12-05 18:46:51 +01:00
static struct sclp_ipl_info sclp_ipl_info ;
2013-12-30 12:54:14 +01:00
static unsigned char sclp_siif ;
2014-10-14 12:11:02 +02:00
static unsigned char sclp_sigpif ;
2014-03-15 18:16:26 +01:00
static u32 sclp_ibc ;
2015-01-14 17:52:10 +01:00
static unsigned int sclp_mtid ;
static unsigned int sclp_mtid_cp ;
static unsigned int sclp_mtid_max ;
static unsigned int sclp_mtid_prev ;
2013-11-13 10:38:27 +01:00
2013-11-13 10:38:27 +01: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 19:03:50 +01:00
static int __init sclp_read_info_early ( struct read_info_sccb * sccb )
2013-11-13 10:38:27 +01:00
{
2013-12-05 18:46:51 +01:00
int rc , i ;
2013-11-13 10:38:27 +01: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 18:46:51 +01:00
if ( sccb - > header . response_code = = 0x10 )
return 0 ;
2013-11-13 10:38:27 +01:00
if ( sccb - > header . response_code ! = 0x1f0 )
break ;
}
2013-12-05 18:46:51 +01:00
return - EIO ;
2013-11-13 10:38:27 +01:00
}
2013-12-05 19:13:36 +01:00
static void __init sclp_facilities_detect ( struct read_info_sccb * sccb )
2013-11-13 10:38:27 +01:00
{
2013-12-30 12:54:14 +01:00
struct sclp_cpu_entry * cpue ;
u16 boot_cpu_address , cpu ;
2013-12-05 19:03:50 +01:00
if ( sclp_read_info_early ( sccb ) )
2013-11-13 10:38:27 +01: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 18:16:26 +01:00
sclp_ibc = sccb - > ibc ;
2013-12-05 18:46:51 +01:00
2014-03-10 14:50:16 +01: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 12:54:14 +01:00
boot_cpu_address = stap ( ) ;
cpue = ( void * ) sccb + sccb - > cpuoff ;
for ( cpu = 0 ; cpu < sccb - > ncpurl ; cpue + + , cpu + + ) {
2015-01-14 17:52:10 +01:00
if ( boot_cpu_address ! = cpue - > core_id )
2013-12-30 12:54:14 +01:00
continue ;
sclp_siif = cpue - > siif ;
2014-10-14 12:11:02 +02:00
sclp_sigpif = cpue - > sigpif ;
2013-12-30 12:54:14 +01:00
break ;
}
2013-12-05 18:46:51 +01: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 ) ;
2015-01-14 17:52:10 +01:00
sclp_mtid = ( sccb - > fac42 & 0x80 ) ? ( sccb - > fac42 & 31 ) : 0 ;
sclp_mtid_cp = ( sccb - > fac42 & 0x80 ) ? ( sccb - > fac43 & 31 ) : 0 ;
sclp_mtid_max = max ( sclp_mtid , sclp_mtid_cp ) ;
sclp_mtid_prev = ( sccb - > fac42 & 0x80 ) ? ( sccb - > fac66 & 31 ) : 0 ;
2013-11-13 10:38:27 +01:00
}
bool __init sclp_has_linemode ( void )
{
2013-12-05 19:28:39 +01:00
return ! ! sclp_con_has_linemode ;
2013-11-13 10:38:27 +01:00
}
bool __init sclp_has_vt220 ( void )
{
2013-12-05 19:28:39 +01:00
return ! ! sclp_con_has_vt220 ;
2013-11-13 10:38:27 +01:00
}
unsigned long long sclp_get_rnmax ( void )
{
return sclp_rnmax ;
}
unsigned long long sclp_get_rzm ( void )
{
return sclp_rzm ;
}
2014-03-10 14:50:16 +01:00
unsigned int sclp_get_max_cpu ( void )
{
return sclp_max_cpu ;
}
2013-12-30 12:54:14 +01:00
int sclp_has_siif ( void )
{
return sclp_siif ;
}
EXPORT_SYMBOL ( sclp_has_siif ) ;
2014-10-14 12:11:02 +02:00
int sclp_has_sigpif ( void )
{
return sclp_sigpif ;
}
EXPORT_SYMBOL ( sclp_has_sigpif ) ;
2014-03-15 18:16:26 +01:00
unsigned int sclp_get_ibc ( void )
{
return sclp_ibc ;
}
EXPORT_SYMBOL ( sclp_get_ibc ) ;
2015-01-14 17:52:10 +01:00
unsigned int sclp_get_mtid ( u8 cpu_type )
{
return cpu_type ? sclp_mtid : sclp_mtid_cp ;
}
unsigned int sclp_get_mtid_max ( void )
{
return sclp_mtid_max ;
}
unsigned int sclp_get_mtid_prev ( void )
{
return sclp_mtid_prev ;
}
2013-11-13 10:38:27 +01:00
/*
* This function will be called after sclp_facilities_detect ( ) , which gets
2013-12-05 18:46:51 +01:00
* called from early . c code . The sclp_facilities_detect ( ) function retrieves
* and saves the IPL information .
2013-11-13 10:38:27 +01:00
*/
void __init sclp_get_ipl_info ( struct sclp_ipl_info * info )
{
2013-12-05 18:46:51 +01:00
* info = sclp_ipl_info ;
2013-11-13 10:38:27 +01:00
}
2013-11-13 10:38:27 +01: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 19:13:36 +01:00
static int __init sclp_set_event_mask ( struct init_sccb * sccb ,
unsigned long receive_mask ,
2013-11-13 10:38:27 +01: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 19:13:36 +01:00
static long __init sclp_hsa_size_init ( struct sdias_sccb * sccb )
2013-11-13 10:38:27 +01:00
{
sccb_init_eq_size ( sccb ) ;
if ( sclp_cmd_early ( SCLP_CMDW_WRITE_EVENT_DATA , sccb ) )
return - EIO ;
2014-02-24 14:30:00 +01:00
if ( sccb - > evbuf . blk_cnt = = 0 )
return 0 ;
return ( sccb - > evbuf . blk_cnt - 1 ) * PAGE_SIZE ;
2013-11-13 10:38:27 +01:00
}
2013-12-05 19:13:36 +01:00
static long __init sclp_hsa_copy_wait ( struct sccb_header * sccb )
2013-11-13 10:38:27 +01: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 14:30:00 +01:00
if ( ( ( struct sdias_sccb * ) sccb ) - > evbuf . blk_cnt = = 0 )
return 0 ;
2013-11-13 10:38:27 +01: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 19:13:36 +01:00
static void __init sclp_hsa_size_detect ( void * sccb )
2013-11-13 10:38:27 +01:00
{
long size ;
/* First try synchronous interface (LPAR) */
2013-12-05 19:13:36 +01:00
if ( sclp_set_event_mask ( sccb , 0 , 0x40000010 ) )
2013-11-13 10:38:27 +01:00
return ;
2013-12-05 19:13:36 +01:00
size = sclp_hsa_size_init ( sccb ) ;
2013-11-13 10:38:27 +01:00
if ( size < 0 )
return ;
if ( size ! = 0 )
goto out ;
/* Then try asynchronous interface (z/VM) */
2013-12-05 19:13:36 +01:00
if ( sclp_set_event_mask ( sccb , 0x00000010 , 0x40000010 ) )
2013-11-13 10:38:27 +01:00
return ;
2013-12-05 19:13:36 +01:00
size = sclp_hsa_size_init ( sccb ) ;
2013-11-13 10:38:27 +01:00
if ( size < 0 )
return ;
2013-12-05 19:13:36 +01:00
size = sclp_hsa_copy_wait ( sccb ) ;
2013-11-13 10:38:27 +01:00
if ( size < 0 )
return ;
out :
sclp_hsa_size = size ;
}
2013-11-13 10:38:27 +01:00
2013-12-05 19:28:39 +01:00
static unsigned int __init sclp_con_check_linemode ( struct init_sccb * sccb )
{
2014-09-09 12:53:12 +02:00
if ( ! ( sccb - > sclp_send_mask & EVTYP_OPCMD_MASK ) )
2013-12-05 19:28:39 +01:00
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 10:38:27 +01:00
void __init sclp_early_detect ( void )
{
2013-12-05 19:13:36 +01:00
void * sccb = & sccb_early ;
sclp_facilities_detect ( sccb ) ;
sclp_hsa_size_detect ( sccb ) ;
2013-12-05 19:28:39 +01:00
/* Turn off SCLP event notifications. Also save remote masks in the
* sccb . These are sufficient to detect sclp console capabilities .
*/
2013-12-05 19:13:36 +01:00
sclp_set_event_mask ( sccb , 0 , 0 ) ;
2013-12-05 19:28:39 +01:00
sclp_console_detect ( sccb ) ;
2013-11-13 10:38:27 +01:00
}