]>
Commit | Line | Data |
---|---|---|
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 | |
21 | struct conf_mgm_data { | |
22 | u8 reserved; | |
23 | u8 ev_qualifier; | |
24 | } __attribute__((packed)); | |
25 | ||
c6f70d3b JS |
26 | #define OFB_DATA_MAX 64 |
27 | ||
28 | struct 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 | ||
34 | struct 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 | |
43 | static struct work_struct sclp_cpu_capability_work; | |
1e489518 | 44 | static struct work_struct sclp_cpu_change_work; |
2fc2d1e9 HC |
45 | |
46 | static 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 |
61 | static 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 |
68 | static 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 | ||
83 | static 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 |
93 | static 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 | } | |
126 | out: | |
127 | free_page((unsigned long)sccb); | |
128 | return rc; | |
129 | } | |
130 | ||
131 | static 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 | 141 | static 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 | ||
150 | static 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 |
168 | static 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); |