mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.1 KiB
180 lines
4.1 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright IBM Corp. 2007 |
|
* Author(s): Heiko Carstens <[email protected]> |
|
*/ |
|
|
|
#define KMSG_COMPONENT "sclp_config" |
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
|
|
|
#include <linux/init.h> |
|
#include <linux/errno.h> |
|
#include <linux/cpu.h> |
|
#include <linux/device.h> |
|
#include <linux/workqueue.h> |
|
#include <linux/slab.h> |
|
#include <linux/sysfs.h> |
|
#include <asm/smp.h> |
|
|
|
#include "sclp.h" |
|
|
|
struct conf_mgm_data { |
|
u8 reserved; |
|
u8 ev_qualifier; |
|
} __attribute__((packed)); |
|
|
|
#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; |
|
|
|
#define EV_QUAL_CPU_CHANGE 1 |
|
#define EV_QUAL_CAP_CHANGE 3 |
|
#define EV_QUAL_OPEN4BUSINESS 5 |
|
|
|
static struct work_struct sclp_cpu_capability_work; |
|
static struct work_struct sclp_cpu_change_work; |
|
|
|
static void sclp_cpu_capability_notify(struct work_struct *work) |
|
{ |
|
int cpu; |
|
struct device *dev; |
|
|
|
s390_update_cpu_mhz(); |
|
pr_info("CPU capability may have changed\n"); |
|
cpus_read_lock(); |
|
for_each_online_cpu(cpu) { |
|
dev = get_cpu_device(cpu); |
|
kobject_uevent(&dev->kobj, KOBJ_CHANGE); |
|
} |
|
cpus_read_unlock(); |
|
} |
|
|
|
static void __ref sclp_cpu_change_notify(struct work_struct *work) |
|
{ |
|
lock_device_hotplug(); |
|
smp_rescan_cpus(); |
|
unlock_device_hotplug(); |
|
} |
|
|
|
static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) |
|
{ |
|
struct conf_mgm_data *cdata; |
|
|
|
cdata = (struct conf_mgm_data *)(evbuf + 1); |
|
switch (cdata->ev_qualifier) { |
|
case EV_QUAL_CPU_CHANGE: |
|
schedule_work(&sclp_cpu_change_work); |
|
break; |
|
case EV_QUAL_CAP_CHANGE: |
|
schedule_work(&sclp_cpu_capability_work); |
|
break; |
|
} |
|
} |
|
|
|
static struct sclp_register sclp_conf_register = |
|
{ |
|
#ifdef CONFIG_SCLP_OFB |
|
.send_mask = EVTYP_CONFMGMDATA_MASK, |
|
#endif |
|
.receive_mask = EVTYP_CONFMGMDATA_MASK, |
|
.receiver_fn = sclp_conf_receiver_fn, |
|
}; |
|
|
|
#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 const 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; |
|
} |
|
|
|
static int __init sclp_conf_init(void) |
|
{ |
|
int rc; |
|
|
|
INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); |
|
INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); |
|
rc = sclp_register(&sclp_conf_register); |
|
if (rc) |
|
return rc; |
|
return sclp_ofb_setup(); |
|
} |
|
|
|
__initcall(sclp_conf_init);
|
|
|