]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/s390/char/sclp_config.c
UBUNTU: Ubuntu-4.15.0-96.97
[mirror_ubuntu-bionic-kernel.git] / drivers / s390 / char / sclp_config.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
2fc2d1e9 2/*
2fc2d1e9
HC
3 * Copyright IBM Corp. 2007
4 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
5 */
6
b3ff088b
MS
7#define KMSG_COMPONENT "sclp_config"
8#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
9
2fc2d1e9
HC
10#include <linux/init.h>
11#include <linux/errno.h>
12#include <linux/cpu.h>
8a25a2fd 13#include <linux/device.h>
2fc2d1e9 14#include <linux/workqueue.h>
c6f70d3b
JS
15#include <linux/slab.h>
16#include <linux/sysfs.h>
1e489518 17#include <asm/smp.h>
2fc2d1e9 18
b3ff088b 19#include "sclp.h"
2fc2d1e9
HC
20
21struct conf_mgm_data {
22 u8 reserved;
23 u8 ev_qualifier;
24} __attribute__((packed));
25
c6f70d3b
JS
26#define OFB_DATA_MAX 64
27
28struct sclp_ofb_evbuf {
29 struct evbuf_header header;
30 struct conf_mgm_data cm_data;
31 char ev_data[OFB_DATA_MAX];
32} __packed;
33
34struct sclp_ofb_sccb {
35 struct sccb_header header;
36 struct sclp_ofb_evbuf ofb_evbuf;
37} __packed;
38
1e489518 39#define EV_QUAL_CPU_CHANGE 1
2fc2d1e9 40#define EV_QUAL_CAP_CHANGE 3
c6f70d3b 41#define EV_QUAL_OPEN4BUSINESS 5
2fc2d1e9
HC
42
43static struct work_struct sclp_cpu_capability_work;
1e489518 44static struct work_struct sclp_cpu_change_work;
2fc2d1e9
HC
45
46static void sclp_cpu_capability_notify(struct work_struct *work)
47{
48 int cpu;
8a25a2fd 49 struct device *dev;
2fc2d1e9 50
097a116c 51 s390_update_cpu_mhz();
363fd4c1 52 pr_info("CPU capability may have changed\n");
86ef5c9a 53 get_online_cpus();
2fc2d1e9 54 for_each_online_cpu(cpu) {
8a25a2fd
KS
55 dev = get_cpu_device(cpu);
56 kobject_uevent(&dev->kobj, KOBJ_CHANGE);
2fc2d1e9 57 }
86ef5c9a 58 put_online_cpus();
2fc2d1e9
HC
59}
60
b9732ca1
HC
61static void __ref sclp_cpu_change_notify(struct work_struct *work)
62{
106c4894 63 lock_device_hotplug();
fc7e1e4b 64 smp_rescan_cpus();
106c4894 65 unlock_device_hotplug();
1e489518
HC
66}
67
2fc2d1e9
HC
68static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
69{
70 struct conf_mgm_data *cdata;
71
72 cdata = (struct conf_mgm_data *)(evbuf + 1);
1e489518
HC
73 switch (cdata->ev_qualifier) {
74 case EV_QUAL_CPU_CHANGE:
75 schedule_work(&sclp_cpu_change_work);
76 break;
77 case EV_QUAL_CAP_CHANGE:
2fc2d1e9 78 schedule_work(&sclp_cpu_capability_work);
1e489518
HC
79 break;
80 }
2fc2d1e9
HC
81}
82
83static struct sclp_register sclp_conf_register =
84{
c6f70d3b
JS
85#ifdef CONFIG_SCLP_OFB
86 .send_mask = EVTYP_CONFMGMDATA_MASK,
87#endif
2fc2d1e9
HC
88 .receive_mask = EVTYP_CONFMGMDATA_MASK,
89 .receiver_fn = sclp_conf_receiver_fn,
90};
91
c6f70d3b
JS
92#ifdef CONFIG_SCLP_OFB
93static int sclp_ofb_send_req(char *ev_data, size_t len)
94{
95 static DEFINE_MUTEX(send_mutex);
96 struct sclp_ofb_sccb *sccb;
97 int rc, response;
98
99 if (len > OFB_DATA_MAX)
100 return -EINVAL;
101 sccb = (struct sclp_ofb_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
102 if (!sccb)
103 return -ENOMEM;
104 /* Setup SCCB for Control-Program Identification */
105 sccb->header.length = sizeof(struct sclp_ofb_sccb);
106 sccb->ofb_evbuf.header.length = sizeof(struct sclp_ofb_evbuf);
107 sccb->ofb_evbuf.header.type = EVTYP_CONFMGMDATA;
108 sccb->ofb_evbuf.cm_data.ev_qualifier = EV_QUAL_OPEN4BUSINESS;
109 memcpy(sccb->ofb_evbuf.ev_data, ev_data, len);
110
111 if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK))
112 pr_warn("SCLP receiver did not register to receive "
113 "Configuration Management Data Events.\n");
114
115 mutex_lock(&send_mutex);
116 rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
117 mutex_unlock(&send_mutex);
118 if (rc)
119 goto out;
120 response = sccb->header.response_code;
121 if (response != 0x0020) {
122 pr_err("Open for Business request failed with response code "
123 "0x%04x\n", response);
124 rc = -EIO;
125 }
126out:
127 free_page((unsigned long)sccb);
128 return rc;
129}
130
131static ssize_t sysfs_ofb_data_write(struct file *filp, struct kobject *kobj,
132 struct bin_attribute *bin_attr,
133 char *buf, loff_t off, size_t count)
134{
135 int rc;
136
137 rc = sclp_ofb_send_req(buf, count);
138 return rc ?: count;
139}
140
c7e85ae5 141static const struct bin_attribute ofb_bin_attr = {
c6f70d3b
JS
142 .attr = {
143 .name = "event_data",
144 .mode = S_IWUSR,
145 },
146 .write = sysfs_ofb_data_write,
147};
148#endif
149
150static int __init sclp_ofb_setup(void)
151{
152#ifdef CONFIG_SCLP_OFB
153 struct kset *ofb_kset;
154 int rc;
155
156 ofb_kset = kset_create_and_add("ofb", NULL, firmware_kobj);
157 if (!ofb_kset)
158 return -ENOMEM;
159 rc = sysfs_create_bin_file(&ofb_kset->kobj, &ofb_bin_attr);
160 if (rc) {
161 kset_unregister(ofb_kset);
162 return rc;
163 }
164#endif
165 return 0;
166}
167
2fc2d1e9
HC
168static int __init sclp_conf_init(void)
169{
c6f70d3b
JS
170 int rc;
171
2fc2d1e9 172 INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
1e489518 173 INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
c6f70d3b
JS
174 rc = sclp_register(&sclp_conf_register);
175 if (rc)
176 return rc;
177 return sclp_ofb_setup();
2fc2d1e9
HC
178}
179
180__initcall(sclp_conf_init);