2007-04-27 16:01:56 +02:00
/*
* Copyright IBM Corp . 2007
* Author ( s ) : Heiko Carstens < heiko . carstens @ de . ibm . com >
*/
2008-12-25 13:39:48 +01:00
# define KMSG_COMPONENT "sclp_config"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2007-04-27 16:01:56 +02:00
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/cpu.h>
2011-12-21 14:29:42 -08:00
# include <linux/device.h>
2007-04-27 16:01:56 +02:00
# include <linux/workqueue.h>
2015-11-26 19:13:01 +01:00
# include <linux/slab.h>
# include <linux/sysfs.h>
2008-04-30 13:38:37 +02:00
# include <asm/smp.h>
2007-04-27 16:01:56 +02:00
2008-12-25 13:39:48 +01:00
# include "sclp.h"
2007-04-27 16:01:56 +02:00
struct conf_mgm_data {
u8 reserved ;
u8 ev_qualifier ;
} __attribute__ ( ( packed ) ) ;
2015-11-26 19:13:01 +01:00
# define OFB_DATA_MAX 64
struct sclp_ofb_evbuf {
struct evbuf_header header ;
struct conf_mgm_data cm_data ;
char ev_data [ OFB_DATA_MAX ] ;
} __packed ;
struct sclp_ofb_sccb {
struct sccb_header header ;
struct sclp_ofb_evbuf ofb_evbuf ;
} __packed ;
2008-04-30 13:38:37 +02:00
# define EV_QUAL_CPU_CHANGE 1
2007-04-27 16:01:56 +02:00
# define EV_QUAL_CAP_CHANGE 3
2015-11-26 19:13:01 +01:00
# define EV_QUAL_OPEN4BUSINESS 5
2007-04-27 16:01:56 +02:00
static struct work_struct sclp_cpu_capability_work ;
2008-04-30 13:38:37 +02:00
static struct work_struct sclp_cpu_change_work ;
2007-04-27 16:01:56 +02:00
static void sclp_cpu_capability_notify ( struct work_struct * work )
{
int cpu ;
2011-12-21 14:29:42 -08:00
struct device * dev ;
2007-04-27 16:01:56 +02:00
2016-04-14 12:35:22 +02:00
s390_update_cpu_mhz ( ) ;
2013-07-26 11:03:16 +02:00
pr_info ( " CPU capability may have changed \n " ) ;
2008-01-25 21:08:02 +01:00
get_online_cpus ( ) ;
2007-04-27 16:01:56 +02:00
for_each_online_cpu ( cpu ) {
2011-12-21 14:29:42 -08:00
dev = get_cpu_device ( cpu ) ;
kobject_uevent ( & dev - > kobj , KOBJ_CHANGE ) ;
2007-04-27 16:01:56 +02:00
}
2008-01-25 21:08:02 +01:00
put_online_cpus ( ) ;
2007-04-27 16:01:56 +02:00
}
2008-07-14 09:57:28 +02:00
static void __ref sclp_cpu_change_notify ( struct work_struct * work )
{
2008-08-01 16:39:23 +02:00
smp_rescan_cpus ( ) ;
2008-04-30 13:38:37 +02:00
}
2007-04-27 16:01:56 +02:00
static void sclp_conf_receiver_fn ( struct evbuf_header * evbuf )
{
struct conf_mgm_data * cdata ;
cdata = ( struct conf_mgm_data * ) ( evbuf + 1 ) ;
2008-04-30 13:38:37 +02:00
switch ( cdata - > ev_qualifier ) {
case EV_QUAL_CPU_CHANGE :
schedule_work ( & sclp_cpu_change_work ) ;
break ;
case EV_QUAL_CAP_CHANGE :
2007-04-27 16:01:56 +02:00
schedule_work ( & sclp_cpu_capability_work ) ;
2008-04-30 13:38:37 +02:00
break ;
}
2007-04-27 16:01:56 +02:00
}
static struct sclp_register sclp_conf_register =
{
2015-11-26 19:13:01 +01:00
# ifdef CONFIG_SCLP_OFB
. send_mask = EVTYP_CONFMGMDATA_MASK ,
# endif
2007-04-27 16:01:56 +02:00
. receive_mask = EVTYP_CONFMGMDATA_MASK ,
. receiver_fn = sclp_conf_receiver_fn ,
} ;
2015-11-26 19:13:01 +01:00
# ifdef CONFIG_SCLP_OFB
static int sclp_ofb_send_req ( char * ev_data , size_t len )
{
static DEFINE_MUTEX ( send_mutex ) ;
struct sclp_ofb_sccb * sccb ;
int rc , response ;
if ( len > OFB_DATA_MAX )
return - EINVAL ;
sccb = ( struct sclp_ofb_sccb * ) get_zeroed_page ( GFP_KERNEL | GFP_DMA ) ;
if ( ! sccb )
return - ENOMEM ;
/* Setup SCCB for Control-Program Identification */
sccb - > header . length = sizeof ( struct sclp_ofb_sccb ) ;
sccb - > ofb_evbuf . header . length = sizeof ( struct sclp_ofb_evbuf ) ;
sccb - > ofb_evbuf . header . type = EVTYP_CONFMGMDATA ;
sccb - > ofb_evbuf . cm_data . ev_qualifier = EV_QUAL_OPEN4BUSINESS ;
memcpy ( sccb - > ofb_evbuf . ev_data , ev_data , len ) ;
if ( ! ( sclp_conf_register . sclp_receive_mask & EVTYP_CONFMGMDATA_MASK ) )
pr_warn ( " SCLP receiver did not register to receive "
" Configuration Management Data Events. \n " ) ;
mutex_lock ( & send_mutex ) ;
rc = sclp_sync_request ( SCLP_CMDW_WRITE_EVENT_DATA , sccb ) ;
mutex_unlock ( & send_mutex ) ;
if ( rc )
goto out ;
response = sccb - > header . response_code ;
if ( response ! = 0x0020 ) {
pr_err ( " Open for Business request failed with response code "
" 0x%04x \n " , response ) ;
rc = - EIO ;
}
out :
free_page ( ( unsigned long ) sccb ) ;
return rc ;
}
static ssize_t sysfs_ofb_data_write ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
int rc ;
rc = sclp_ofb_send_req ( buf , count ) ;
return rc ? : count ;
}
static struct bin_attribute ofb_bin_attr = {
. attr = {
. name = " event_data " ,
. mode = S_IWUSR ,
} ,
. write = sysfs_ofb_data_write ,
} ;
# endif
static int __init sclp_ofb_setup ( void )
{
# ifdef CONFIG_SCLP_OFB
struct kset * ofb_kset ;
int rc ;
ofb_kset = kset_create_and_add ( " ofb " , NULL , firmware_kobj ) ;
if ( ! ofb_kset )
return - ENOMEM ;
rc = sysfs_create_bin_file ( & ofb_kset - > kobj , & ofb_bin_attr ) ;
if ( rc ) {
kset_unregister ( ofb_kset ) ;
return rc ;
}
# endif
return 0 ;
}
2007-04-27 16:01:56 +02:00
static int __init sclp_conf_init ( void )
{
2015-11-26 19:13:01 +01:00
int rc ;
2007-04-27 16:01:56 +02:00
INIT_WORK ( & sclp_cpu_capability_work , sclp_cpu_capability_notify ) ;
2008-04-30 13:38:37 +02:00
INIT_WORK ( & sclp_cpu_change_work , sclp_cpu_change_notify ) ;
2015-11-26 19:13:01 +01:00
rc = sclp_register ( & sclp_conf_register ) ;
if ( rc )
return rc ;
return sclp_ofb_setup ( ) ;
2007-04-27 16:01:56 +02:00
}
__initcall ( sclp_conf_init ) ;