blob: 12a344c66b46da361b01ca55a7000496a9b85e6d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/cio/chsc.c
3 * S/390 common I/O routines -- channel subsystem call
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
6 * IBM Corporation
7 * Author(s): Ingo Adlung (adlung@de.ibm.com)
Cornelia Huck4ce3b302006-01-14 13:21:04 -08008 * Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 * Arnd Bergmann (arndb@de.ibm.com)
10 */
11
12#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/slab.h>
14#include <linux/init.h>
15#include <linux/device.h>
16
17#include <asm/cio.h>
Peter Oberparleitere5854a52007-04-27 16:01:31 +020018#include <asm/chpid.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
20#include "css.h"
21#include "cio.h"
22#include "cio_debug.h"
23#include "ioasm.h"
Peter Oberparleitere6b6e102007-04-27 16:01:28 +020024#include "chp.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include "chsc.h"
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027static void *sei_page;
28
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020029struct chsc_ssd_area {
30 struct chsc_header request;
31 u16 :10;
32 u16 ssid:2;
33 u16 :4;
34 u16 f_sch; /* first subchannel */
35 u16 :16;
36 u16 l_sch; /* last subchannel */
37 u32 :32;
38 struct chsc_header response;
39 u32 :32;
40 u8 sch_valid : 1;
41 u8 dev_valid : 1;
42 u8 st : 3; /* subchannel type */
43 u8 zeroes : 3;
44 u8 unit_addr; /* unit address */
45 u16 devno; /* device number */
46 u8 path_mask;
47 u8 fla_valid_mask;
48 u16 sch; /* subchannel */
49 u8 chpid[8]; /* chpids 0-7 */
50 u16 fla[8]; /* full link addresses 0-7 */
51} __attribute__ ((packed));
52
53int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054{
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020055 unsigned long page;
56 struct chsc_ssd_area *ssd_area;
57 int ccode;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 int ret;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020059 int i;
60 int mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020062 page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 if (!page)
64 return -ENOMEM;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020065 ssd_area = (struct chsc_ssd_area *) page;
66 ssd_area->request.length = 0x0010;
67 ssd_area->request.code = 0x0004;
68 ssd_area->ssid = schid.ssid;
69 ssd_area->f_sch = schid.sch_no;
70 ssd_area->l_sch = schid.sch_no;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +020071
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020072 ccode = chsc(ssd_area);
73 /* Check response. */
74 if (ccode > 0) {
75 ret = (ccode == 3) ? -ENODEV : -EBUSY;
76 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 }
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020078 if (ssd_area->response.code != 0x0001) {
79 CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
80 schid.ssid, schid.sch_no,
81 ssd_area->response.code);
82 ret = -EIO;
83 goto out_free;
84 }
85 if (!ssd_area->sch_valid) {
86 ret = -ENODEV;
87 goto out_free;
88 }
89 /* Copy data */
90 ret = 0;
91 memset(ssd, 0, sizeof(struct chsc_ssd_info));
Cornelia Huckb279a4f2008-01-26 14:10:45 +010092 if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
93 (ssd_area->st != SUBCHANNEL_TYPE_MSG))
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020094 goto out_free;
95 ssd->path_mask = ssd_area->path_mask;
96 ssd->fla_valid_mask = ssd_area->fla_valid_mask;
97 for (i = 0; i < 8; i++) {
98 mask = 0x80 >> i;
99 if (ssd_area->path_mask & mask) {
100 chp_id_init(&ssd->chpid[i]);
101 ssd->chpid[i].id = ssd_area->chpid[i];
102 }
103 if (ssd_area->fla_valid_mask & mask)
104 ssd->fla[i] = ssd_area->fla[i];
105 }
106out_free:
107 free_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 return ret;
109}
110
Stefan Bader387b7342007-04-27 16:01:33 +0200111static int check_for_io_on_path(struct subchannel *sch, int mask)
112{
113 int cc;
114
115 cc = stsch(sch->schid, &sch->schib);
116 if (cc)
117 return 0;
118 if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
119 return 1;
120 return 0;
121}
122
123static void terminate_internal_io(struct subchannel *sch)
124{
125 if (cio_clear(sch)) {
126 /* Recheck device in case clear failed. */
127 sch->lpm = 0;
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200128 if (device_trigger_verify(sch) != 0)
129 css_schedule_eval(sch->schid);
Stefan Bader387b7342007-04-27 16:01:33 +0200130 return;
131 }
132 /* Request retry of internal operation. */
133 device_set_intretry(sch);
134 /* Call handler. */
135 if (sch->driver && sch->driver->termination)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100136 sch->driver->termination(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200137}
138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static int
140s390_subchannel_remove_chpid(struct device *dev, void *data)
141{
142 int j;
143 int mask;
144 struct subchannel *sch;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200145 struct chp_id *chpid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 struct schib schib;
147
148 sch = to_subchannel(dev);
149 chpid = data;
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200150 for (j = 0; j < 8; j++) {
151 mask = 0x80 >> j;
152 if ((sch->schib.pmcw.pim & mask) &&
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200153 (sch->schib.pmcw.chpid[j] == chpid->id))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 break;
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200155 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 if (j >= 8)
157 return 0;
158
Cornelia Huck2ec22982006-12-08 15:54:26 +0100159 spin_lock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800161 stsch(sch->schid, &schib);
Cornelia Huckb279a4f2008-01-26 14:10:45 +0100162 if (!css_sch_is_valid(&schib))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 goto out_unreg;
164 memcpy(&sch->schib, &schib, sizeof(struct schib));
165 /* Check for single path devices. */
166 if (sch->schib.pmcw.pim == 0x80)
167 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
Stefan Bader387b7342007-04-27 16:01:33 +0200169 if (check_for_io_on_path(sch, mask)) {
170 if (device_is_online(sch))
171 device_kill_io(sch);
172 else {
173 terminate_internal_io(sch);
174 /* Re-start path verification. */
175 if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100176 sch->driver->verify(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200177 }
178 } else {
179 /* trigger path verification. */
180 if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100181 sch->driver->verify(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200182 else if (sch->lpm == mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 }
185
Cornelia Huck2ec22982006-12-08 15:54:26 +0100186 spin_unlock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 return 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189out_unreg:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 sch->lpm = 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200191 spin_unlock_irq(sch->lock);
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200192 css_schedule_eval(sch->schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 return 0;
194}
195
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200196void chsc_chp_offline(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
198 char dbf_txt[15];
199
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200200 sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 CIO_TRACE_EVENT(2, dbf_txt);
202
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200203 if (chp_get_status(chpid) <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 return;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200205 bus_for_each_dev(&css_bus_type, NULL, &chpid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 s390_subchannel_remove_chpid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207}
208
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100209static int
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800210s390_process_res_acc_new_sch(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800212 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800213 /*
214 * We don't know the device yet, but since a path
215 * may be available now to the device we'll have
216 * to do recognition again.
217 * Since we don't have any idea about which chpid
218 * that beast may be on we'll have to do a stsch
219 * on all devices, grr...
220 */
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800221 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800222 /* We're through */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200223 return -ENXIO;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800224
225 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200226 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800227 return 0;
228}
229
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200230struct res_acc_data {
231 struct chp_id chpid;
232 u32 fla_mask;
233 u16 fla;
234};
235
236static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
237 struct res_acc_data *data)
238{
239 int i;
240 int mask;
241
242 for (i = 0; i < 8; i++) {
243 mask = 0x80 >> i;
244 if (!(ssd->path_mask & mask))
245 continue;
246 if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
247 continue;
248 if ((ssd->fla_valid_mask & mask) &&
249 ((ssd->fla[i] & data->fla_mask) != data->fla))
250 continue;
251 return mask;
252 }
253 return 0;
254}
255
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800256static int
257__s390_process_res_acc(struct subchannel_id schid, void *data)
258{
259 int chp_mask, old_lpm;
260 struct res_acc_data *res_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 struct subchannel *sch;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800262
Cornelia Huck12975ae2006-10-11 15:31:47 +0200263 res_data = data;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800264 sch = get_subchannel_by_schid(schid);
265 if (!sch)
266 /* Check if a subchannel is newly available. */
267 return s390_process_res_acc_new_sch(schid);
268
Cornelia Huck2ec22982006-12-08 15:54:26 +0100269 spin_lock_irq(sch->lock);
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200270 chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
271 if (chp_mask == 0)
272 goto out;
273 if (stsch(sch->schid, &sch->schib))
274 goto out;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800275 old_lpm = sch->lpm;
276 sch->lpm = ((sch->schib.pmcw.pim &
277 sch->schib.pmcw.pam &
278 sch->schib.pmcw.pom)
279 | chp_mask) & sch->opm;
280 if (!old_lpm && sch->lpm)
281 device_trigger_reprobe(sch);
282 else if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100283 sch->driver->verify(sch);
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200284out:
Cornelia Huck2ec22982006-12-08 15:54:26 +0100285 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800286 put_device(&sch->dev);
Peter Oberparleiterdd9963f2006-09-20 15:59:54 +0200287 return 0;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800288}
289
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200290static void s390_process_res_acc (struct res_acc_data *res_data)
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800291{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 char dbf_txt[15];
293
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200294 sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
295 res_data->chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 CIO_TRACE_EVENT( 2, dbf_txt);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800297 if (res_data->fla != 0) {
298 sprintf(dbf_txt, "fla%x", res_data->fla);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 CIO_TRACE_EVENT( 2, dbf_txt);
300 }
301
302 /*
303 * I/O resources may have become accessible.
304 * Scan through all subchannels that may be concerned and
305 * do a validation on those.
306 * The more information we have (info), the less scanning
307 * will we have to do.
308 */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200309 for_each_subchannel(__s390_process_res_acc, res_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
312static int
313__get_chpid_from_lir(void *data)
314{
315 struct lir {
316 u8 iq;
317 u8 ic;
318 u16 sci;
319 /* incident-node descriptor */
320 u32 indesc[28];
321 /* attached-node descriptor */
322 u32 andesc[28];
323 /* incident-specific information */
324 u32 isinfo[28];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100325 } __attribute__ ((packed)) *lir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
Cornelia Huck12975ae2006-10-11 15:31:47 +0200327 lir = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 if (!(lir->iq&0x80))
329 /* NULL link incident record */
330 return -EINVAL;
331 if (!(lir->indesc[0]&0xc0000000))
332 /* node descriptor not valid */
333 return -EINVAL;
334 if (!(lir->indesc[0]&0x10000000))
335 /* don't handle device-type nodes - FIXME */
336 return -EINVAL;
337 /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
338
339 return (u16) (lir->indesc[0]&0x000000ff);
340}
341
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100342struct chsc_sei_area {
343 struct chsc_header request;
344 u32 reserved1;
345 u32 reserved2;
346 u32 reserved3;
347 struct chsc_header response;
348 u32 reserved4;
349 u8 flags;
350 u8 vf; /* validity flags */
351 u8 rs; /* reporting source */
352 u8 cc; /* content code */
353 u16 fla; /* full link address */
354 u16 rsid; /* reporting source id */
355 u32 reserved5;
356 u32 reserved6;
357 u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */
358 /* ccdf has to be big enough for a link-incident record */
359} __attribute__ ((packed));
360
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200361static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200363 struct chp_id chpid;
364 int id;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100365
366 CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
367 sei_area->rs, sei_area->rsid);
368 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200369 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200370 id = __get_chpid_from_lir(sei_area->ccdf);
371 if (id < 0)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100372 CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200373 else {
374 chp_id_init(&chpid);
375 chpid.id = id;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200376 chsc_chp_offline(chpid);
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200377 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100378}
379
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200380static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100381{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800382 struct res_acc_data res_data;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200383 struct chp_id chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100384 int status;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100385
386 CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
387 "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
388 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200389 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200390 chp_id_init(&chpid);
391 chpid.id = sei_area->rsid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100392 /* allocate a new channel path structure, if needed */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200393 status = chp_get_status(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100394 if (status < 0)
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200395 chp_new(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100396 else if (!status)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200397 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100398 memset(&res_data, 0, sizeof(struct res_acc_data));
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200399 res_data.chpid = chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100400 if ((sei_area->vf & 0xc0) != 0) {
401 res_data.fla = sei_area->fla;
402 if ((sei_area->vf & 0xc0) == 0xc0)
403 /* full link address */
404 res_data.fla_mask = 0xffff;
405 else
406 /* link address */
407 res_data.fla_mask = 0xff00;
408 }
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200409 s390_process_res_acc(&res_data);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100410}
411
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200412struct chp_config_data {
413 u8 map[32];
414 u8 op;
415 u8 pc;
416};
417
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200418static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200419{
420 struct chp_config_data *data;
421 struct chp_id chpid;
422 int num;
423
424 CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
425 if (sei_area->rs != 0)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200426 return;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200427 data = (struct chp_config_data *) &(sei_area->ccdf);
428 chp_id_init(&chpid);
429 for (num = 0; num <= __MAX_CHPID; num++) {
430 if (!chp_test_bit(data->map, num))
431 continue;
432 chpid.id = num;
433 printk(KERN_WARNING "cio: processing configure event %d for "
434 "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
435 switch (data->op) {
436 case 0:
437 chp_cfg_schedule(chpid, 1);
438 break;
439 case 1:
440 chp_cfg_schedule(chpid, 0);
441 break;
442 case 2:
443 chp_cfg_cancel_deconfigure(chpid);
444 break;
445 }
446 }
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200447}
448
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200449static void chsc_process_sei(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100450{
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100451 /* Check if we might have lost some information. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200452 if (sei_area->flags & 0x40) {
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100453 CIO_CRW_EVENT(2, "chsc: event overflow\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200454 css_schedule_eval_all();
455 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100456 /* which kind of information was stored? */
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100457 switch (sei_area->cc) {
458 case 1: /* link incident*/
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200459 chsc_process_sei_link_incident(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100460 break;
461 case 2: /* i/o resource accessibiliy */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200462 chsc_process_sei_res_acc(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100463 break;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200464 case 8: /* channel-path-configuration notification */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200465 chsc_process_sei_chp_config(sei_area);
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200466 break;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100467 default: /* other stuff */
468 CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
469 sei_area->cc);
470 break;
471 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100472}
473
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200474void chsc_process_crw(void)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100475{
476 struct chsc_sei_area *sei_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
478 if (!sei_page)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200479 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100480 /* Access to sei_page is serialized through machine check handler
481 * thread, so no need for locking. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 sei_area = sei_page;
483
484 CIO_TRACE_EVENT( 2, "prcss");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 memset(sei_area, 0, sizeof(*sei_area));
Cornelia Huck495a5b42006-03-24 03:15:14 -0800487 sei_area->request.length = 0x0010;
488 sei_area->request.code = 0x000e;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100489 if (chsc(sei_area))
490 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100492 if (sei_area->response.code == 0x0001) {
493 CIO_CRW_EVENT(4, "chsc: sei successful\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200494 chsc_process_sei(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100495 } else {
496 CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 sei_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 break;
499 }
500 } while (sei_area->flags & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501}
502
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100503static int
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800504__chp_add_new_sch(struct subchannel_id schid)
505{
506 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800507
Cornelia Huck758976f2007-02-05 21:17:36 +0100508 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800509 /* We're through */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200510 return -ENXIO;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800511
512 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200513 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800514 return 0;
515}
516
517
518static int
519__chp_add(struct subchannel_id schid, void *data)
520{
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200521 int i, mask;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200522 struct chp_id *chpid;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800523 struct subchannel *sch;
524
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200525 chpid = data;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800526 sch = get_subchannel_by_schid(schid);
527 if (!sch)
528 /* Check if the subchannel is now available. */
529 return __chp_add_new_sch(schid);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100530 spin_lock_irq(sch->lock);
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200531 for (i=0; i<8; i++) {
532 mask = 0x80 >> i;
533 if ((sch->schib.pmcw.pim & mask) &&
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200534 (sch->schib.pmcw.chpid[i] == chpid->id)) {
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800535 if (stsch(sch->schid, &sch->schib) != 0) {
536 /* Endgame. */
Cornelia Huck2ec22982006-12-08 15:54:26 +0100537 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800538 return -ENXIO;
539 }
540 break;
541 }
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200542 }
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800543 if (i==8) {
Cornelia Huck2ec22982006-12-08 15:54:26 +0100544 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800545 return 0;
546 }
547 sch->lpm = ((sch->schib.pmcw.pim &
548 sch->schib.pmcw.pam &
549 sch->schib.pmcw.pom)
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200550 | mask) & sch->opm;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800551
552 if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100553 sch->driver->verify(sch);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800554
Cornelia Huck2ec22982006-12-08 15:54:26 +0100555 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800556 put_device(&sch->dev);
557 return 0;
558}
559
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200560void chsc_chp_online(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 char dbf_txt[15];
563
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200564 sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 CIO_TRACE_EVENT(2, dbf_txt);
566
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200567 if (chp_get_status(chpid) != 0)
568 for_each_subchannel(__chp_add, &chpid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200571static void __s390_subchannel_vary_chpid(struct subchannel *sch,
572 struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
574 int chp, old_lpm;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200575 int mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 unsigned long flags;
577
Cornelia Huck2ec22982006-12-08 15:54:26 +0100578 spin_lock_irqsave(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 old_lpm = sch->lpm;
580 for (chp = 0; chp < 8; chp++) {
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200581 mask = 0x80 >> chp;
582 if (!(sch->ssd_info.path_mask & mask))
583 continue;
584 if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 continue;
586
587 if (on) {
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200588 sch->opm |= mask;
589 sch->lpm |= mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 if (!old_lpm)
591 device_trigger_reprobe(sch);
592 else if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100593 sch->driver->verify(sch);
Cornelia Huck24cb5b42006-12-04 15:41:01 +0100594 break;
595 }
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200596 sch->opm &= ~mask;
597 sch->lpm &= ~mask;
598 if (check_for_io_on_path(sch, mask)) {
Cornelia Huckd23861f2006-12-04 15:41:04 +0100599 if (device_is_online(sch))
600 /* Path verification is done after killing. */
601 device_kill_io(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200602 else {
Cornelia Huckd23861f2006-12-04 15:41:04 +0100603 /* Kill and retry internal I/O. */
604 terminate_internal_io(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200605 /* Re-start path verification. */
606 if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100607 sch->driver->verify(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200608 }
Cornelia Huckd23861f2006-12-04 15:41:04 +0100609 } else if (!sch->lpm) {
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200610 if (device_trigger_verify(sch) != 0)
611 css_schedule_eval(sch->schid);
Cornelia Huck24cb5b42006-12-04 15:41:01 +0100612 } else if (sch->driver && sch->driver->verify)
Cornelia Huck602b20f2008-01-26 14:10:39 +0100613 sch->driver->verify(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 break;
615 }
Cornelia Huck2ec22982006-12-08 15:54:26 +0100616 spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617}
618
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200619static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620{
621 struct subchannel *sch;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200622 struct chp_id *chpid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
624 sch = to_subchannel(dev);
625 chpid = data;
626
627 __s390_subchannel_vary_chpid(sch, *chpid, 0);
628 return 0;
629}
630
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200631static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
633 struct subchannel *sch;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200634 struct chp_id *chpid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636 sch = to_subchannel(dev);
637 chpid = data;
638
639 __s390_subchannel_vary_chpid(sch, *chpid, 1);
640 return 0;
641}
642
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800643static int
644__s390_vary_chpid_on(struct subchannel_id schid, void *data)
645{
646 struct schib schib;
647 struct subchannel *sch;
648
649 sch = get_subchannel_by_schid(schid);
650 if (sch) {
651 put_device(&sch->dev);
652 return 0;
653 }
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800654 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800655 /* We're through */
656 return -ENXIO;
657 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200658 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800659 return 0;
660}
661
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200662/**
663 * chsc_chp_vary - propagate channel-path vary operation to subchannels
664 * @chpid: channl-path ID
665 * @on: non-zero for vary online, zero for vary offline
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200667int chsc_chp_vary(struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 /*
670 * Redo PathVerification on the devices the chpid connects to
671 */
672
673 bus_for_each_dev(&css_bus_type, NULL, &chpid, on ?
674 s390_subchannel_vary_chpid_on :
675 s390_subchannel_vary_chpid_off);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800676 if (on)
677 /* Scan for new devices on varied on path. */
678 for_each_subchannel(__s390_vary_chpid_on, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 return 0;
680}
681
Cornelia Huck495a5b42006-03-24 03:15:14 -0800682static void
683chsc_remove_cmg_attr(struct channel_subsystem *css)
684{
685 int i;
686
687 for (i = 0; i <= __MAX_CHPID; i++) {
688 if (!css->chps[i])
689 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200690 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800691 }
692}
693
694static int
695chsc_add_cmg_attr(struct channel_subsystem *css)
696{
697 int i, ret;
698
699 ret = 0;
700 for (i = 0; i <= __MAX_CHPID; i++) {
701 if (!css->chps[i])
702 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200703 ret = chp_add_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800704 if (ret)
705 goto cleanup;
706 }
707 return ret;
708cleanup:
709 for (--i; i >= 0; i--) {
710 if (!css->chps[i])
711 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200712 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800713 }
714 return ret;
715}
716
Cornelia Huck495a5b42006-03-24 03:15:14 -0800717static int
718__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
719{
720 struct {
721 struct chsc_header request;
722 u32 operation_code : 2;
723 u32 : 30;
724 u32 key : 4;
725 u32 : 28;
726 u32 zeroes1;
727 u32 cub_addr1;
728 u32 zeroes2;
729 u32 cub_addr2;
730 u32 reserved[13];
731 struct chsc_header response;
732 u32 status : 8;
733 u32 : 4;
734 u32 fmt : 4;
735 u32 : 16;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100736 } __attribute__ ((packed)) *secm_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800737 int ret, ccode;
738
739 secm_area = page;
740 secm_area->request.length = 0x0050;
741 secm_area->request.code = 0x0016;
742
743 secm_area->key = PAGE_DEFAULT_KEY;
744 secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
745 secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;
746
747 secm_area->operation_code = enable ? 0 : 1;
748
749 ccode = chsc(secm_area);
750 if (ccode > 0)
751 return (ccode == 3) ? -ENODEV : -EBUSY;
752
753 switch (secm_area->response.code) {
754 case 0x0001: /* Success. */
755 ret = 0;
756 break;
757 case 0x0003: /* Invalid block. */
758 case 0x0007: /* Invalid format. */
759 case 0x0008: /* Other invalid block. */
760 CIO_CRW_EVENT(2, "Error in chsc request block!\n");
761 ret = -EINVAL;
762 break;
763 case 0x0004: /* Command not provided in model. */
764 CIO_CRW_EVENT(2, "Model does not provide secm\n");
765 ret = -EOPNOTSUPP;
766 break;
767 case 0x0102: /* cub adresses incorrect */
768 CIO_CRW_EVENT(2, "Invalid addresses in chsc request block\n");
769 ret = -EINVAL;
770 break;
771 case 0x0103: /* key error */
772 CIO_CRW_EVENT(2, "Access key error in secm\n");
773 ret = -EINVAL;
774 break;
775 case 0x0105: /* error while starting */
776 CIO_CRW_EVENT(2, "Error while starting channel measurement\n");
777 ret = -EIO;
778 break;
779 default:
780 CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
781 secm_area->response.code);
782 ret = -EIO;
783 }
784 return ret;
785}
786
787int
788chsc_secm(struct channel_subsystem *css, int enable)
789{
790 void *secm_area;
791 int ret;
792
793 secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
794 if (!secm_area)
795 return -ENOMEM;
796
797 mutex_lock(&css->mutex);
798 if (enable && !css->cm_enabled) {
799 css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
800 css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
801 if (!css->cub_addr1 || !css->cub_addr2) {
802 free_page((unsigned long)css->cub_addr1);
803 free_page((unsigned long)css->cub_addr2);
804 free_page((unsigned long)secm_area);
805 mutex_unlock(&css->mutex);
806 return -ENOMEM;
807 }
808 }
809 ret = __chsc_do_secm(css, enable, secm_area);
810 if (!ret) {
811 css->cm_enabled = enable;
812 if (css->cm_enabled) {
813 ret = chsc_add_cmg_attr(css);
814 if (ret) {
815 memset(secm_area, 0, PAGE_SIZE);
816 __chsc_do_secm(css, 0, secm_area);
817 css->cm_enabled = 0;
818 }
819 } else
820 chsc_remove_cmg_attr(css);
821 }
Cornelia Huck8c4941c2007-04-27 16:01:38 +0200822 if (!css->cm_enabled) {
Cornelia Huck495a5b42006-03-24 03:15:14 -0800823 free_page((unsigned long)css->cub_addr1);
824 free_page((unsigned long)css->cub_addr2);
825 }
826 mutex_unlock(&css->mutex);
827 free_page((unsigned long)secm_area);
828 return ret;
829}
830
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200831int chsc_determine_channel_path_description(struct chp_id chpid,
832 struct channel_path_desc *desc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833{
834 int ccode, ret;
835
836 struct {
837 struct chsc_header request;
838 u32 : 24;
839 u32 first_chpid : 8;
840 u32 : 24;
841 u32 last_chpid : 8;
842 u32 zeroes1;
843 struct chsc_header response;
844 u32 zeroes2;
845 struct channel_path_desc desc;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100846 } __attribute__ ((packed)) *scpd_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
848 scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
849 if (!scpd_area)
850 return -ENOMEM;
851
Cornelia Huck495a5b42006-03-24 03:15:14 -0800852 scpd_area->request.length = 0x0010;
853 scpd_area->request.code = 0x0002;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200855 scpd_area->first_chpid = chpid.id;
856 scpd_area->last_chpid = chpid.id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 ccode = chsc(scpd_area);
859 if (ccode > 0) {
860 ret = (ccode == 3) ? -ENODEV : -EBUSY;
861 goto out;
862 }
863
864 switch (scpd_area->response.code) {
865 case 0x0001: /* Success. */
866 memcpy(desc, &scpd_area->desc,
867 sizeof(struct channel_path_desc));
868 ret = 0;
869 break;
870 case 0x0003: /* Invalid block. */
871 case 0x0007: /* Invalid format. */
872 case 0x0008: /* Other invalid block. */
873 CIO_CRW_EVENT(2, "Error in chsc request block!\n");
874 ret = -EINVAL;
875 break;
876 case 0x0004: /* Command not provided in model. */
877 CIO_CRW_EVENT(2, "Model does not provide scpd\n");
878 ret = -EOPNOTSUPP;
879 break;
880 default:
881 CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
882 scpd_area->response.code);
883 ret = -EIO;
884 }
885out:
886 free_page((unsigned long)scpd_area);
887 return ret;
888}
889
Cornelia Huck495a5b42006-03-24 03:15:14 -0800890static void
891chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
892 struct cmg_chars *chars)
893{
894 switch (chp->cmg) {
895 case 2:
896 case 3:
897 chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
898 GFP_KERNEL);
899 if (chp->cmg_chars) {
900 int i, mask;
901 struct cmg_chars *cmg_chars;
902
903 cmg_chars = chp->cmg_chars;
904 for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
905 mask = 0x80 >> (i + 3);
906 if (cmcv & mask)
907 cmg_chars->values[i] = chars->values[i];
908 else
909 cmg_chars->values[i] = 0;
910 }
911 }
912 break;
913 default:
914 /* No cmg-dependent data. */
915 break;
916 }
917}
918
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200919int chsc_get_channel_measurement_chars(struct channel_path *chp)
Cornelia Huck495a5b42006-03-24 03:15:14 -0800920{
921 int ccode, ret;
922
923 struct {
924 struct chsc_header request;
925 u32 : 24;
926 u32 first_chpid : 8;
927 u32 : 24;
928 u32 last_chpid : 8;
929 u32 zeroes1;
930 struct chsc_header response;
931 u32 zeroes2;
932 u32 not_valid : 1;
933 u32 shared : 1;
934 u32 : 22;
935 u32 chpid : 8;
936 u32 cmcv : 5;
937 u32 : 11;
938 u32 cmgq : 8;
939 u32 cmg : 8;
940 u32 zeroes3;
941 u32 data[NR_MEASUREMENT_CHARS];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100942 } __attribute__ ((packed)) *scmc_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800943
944 scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
945 if (!scmc_area)
946 return -ENOMEM;
947
948 scmc_area->request.length = 0x0010;
949 scmc_area->request.code = 0x0022;
950
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200951 scmc_area->first_chpid = chp->chpid.id;
952 scmc_area->last_chpid = chp->chpid.id;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800953
954 ccode = chsc(scmc_area);
955 if (ccode > 0) {
956 ret = (ccode == 3) ? -ENODEV : -EBUSY;
957 goto out;
958 }
959
960 switch (scmc_area->response.code) {
961 case 0x0001: /* Success. */
962 if (!scmc_area->not_valid) {
963 chp->cmg = scmc_area->cmg;
964 chp->shared = scmc_area->shared;
965 chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
966 (struct cmg_chars *)
967 &scmc_area->data);
968 } else {
969 chp->cmg = -1;
970 chp->shared = -1;
971 }
972 ret = 0;
973 break;
974 case 0x0003: /* Invalid block. */
975 case 0x0007: /* Invalid format. */
976 case 0x0008: /* Invalid bit combination. */
977 CIO_CRW_EVENT(2, "Error in chsc request block!\n");
978 ret = -EINVAL;
979 break;
980 case 0x0004: /* Command not provided. */
981 CIO_CRW_EVENT(2, "Model does not provide scmc\n");
982 ret = -EOPNOTSUPP;
983 break;
984 default:
985 CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
986 scmc_area->response.code);
987 ret = -EIO;
988 }
989out:
990 free_page((unsigned long)scmc_area);
991 return ret;
992}
993
Cornelia Huck4434a382007-07-27 12:29:21 +0200994int __init chsc_alloc_sei_area(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
996 sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
997 if (!sei_page)
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200998 CIO_MSG_EVENT(0, "Can't allocate page for processing of "
999 "chsc machine checks!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 return (sei_page ? 0 : -ENOMEM);
1001}
1002
Cornelia Huck4434a382007-07-27 12:29:21 +02001003void __init chsc_free_sei_area(void)
1004{
1005 kfree(sei_page);
1006}
1007
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001008int __init
1009chsc_enable_facility(int operation_code)
1010{
1011 int ret;
1012 struct {
1013 struct chsc_header request;
1014 u8 reserved1:4;
1015 u8 format:4;
1016 u8 reserved2;
1017 u16 operation_code;
1018 u32 reserved3;
1019 u32 reserved4;
1020 u32 operation_data_area[252];
1021 struct chsc_header response;
1022 u32 reserved5:4;
1023 u32 format2:4;
1024 u32 reserved6:24;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +01001025 } __attribute__ ((packed)) *sda_area;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001026
1027 sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
1028 if (!sda_area)
1029 return -ENOMEM;
Cornelia Huck495a5b42006-03-24 03:15:14 -08001030 sda_area->request.length = 0x0400;
1031 sda_area->request.code = 0x0031;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001032 sda_area->operation_code = operation_code;
1033
1034 ret = chsc(sda_area);
1035 if (ret > 0) {
1036 ret = (ret == 3) ? -ENODEV : -EBUSY;
1037 goto out;
1038 }
1039 switch (sda_area->response.code) {
Cornelia Huck15730dd2006-03-06 15:43:02 -08001040 case 0x0001: /* everything ok */
1041 ret = 0;
1042 break;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001043 case 0x0003: /* invalid request block */
1044 case 0x0007:
1045 ret = -EINVAL;
1046 break;
1047 case 0x0004: /* command not provided */
1048 case 0x0101: /* facility not provided */
1049 ret = -EOPNOTSUPP;
1050 break;
Cornelia Huck15730dd2006-03-06 15:43:02 -08001051 default: /* something went wrong */
1052 ret = -EIO;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001053 }
1054 out:
1055 free_page((unsigned long)sda_area);
1056 return ret;
1057}
1058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059struct css_general_char css_general_characteristics;
1060struct css_chsc_char css_chsc_characteristics;
1061
1062int __init
1063chsc_determine_css_characteristics(void)
1064{
1065 int result;
1066 struct {
1067 struct chsc_header request;
1068 u32 reserved1;
1069 u32 reserved2;
1070 u32 reserved3;
1071 struct chsc_header response;
1072 u32 reserved4;
1073 u32 general_char[510];
1074 u32 chsc_char[518];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +01001075 } __attribute__ ((packed)) *scsc_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077 scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
1078 if (!scsc_area) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +02001079 CIO_MSG_EVENT(0, "Was not able to determine available"
1080 "CHSCs due to no memory.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 return -ENOMEM;
1082 }
1083
Cornelia Huck495a5b42006-03-24 03:15:14 -08001084 scsc_area->request.length = 0x0010;
1085 scsc_area->request.code = 0x0010;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
1087 result = chsc(scsc_area);
1088 if (result) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +02001089 CIO_MSG_EVENT(0, "Was not able to determine available CHSCs, "
1090 "cc=%i.\n", result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 result = -EIO;
1092 goto exit;
1093 }
1094
1095 if (scsc_area->response.code != 1) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +02001096 CIO_MSG_EVENT(0, "Was not able to determine "
1097 "available CHSCs.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 result = -EIO;
1099 goto exit;
1100 }
1101 memcpy(&css_general_characteristics, scsc_area->general_char,
1102 sizeof(css_general_characteristics));
1103 memcpy(&css_chsc_characteristics, scsc_area->chsc_char,
1104 sizeof(css_chsc_characteristics));
1105exit:
1106 free_page ((unsigned long) scsc_area);
1107 return result;
1108}
1109
1110EXPORT_SYMBOL_GPL(css_general_characteristics);
1111EXPORT_SYMBOL_GPL(css_chsc_characteristics);