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