blob: 597c0c76a2adcaf12cc228465b6f363a8a528626 [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));
92 if ((ssd_area->st != 0) && (ssd_area->st != 2))
93 goto out_free;
94 ssd->path_mask = ssd_area->path_mask;
95 ssd->fla_valid_mask = ssd_area->fla_valid_mask;
96 for (i = 0; i < 8; i++) {
97 mask = 0x80 >> i;
98 if (ssd_area->path_mask & mask) {
99 chp_id_init(&ssd->chpid[i]);
100 ssd->chpid[i].id = ssd_area->chpid[i];
101 }
102 if (ssd_area->fla_valid_mask & mask)
103 ssd->fla[i] = ssd_area->fla[i];
104 }
105out_free:
106 free_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 return ret;
108}
109
Stefan Bader387b7342007-04-27 16:01:33 +0200110static int check_for_io_on_path(struct subchannel *sch, int mask)
111{
112 int cc;
113
114 cc = stsch(sch->schid, &sch->schib);
115 if (cc)
116 return 0;
117 if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
118 return 1;
119 return 0;
120}
121
122static void terminate_internal_io(struct subchannel *sch)
123{
124 if (cio_clear(sch)) {
125 /* Recheck device in case clear failed. */
126 sch->lpm = 0;
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200127 if (device_trigger_verify(sch) != 0)
128 css_schedule_eval(sch->schid);
Stefan Bader387b7342007-04-27 16:01:33 +0200129 return;
130 }
131 /* Request retry of internal operation. */
132 device_set_intretry(sch);
133 /* Call handler. */
134 if (sch->driver && sch->driver->termination)
135 sch->driver->termination(&sch->dev);
136}
137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138static int
139s390_subchannel_remove_chpid(struct device *dev, void *data)
140{
141 int j;
142 int mask;
143 struct subchannel *sch;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200144 struct chp_id *chpid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 struct schib schib;
146
147 sch = to_subchannel(dev);
148 chpid = data;
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200149 for (j = 0; j < 8; j++) {
150 mask = 0x80 >> j;
151 if ((sch->schib.pmcw.pim & mask) &&
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200152 (sch->schib.pmcw.chpid[j] == chpid->id))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 break;
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200154 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 if (j >= 8)
156 return 0;
157
Cornelia Huck2ec22982006-12-08 15:54:26 +0100158 spin_lock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800160 stsch(sch->schid, &schib);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 if (!schib.pmcw.dnv)
162 goto out_unreg;
163 memcpy(&sch->schib, &schib, sizeof(struct schib));
164 /* Check for single path devices. */
165 if (sch->schib.pmcw.pim == 0x80)
166 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
Stefan Bader387b7342007-04-27 16:01:33 +0200168 if (check_for_io_on_path(sch, mask)) {
169 if (device_is_online(sch))
170 device_kill_io(sch);
171 else {
172 terminate_internal_io(sch);
173 /* Re-start path verification. */
174 if (sch->driver && sch->driver->verify)
175 sch->driver->verify(&sch->dev);
176 }
177 } else {
178 /* trigger path verification. */
179 if (sch->driver && sch->driver->verify)
180 sch->driver->verify(&sch->dev);
181 else if (sch->lpm == mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
184
Cornelia Huck2ec22982006-12-08 15:54:26 +0100185 spin_unlock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 return 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200187
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188out_unreg:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 sch->lpm = 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200190 spin_unlock_irq(sch->lock);
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200191 css_schedule_eval(sch->schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 return 0;
193}
194
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200195void chsc_chp_offline(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
197 char dbf_txt[15];
198
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200199 sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 CIO_TRACE_EVENT(2, dbf_txt);
201
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200202 if (chp_get_status(chpid) <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 return;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200204 bus_for_each_dev(&css_bus_type, NULL, &chpid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 s390_subchannel_remove_chpid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206}
207
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100208static int
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800209s390_process_res_acc_new_sch(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800211 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800212 /*
213 * We don't know the device yet, but since a path
214 * may be available now to the device we'll have
215 * to do recognition again.
216 * Since we don't have any idea about which chpid
217 * that beast may be on we'll have to do a stsch
218 * on all devices, grr...
219 */
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800220 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800221 /* We're through */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200222 return -ENXIO;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800223
224 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200225 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800226 return 0;
227}
228
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200229struct res_acc_data {
230 struct chp_id chpid;
231 u32 fla_mask;
232 u16 fla;
233};
234
235static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
236 struct res_acc_data *data)
237{
238 int i;
239 int mask;
240
241 for (i = 0; i < 8; i++) {
242 mask = 0x80 >> i;
243 if (!(ssd->path_mask & mask))
244 continue;
245 if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
246 continue;
247 if ((ssd->fla_valid_mask & mask) &&
248 ((ssd->fla[i] & data->fla_mask) != data->fla))
249 continue;
250 return mask;
251 }
252 return 0;
253}
254
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800255static int
256__s390_process_res_acc(struct subchannel_id schid, void *data)
257{
258 int chp_mask, old_lpm;
259 struct res_acc_data *res_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 struct subchannel *sch;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800261
Cornelia Huck12975ae2006-10-11 15:31:47 +0200262 res_data = data;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800263 sch = get_subchannel_by_schid(schid);
264 if (!sch)
265 /* Check if a subchannel is newly available. */
266 return s390_process_res_acc_new_sch(schid);
267
Cornelia Huck2ec22982006-12-08 15:54:26 +0100268 spin_lock_irq(sch->lock);
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200269 chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
270 if (chp_mask == 0)
271 goto out;
272 if (stsch(sch->schid, &sch->schib))
273 goto out;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800274 old_lpm = sch->lpm;
275 sch->lpm = ((sch->schib.pmcw.pim &
276 sch->schib.pmcw.pam &
277 sch->schib.pmcw.pom)
278 | chp_mask) & sch->opm;
279 if (!old_lpm && sch->lpm)
280 device_trigger_reprobe(sch);
281 else if (sch->driver && sch->driver->verify)
282 sch->driver->verify(&sch->dev);
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200283out:
Cornelia Huck2ec22982006-12-08 15:54:26 +0100284 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800285 put_device(&sch->dev);
Peter Oberparleiterdd9963f2006-09-20 15:59:54 +0200286 return 0;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800287}
288
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200289static void s390_process_res_acc (struct res_acc_data *res_data)
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800290{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 char dbf_txt[15];
292
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200293 sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
294 res_data->chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 CIO_TRACE_EVENT( 2, dbf_txt);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800296 if (res_data->fla != 0) {
297 sprintf(dbf_txt, "fla%x", res_data->fla);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 CIO_TRACE_EVENT( 2, dbf_txt);
299 }
300
301 /*
302 * I/O resources may have become accessible.
303 * Scan through all subchannels that may be concerned and
304 * do a validation on those.
305 * The more information we have (info), the less scanning
306 * will we have to do.
307 */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200308 for_each_subchannel(__s390_process_res_acc, res_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309}
310
311static int
312__get_chpid_from_lir(void *data)
313{
314 struct lir {
315 u8 iq;
316 u8 ic;
317 u16 sci;
318 /* incident-node descriptor */
319 u32 indesc[28];
320 /* attached-node descriptor */
321 u32 andesc[28];
322 /* incident-specific information */
323 u32 isinfo[28];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100324 } __attribute__ ((packed)) *lir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
Cornelia Huck12975ae2006-10-11 15:31:47 +0200326 lir = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 if (!(lir->iq&0x80))
328 /* NULL link incident record */
329 return -EINVAL;
330 if (!(lir->indesc[0]&0xc0000000))
331 /* node descriptor not valid */
332 return -EINVAL;
333 if (!(lir->indesc[0]&0x10000000))
334 /* don't handle device-type nodes - FIXME */
335 return -EINVAL;
336 /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
337
338 return (u16) (lir->indesc[0]&0x000000ff);
339}
340
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100341struct chsc_sei_area {
342 struct chsc_header request;
343 u32 reserved1;
344 u32 reserved2;
345 u32 reserved3;
346 struct chsc_header response;
347 u32 reserved4;
348 u8 flags;
349 u8 vf; /* validity flags */
350 u8 rs; /* reporting source */
351 u8 cc; /* content code */
352 u16 fla; /* full link address */
353 u16 rsid; /* reporting source id */
354 u32 reserved5;
355 u32 reserved6;
356 u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */
357 /* ccdf has to be big enough for a link-incident record */
358} __attribute__ ((packed));
359
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200360static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200362 struct chp_id chpid;
363 int id;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100364
365 CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
366 sei_area->rs, sei_area->rsid);
367 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200368 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200369 id = __get_chpid_from_lir(sei_area->ccdf);
370 if (id < 0)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100371 CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200372 else {
373 chp_id_init(&chpid);
374 chpid.id = id;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200375 chsc_chp_offline(chpid);
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200376 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100377}
378
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200379static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100380{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800381 struct res_acc_data res_data;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200382 struct chp_id chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100383 int status;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100384
385 CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
386 "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
387 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200388 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200389 chp_id_init(&chpid);
390 chpid.id = sei_area->rsid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100391 /* allocate a new channel path structure, if needed */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200392 status = chp_get_status(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100393 if (status < 0)
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200394 chp_new(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100395 else if (!status)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200396 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100397 memset(&res_data, 0, sizeof(struct res_acc_data));
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200398 res_data.chpid = chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100399 if ((sei_area->vf & 0xc0) != 0) {
400 res_data.fla = sei_area->fla;
401 if ((sei_area->vf & 0xc0) == 0xc0)
402 /* full link address */
403 res_data.fla_mask = 0xffff;
404 else
405 /* link address */
406 res_data.fla_mask = 0xff00;
407 }
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200408 s390_process_res_acc(&res_data);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100409}
410
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200411struct chp_config_data {
412 u8 map[32];
413 u8 op;
414 u8 pc;
415};
416
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200417static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200418{
419 struct chp_config_data *data;
420 struct chp_id chpid;
421 int num;
422
423 CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
424 if (sei_area->rs != 0)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200425 return;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200426 data = (struct chp_config_data *) &(sei_area->ccdf);
427 chp_id_init(&chpid);
428 for (num = 0; num <= __MAX_CHPID; num++) {
429 if (!chp_test_bit(data->map, num))
430 continue;
431 chpid.id = num;
432 printk(KERN_WARNING "cio: processing configure event %d for "
433 "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
434 switch (data->op) {
435 case 0:
436 chp_cfg_schedule(chpid, 1);
437 break;
438 case 1:
439 chp_cfg_schedule(chpid, 0);
440 break;
441 case 2:
442 chp_cfg_cancel_deconfigure(chpid);
443 break;
444 }
445 }
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200446}
447
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200448static void chsc_process_sei(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100449{
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100450 /* Check if we might have lost some information. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200451 if (sei_area->flags & 0x40) {
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100452 CIO_CRW_EVENT(2, "chsc: event overflow\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200453 css_schedule_eval_all();
454 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100455 /* which kind of information was stored? */
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100456 switch (sei_area->cc) {
457 case 1: /* link incident*/
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200458 chsc_process_sei_link_incident(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100459 break;
460 case 2: /* i/o resource accessibiliy */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200461 chsc_process_sei_res_acc(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100462 break;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200463 case 8: /* channel-path-configuration notification */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200464 chsc_process_sei_chp_config(sei_area);
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200465 break;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100466 default: /* other stuff */
467 CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
468 sei_area->cc);
469 break;
470 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100471}
472
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200473void chsc_process_crw(void)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100474{
475 struct chsc_sei_area *sei_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477 if (!sei_page)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200478 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100479 /* Access to sei_page is serialized through machine check handler
480 * thread, so no need for locking. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 sei_area = sei_page;
482
483 CIO_TRACE_EVENT( 2, "prcss");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 memset(sei_area, 0, sizeof(*sei_area));
Cornelia Huck495a5b42006-03-24 03:15:14 -0800486 sei_area->request.length = 0x0010;
487 sei_area->request.code = 0x000e;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100488 if (chsc(sei_area))
489 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100491 if (sei_area->response.code == 0x0001) {
492 CIO_CRW_EVENT(4, "chsc: sei successful\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200493 chsc_process_sei(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100494 } else {
495 CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 sei_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 break;
498 }
499 } while (sei_area->flags & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500}
501
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100502static int
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800503__chp_add_new_sch(struct subchannel_id schid)
504{
505 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800506
Cornelia Huck758976f2007-02-05 21:17:36 +0100507 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800508 /* We're through */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200509 return -ENXIO;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800510
511 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200512 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800513 return 0;
514}
515
516
517static int
518__chp_add(struct subchannel_id schid, void *data)
519{
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200520 int i, mask;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200521 struct chp_id *chpid;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800522 struct subchannel *sch;
523
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200524 chpid = data;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800525 sch = get_subchannel_by_schid(schid);
526 if (!sch)
527 /* Check if the subchannel is now available. */
528 return __chp_add_new_sch(schid);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100529 spin_lock_irq(sch->lock);
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200530 for (i=0; i<8; i++) {
531 mask = 0x80 >> i;
532 if ((sch->schib.pmcw.pim & mask) &&
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200533 (sch->schib.pmcw.chpid[i] == chpid->id)) {
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800534 if (stsch(sch->schid, &sch->schib) != 0) {
535 /* Endgame. */
Cornelia Huck2ec22982006-12-08 15:54:26 +0100536 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800537 return -ENXIO;
538 }
539 break;
540 }
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200541 }
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800542 if (i==8) {
Cornelia Huck2ec22982006-12-08 15:54:26 +0100543 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800544 return 0;
545 }
546 sch->lpm = ((sch->schib.pmcw.pim &
547 sch->schib.pmcw.pam &
548 sch->schib.pmcw.pom)
Cornelia Huck7e8ae7b2006-10-06 16:38:29 +0200549 | mask) & sch->opm;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800550
551 if (sch->driver && sch->driver->verify)
552 sch->driver->verify(&sch->dev);
553
Cornelia Huck2ec22982006-12-08 15:54:26 +0100554 spin_unlock_irq(sch->lock);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800555 put_device(&sch->dev);
556 return 0;
557}
558
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200559void chsc_chp_online(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 char dbf_txt[15];
562
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200563 sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 CIO_TRACE_EVENT(2, dbf_txt);
565
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200566 if (chp_get_status(chpid) != 0)
567 for_each_subchannel(__chp_add, &chpid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568}
569
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200570static void __s390_subchannel_vary_chpid(struct subchannel *sch,
571 struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
573 int chp, old_lpm;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200574 int mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 unsigned long flags;
576
Cornelia Huck2ec22982006-12-08 15:54:26 +0100577 spin_lock_irqsave(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 old_lpm = sch->lpm;
579 for (chp = 0; chp < 8; chp++) {
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200580 mask = 0x80 >> chp;
581 if (!(sch->ssd_info.path_mask & mask))
582 continue;
583 if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 continue;
585
586 if (on) {
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200587 sch->opm |= mask;
588 sch->lpm |= mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 if (!old_lpm)
590 device_trigger_reprobe(sch);
591 else if (sch->driver && sch->driver->verify)
592 sch->driver->verify(&sch->dev);
Cornelia Huck24cb5b42006-12-04 15:41:01 +0100593 break;
594 }
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200595 sch->opm &= ~mask;
596 sch->lpm &= ~mask;
597 if (check_for_io_on_path(sch, mask)) {
Cornelia Huckd23861f2006-12-04 15:41:04 +0100598 if (device_is_online(sch))
599 /* Path verification is done after killing. */
600 device_kill_io(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200601 else {
Cornelia Huckd23861f2006-12-04 15:41:04 +0100602 /* Kill and retry internal I/O. */
603 terminate_internal_io(sch);
Stefan Bader387b7342007-04-27 16:01:33 +0200604 /* Re-start path verification. */
605 if (sch->driver && sch->driver->verify)
606 sch->driver->verify(&sch->dev);
607 }
Cornelia Huckd23861f2006-12-04 15:41:04 +0100608 } else if (!sch->lpm) {
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200609 if (device_trigger_verify(sch) != 0)
610 css_schedule_eval(sch->schid);
Cornelia Huck24cb5b42006-12-04 15:41:01 +0100611 } else if (sch->driver && sch->driver->verify)
612 sch->driver->verify(&sch->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 break;
614 }
Cornelia Huck2ec22982006-12-08 15:54:26 +0100615 spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616}
617
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200618static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
620 struct subchannel *sch;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200621 struct chp_id *chpid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623 sch = to_subchannel(dev);
624 chpid = data;
625
626 __s390_subchannel_vary_chpid(sch, *chpid, 0);
627 return 0;
628}
629
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200630static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
632 struct subchannel *sch;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200633 struct chp_id *chpid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
635 sch = to_subchannel(dev);
636 chpid = data;
637
638 __s390_subchannel_vary_chpid(sch, *chpid, 1);
639 return 0;
640}
641
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800642static int
643__s390_vary_chpid_on(struct subchannel_id schid, void *data)
644{
645 struct schib schib;
646 struct subchannel *sch;
647
648 sch = get_subchannel_by_schid(schid);
649 if (sch) {
650 put_device(&sch->dev);
651 return 0;
652 }
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800653 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800654 /* We're through */
655 return -ENXIO;
656 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200657 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800658 return 0;
659}
660
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200661/**
662 * chsc_chp_vary - propagate channel-path vary operation to subchannels
663 * @chpid: channl-path ID
664 * @on: non-zero for vary online, zero for vary offline
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200666int chsc_chp_vary(struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 /*
669 * Redo PathVerification on the devices the chpid connects to
670 */
671
672 bus_for_each_dev(&css_bus_type, NULL, &chpid, on ?
673 s390_subchannel_vary_chpid_on :
674 s390_subchannel_vary_chpid_off);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800675 if (on)
676 /* Scan for new devices on varied on path. */
677 for_each_subchannel(__s390_vary_chpid_on, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 return 0;
679}
680
Cornelia Huck495a5b42006-03-24 03:15:14 -0800681static void
682chsc_remove_cmg_attr(struct channel_subsystem *css)
683{
684 int i;
685
686 for (i = 0; i <= __MAX_CHPID; i++) {
687 if (!css->chps[i])
688 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200689 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800690 }
691}
692
693static int
694chsc_add_cmg_attr(struct channel_subsystem *css)
695{
696 int i, ret;
697
698 ret = 0;
699 for (i = 0; i <= __MAX_CHPID; i++) {
700 if (!css->chps[i])
701 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200702 ret = chp_add_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800703 if (ret)
704 goto cleanup;
705 }
706 return ret;
707cleanup:
708 for (--i; i >= 0; i--) {
709 if (!css->chps[i])
710 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200711 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800712 }
713 return ret;
714}
715
Cornelia Huck495a5b42006-03-24 03:15:14 -0800716static int
717__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
718{
719 struct {
720 struct chsc_header request;
721 u32 operation_code : 2;
722 u32 : 30;
723 u32 key : 4;
724 u32 : 28;
725 u32 zeroes1;
726 u32 cub_addr1;
727 u32 zeroes2;
728 u32 cub_addr2;
729 u32 reserved[13];
730 struct chsc_header response;
731 u32 status : 8;
732 u32 : 4;
733 u32 fmt : 4;
734 u32 : 16;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100735 } __attribute__ ((packed)) *secm_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800736 int ret, ccode;
737
738 secm_area = page;
739 secm_area->request.length = 0x0050;
740 secm_area->request.code = 0x0016;
741
742 secm_area->key = PAGE_DEFAULT_KEY;
743 secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
744 secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;
745
746 secm_area->operation_code = enable ? 0 : 1;
747
748 ccode = chsc(secm_area);
749 if (ccode > 0)
750 return (ccode == 3) ? -ENODEV : -EBUSY;
751
752 switch (secm_area->response.code) {
753 case 0x0001: /* Success. */
754 ret = 0;
755 break;
756 case 0x0003: /* Invalid block. */
757 case 0x0007: /* Invalid format. */
758 case 0x0008: /* Other invalid block. */
759 CIO_CRW_EVENT(2, "Error in chsc request block!\n");
760 ret = -EINVAL;
761 break;
762 case 0x0004: /* Command not provided in model. */
763 CIO_CRW_EVENT(2, "Model does not provide secm\n");
764 ret = -EOPNOTSUPP;
765 break;
766 case 0x0102: /* cub adresses incorrect */
767 CIO_CRW_EVENT(2, "Invalid addresses in chsc request block\n");
768 ret = -EINVAL;
769 break;
770 case 0x0103: /* key error */
771 CIO_CRW_EVENT(2, "Access key error in secm\n");
772 ret = -EINVAL;
773 break;
774 case 0x0105: /* error while starting */
775 CIO_CRW_EVENT(2, "Error while starting channel measurement\n");
776 ret = -EIO;
777 break;
778 default:
779 CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
780 secm_area->response.code);
781 ret = -EIO;
782 }
783 return ret;
784}
785
786int
787chsc_secm(struct channel_subsystem *css, int enable)
788{
789 void *secm_area;
790 int ret;
791
792 secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
793 if (!secm_area)
794 return -ENOMEM;
795
796 mutex_lock(&css->mutex);
797 if (enable && !css->cm_enabled) {
798 css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
799 css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
800 if (!css->cub_addr1 || !css->cub_addr2) {
801 free_page((unsigned long)css->cub_addr1);
802 free_page((unsigned long)css->cub_addr2);
803 free_page((unsigned long)secm_area);
804 mutex_unlock(&css->mutex);
805 return -ENOMEM;
806 }
807 }
808 ret = __chsc_do_secm(css, enable, secm_area);
809 if (!ret) {
810 css->cm_enabled = enable;
811 if (css->cm_enabled) {
812 ret = chsc_add_cmg_attr(css);
813 if (ret) {
814 memset(secm_area, 0, PAGE_SIZE);
815 __chsc_do_secm(css, 0, secm_area);
816 css->cm_enabled = 0;
817 }
818 } else
819 chsc_remove_cmg_attr(css);
820 }
Cornelia Huck8c4941c2007-04-27 16:01:38 +0200821 if (!css->cm_enabled) {
Cornelia Huck495a5b42006-03-24 03:15:14 -0800822 free_page((unsigned long)css->cub_addr1);
823 free_page((unsigned long)css->cub_addr2);
824 }
825 mutex_unlock(&css->mutex);
826 free_page((unsigned long)secm_area);
827 return ret;
828}
829
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200830int chsc_determine_channel_path_description(struct chp_id chpid,
831 struct channel_path_desc *desc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832{
833 int ccode, ret;
834
835 struct {
836 struct chsc_header request;
837 u32 : 24;
838 u32 first_chpid : 8;
839 u32 : 24;
840 u32 last_chpid : 8;
841 u32 zeroes1;
842 struct chsc_header response;
843 u32 zeroes2;
844 struct channel_path_desc desc;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100845 } __attribute__ ((packed)) *scpd_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
847 scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
848 if (!scpd_area)
849 return -ENOMEM;
850
Cornelia Huck495a5b42006-03-24 03:15:14 -0800851 scpd_area->request.length = 0x0010;
852 scpd_area->request.code = 0x0002;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200854 scpd_area->first_chpid = chpid.id;
855 scpd_area->last_chpid = chpid.id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
857 ccode = chsc(scpd_area);
858 if (ccode > 0) {
859 ret = (ccode == 3) ? -ENODEV : -EBUSY;
860 goto out;
861 }
862
863 switch (scpd_area->response.code) {
864 case 0x0001: /* Success. */
865 memcpy(desc, &scpd_area->desc,
866 sizeof(struct channel_path_desc));
867 ret = 0;
868 break;
869 case 0x0003: /* Invalid block. */
870 case 0x0007: /* Invalid format. */
871 case 0x0008: /* Other invalid block. */
872 CIO_CRW_EVENT(2, "Error in chsc request block!\n");
873 ret = -EINVAL;
874 break;
875 case 0x0004: /* Command not provided in model. */
876 CIO_CRW_EVENT(2, "Model does not provide scpd\n");
877 ret = -EOPNOTSUPP;
878 break;
879 default:
880 CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
881 scpd_area->response.code);
882 ret = -EIO;
883 }
884out:
885 free_page((unsigned long)scpd_area);
886 return ret;
887}
888
Cornelia Huck495a5b42006-03-24 03:15:14 -0800889static void
890chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
891 struct cmg_chars *chars)
892{
893 switch (chp->cmg) {
894 case 2:
895 case 3:
896 chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
897 GFP_KERNEL);
898 if (chp->cmg_chars) {
899 int i, mask;
900 struct cmg_chars *cmg_chars;
901
902 cmg_chars = chp->cmg_chars;
903 for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
904 mask = 0x80 >> (i + 3);
905 if (cmcv & mask)
906 cmg_chars->values[i] = chars->values[i];
907 else
908 cmg_chars->values[i] = 0;
909 }
910 }
911 break;
912 default:
913 /* No cmg-dependent data. */
914 break;
915 }
916}
917
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200918int chsc_get_channel_measurement_chars(struct channel_path *chp)
Cornelia Huck495a5b42006-03-24 03:15:14 -0800919{
920 int ccode, ret;
921
922 struct {
923 struct chsc_header request;
924 u32 : 24;
925 u32 first_chpid : 8;
926 u32 : 24;
927 u32 last_chpid : 8;
928 u32 zeroes1;
929 struct chsc_header response;
930 u32 zeroes2;
931 u32 not_valid : 1;
932 u32 shared : 1;
933 u32 : 22;
934 u32 chpid : 8;
935 u32 cmcv : 5;
936 u32 : 11;
937 u32 cmgq : 8;
938 u32 cmg : 8;
939 u32 zeroes3;
940 u32 data[NR_MEASUREMENT_CHARS];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100941 } __attribute__ ((packed)) *scmc_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800942
943 scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
944 if (!scmc_area)
945 return -ENOMEM;
946
947 scmc_area->request.length = 0x0010;
948 scmc_area->request.code = 0x0022;
949
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200950 scmc_area->first_chpid = chp->chpid.id;
951 scmc_area->last_chpid = chp->chpid.id;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800952
953 ccode = chsc(scmc_area);
954 if (ccode > 0) {
955 ret = (ccode == 3) ? -ENODEV : -EBUSY;
956 goto out;
957 }
958
959 switch (scmc_area->response.code) {
960 case 0x0001: /* Success. */
961 if (!scmc_area->not_valid) {
962 chp->cmg = scmc_area->cmg;
963 chp->shared = scmc_area->shared;
964 chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
965 (struct cmg_chars *)
966 &scmc_area->data);
967 } else {
968 chp->cmg = -1;
969 chp->shared = -1;
970 }
971 ret = 0;
972 break;
973 case 0x0003: /* Invalid block. */
974 case 0x0007: /* Invalid format. */
975 case 0x0008: /* Invalid bit combination. */
976 CIO_CRW_EVENT(2, "Error in chsc request block!\n");
977 ret = -EINVAL;
978 break;
979 case 0x0004: /* Command not provided. */
980 CIO_CRW_EVENT(2, "Model does not provide scmc\n");
981 ret = -EOPNOTSUPP;
982 break;
983 default:
984 CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
985 scmc_area->response.code);
986 ret = -EIO;
987 }
988out:
989 free_page((unsigned long)scmc_area);
990 return ret;
991}
992
Cornelia Huck4434a382007-07-27 12:29:21 +0200993int __init chsc_alloc_sei_area(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994{
995 sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
996 if (!sei_page)
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200997 CIO_MSG_EVENT(0, "Can't allocate page for processing of "
998 "chsc machine checks!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 return (sei_page ? 0 : -ENOMEM);
1000}
1001
Cornelia Huck4434a382007-07-27 12:29:21 +02001002void __init chsc_free_sei_area(void)
1003{
1004 kfree(sei_page);
1005}
1006
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001007int __init
1008chsc_enable_facility(int operation_code)
1009{
1010 int ret;
1011 struct {
1012 struct chsc_header request;
1013 u8 reserved1:4;
1014 u8 format:4;
1015 u8 reserved2;
1016 u16 operation_code;
1017 u32 reserved3;
1018 u32 reserved4;
1019 u32 operation_data_area[252];
1020 struct chsc_header response;
1021 u32 reserved5:4;
1022 u32 format2:4;
1023 u32 reserved6:24;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +01001024 } __attribute__ ((packed)) *sda_area;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001025
1026 sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
1027 if (!sda_area)
1028 return -ENOMEM;
Cornelia Huck495a5b42006-03-24 03:15:14 -08001029 sda_area->request.length = 0x0400;
1030 sda_area->request.code = 0x0031;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001031 sda_area->operation_code = operation_code;
1032
1033 ret = chsc(sda_area);
1034 if (ret > 0) {
1035 ret = (ret == 3) ? -ENODEV : -EBUSY;
1036 goto out;
1037 }
1038 switch (sda_area->response.code) {
Cornelia Huck15730dd2006-03-06 15:43:02 -08001039 case 0x0001: /* everything ok */
1040 ret = 0;
1041 break;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001042 case 0x0003: /* invalid request block */
1043 case 0x0007:
1044 ret = -EINVAL;
1045 break;
1046 case 0x0004: /* command not provided */
1047 case 0x0101: /* facility not provided */
1048 ret = -EOPNOTSUPP;
1049 break;
Cornelia Huck15730dd2006-03-06 15:43:02 -08001050 default: /* something went wrong */
1051 ret = -EIO;
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001052 }
1053 out:
1054 free_page((unsigned long)sda_area);
1055 return ret;
1056}
1057
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058struct css_general_char css_general_characteristics;
1059struct css_chsc_char css_chsc_characteristics;
1060
1061int __init
1062chsc_determine_css_characteristics(void)
1063{
1064 int result;
1065 struct {
1066 struct chsc_header request;
1067 u32 reserved1;
1068 u32 reserved2;
1069 u32 reserved3;
1070 struct chsc_header response;
1071 u32 reserved4;
1072 u32 general_char[510];
1073 u32 chsc_char[518];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +01001074 } __attribute__ ((packed)) *scsc_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
1076 scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
1077 if (!scsc_area) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +02001078 CIO_MSG_EVENT(0, "Was not able to determine available"
1079 "CHSCs due to no memory.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 return -ENOMEM;
1081 }
1082
Cornelia Huck495a5b42006-03-24 03:15:14 -08001083 scsc_area->request.length = 0x0010;
1084 scsc_area->request.code = 0x0010;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
1086 result = chsc(scsc_area);
1087 if (result) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +02001088 CIO_MSG_EVENT(0, "Was not able to determine available CHSCs, "
1089 "cc=%i.\n", result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 result = -EIO;
1091 goto exit;
1092 }
1093
1094 if (scsc_area->response.code != 1) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +02001095 CIO_MSG_EVENT(0, "Was not able to determine "
1096 "available CHSCs.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 result = -EIO;
1098 goto exit;
1099 }
1100 memcpy(&css_general_characteristics, scsc_area->general_char,
1101 sizeof(css_general_characteristics));
1102 memcpy(&css_chsc_characteristics, scsc_area->chsc_char,
1103 sizeof(css_chsc_characteristics));
1104exit:
1105 free_page ((unsigned long) scsc_area);
1106 return result;
1107}
1108
1109EXPORT_SYMBOL_GPL(css_general_characteristics);
1110EXPORT_SYMBOL_GPL(css_chsc_characteristics);