18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2007 48c2ecf20Sopenharmony_ci * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sclp_config" 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/cpu.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 178c2ecf20Sopenharmony_ci#include <asm/smp.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "sclp.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct conf_mgm_data { 228c2ecf20Sopenharmony_ci u8 reserved; 238c2ecf20Sopenharmony_ci u8 ev_qualifier; 248c2ecf20Sopenharmony_ci} __attribute__((packed)); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define OFB_DATA_MAX 64 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct sclp_ofb_evbuf { 298c2ecf20Sopenharmony_ci struct evbuf_header header; 308c2ecf20Sopenharmony_ci struct conf_mgm_data cm_data; 318c2ecf20Sopenharmony_ci char ev_data[OFB_DATA_MAX]; 328c2ecf20Sopenharmony_ci} __packed; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct sclp_ofb_sccb { 358c2ecf20Sopenharmony_ci struct sccb_header header; 368c2ecf20Sopenharmony_ci struct sclp_ofb_evbuf ofb_evbuf; 378c2ecf20Sopenharmony_ci} __packed; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define EV_QUAL_CPU_CHANGE 1 408c2ecf20Sopenharmony_ci#define EV_QUAL_CAP_CHANGE 3 418c2ecf20Sopenharmony_ci#define EV_QUAL_OPEN4BUSINESS 5 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct work_struct sclp_cpu_capability_work; 448c2ecf20Sopenharmony_cistatic struct work_struct sclp_cpu_change_work; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void sclp_cpu_capability_notify(struct work_struct *work) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int cpu; 498c2ecf20Sopenharmony_ci struct device *dev; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci s390_update_cpu_mhz(); 528c2ecf20Sopenharmony_ci pr_info("CPU capability may have changed\n"); 538c2ecf20Sopenharmony_ci get_online_cpus(); 548c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 558c2ecf20Sopenharmony_ci dev = get_cpu_device(cpu); 568c2ecf20Sopenharmony_ci kobject_uevent(&dev->kobj, KOBJ_CHANGE); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci put_online_cpus(); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void __ref sclp_cpu_change_notify(struct work_struct *work) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci lock_device_hotplug(); 648c2ecf20Sopenharmony_ci smp_rescan_cpus(); 658c2ecf20Sopenharmony_ci unlock_device_hotplug(); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void sclp_conf_receiver_fn(struct evbuf_header *evbuf) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct conf_mgm_data *cdata; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci cdata = (struct conf_mgm_data *)(evbuf + 1); 738c2ecf20Sopenharmony_ci switch (cdata->ev_qualifier) { 748c2ecf20Sopenharmony_ci case EV_QUAL_CPU_CHANGE: 758c2ecf20Sopenharmony_ci schedule_work(&sclp_cpu_change_work); 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci case EV_QUAL_CAP_CHANGE: 788c2ecf20Sopenharmony_ci schedule_work(&sclp_cpu_capability_work); 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct sclp_register sclp_conf_register = 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci#ifdef CONFIG_SCLP_OFB 868c2ecf20Sopenharmony_ci .send_mask = EVTYP_CONFMGMDATA_MASK, 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci .receive_mask = EVTYP_CONFMGMDATA_MASK, 898c2ecf20Sopenharmony_ci .receiver_fn = sclp_conf_receiver_fn, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#ifdef CONFIG_SCLP_OFB 938c2ecf20Sopenharmony_cistatic int sclp_ofb_send_req(char *ev_data, size_t len) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci static DEFINE_MUTEX(send_mutex); 968c2ecf20Sopenharmony_ci struct sclp_ofb_sccb *sccb; 978c2ecf20Sopenharmony_ci int rc, response; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (len > OFB_DATA_MAX) 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci sccb = (struct sclp_ofb_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 1028c2ecf20Sopenharmony_ci if (!sccb) 1038c2ecf20Sopenharmony_ci return -ENOMEM; 1048c2ecf20Sopenharmony_ci /* Setup SCCB for Control-Program Identification */ 1058c2ecf20Sopenharmony_ci sccb->header.length = sizeof(struct sclp_ofb_sccb); 1068c2ecf20Sopenharmony_ci sccb->ofb_evbuf.header.length = sizeof(struct sclp_ofb_evbuf); 1078c2ecf20Sopenharmony_ci sccb->ofb_evbuf.header.type = EVTYP_CONFMGMDATA; 1088c2ecf20Sopenharmony_ci sccb->ofb_evbuf.cm_data.ev_qualifier = EV_QUAL_OPEN4BUSINESS; 1098c2ecf20Sopenharmony_ci memcpy(sccb->ofb_evbuf.ev_data, ev_data, len); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK)) 1128c2ecf20Sopenharmony_ci pr_warn("SCLP receiver did not register to receive " 1138c2ecf20Sopenharmony_ci "Configuration Management Data Events.\n"); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci mutex_lock(&send_mutex); 1168c2ecf20Sopenharmony_ci rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb); 1178c2ecf20Sopenharmony_ci mutex_unlock(&send_mutex); 1188c2ecf20Sopenharmony_ci if (rc) 1198c2ecf20Sopenharmony_ci goto out; 1208c2ecf20Sopenharmony_ci response = sccb->header.response_code; 1218c2ecf20Sopenharmony_ci if (response != 0x0020) { 1228c2ecf20Sopenharmony_ci pr_err("Open for Business request failed with response code " 1238c2ecf20Sopenharmony_ci "0x%04x\n", response); 1248c2ecf20Sopenharmony_ci rc = -EIO; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ciout: 1278c2ecf20Sopenharmony_ci free_page((unsigned long)sccb); 1288c2ecf20Sopenharmony_ci return rc; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic ssize_t sysfs_ofb_data_write(struct file *filp, struct kobject *kobj, 1328c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 1338c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int rc; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci rc = sclp_ofb_send_req(buf, count); 1388c2ecf20Sopenharmony_ci return rc ?: count; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct bin_attribute ofb_bin_attr = { 1428c2ecf20Sopenharmony_ci .attr = { 1438c2ecf20Sopenharmony_ci .name = "event_data", 1448c2ecf20Sopenharmony_ci .mode = S_IWUSR, 1458c2ecf20Sopenharmony_ci }, 1468c2ecf20Sopenharmony_ci .write = sysfs_ofb_data_write, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci#endif 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int __init sclp_ofb_setup(void) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci#ifdef CONFIG_SCLP_OFB 1538c2ecf20Sopenharmony_ci struct kset *ofb_kset; 1548c2ecf20Sopenharmony_ci int rc; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ofb_kset = kset_create_and_add("ofb", NULL, firmware_kobj); 1578c2ecf20Sopenharmony_ci if (!ofb_kset) 1588c2ecf20Sopenharmony_ci return -ENOMEM; 1598c2ecf20Sopenharmony_ci rc = sysfs_create_bin_file(&ofb_kset->kobj, &ofb_bin_attr); 1608c2ecf20Sopenharmony_ci if (rc) { 1618c2ecf20Sopenharmony_ci kset_unregister(ofb_kset); 1628c2ecf20Sopenharmony_ci return rc; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int __init sclp_conf_init(void) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int rc; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); 1738c2ecf20Sopenharmony_ci INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); 1748c2ecf20Sopenharmony_ci rc = sclp_register(&sclp_conf_register); 1758c2ecf20Sopenharmony_ci if (rc) 1768c2ecf20Sopenharmony_ci return rc; 1778c2ecf20Sopenharmony_ci return sclp_ofb_setup(); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci__initcall(sclp_conf_init); 181