blob: 1c0f5db94c7b8bffa910927007444d735ec4603d [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 *
Cornelia Huckc820de32008-07-14 09:58:45 +02005 * Copyright IBM Corp. 1999,2008
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Author(s): Ingo Adlung (adlung@de.ibm.com)
Cornelia Huck4ce3b302006-01-14 13:21:04 -08007 * Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * Arnd Bergmann (arndb@de.ibm.com)
9 */
10
11#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/slab.h>
13#include <linux/init.h>
14#include <linux/device.h>
15
16#include <asm/cio.h>
Peter Oberparleitere5854a52007-04-27 16:01:31 +020017#include <asm/chpid.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
19#include "css.h"
20#include "cio.h"
21#include "cio_debug.h"
22#include "ioasm.h"
Peter Oberparleitere6b6e102007-04-27 16:01:28 +020023#include "chp.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "chsc.h"
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026static void *sei_page;
27
Cornelia Huckb9c9a212008-02-05 16:50:34 +010028static int chsc_error_from_response(int response)
29{
30 switch (response) {
31 case 0x0001:
32 return 0;
33 case 0x0002:
34 case 0x0003:
35 case 0x0006:
36 case 0x0007:
37 case 0x0008:
38 case 0x000a:
39 return -EINVAL;
40 case 0x0004:
41 return -EOPNOTSUPP;
42 default:
43 return -EIO;
44 }
45}
46
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020047struct chsc_ssd_area {
48 struct chsc_header request;
49 u16 :10;
50 u16 ssid:2;
51 u16 :4;
52 u16 f_sch; /* first subchannel */
53 u16 :16;
54 u16 l_sch; /* last subchannel */
55 u32 :32;
56 struct chsc_header response;
57 u32 :32;
58 u8 sch_valid : 1;
59 u8 dev_valid : 1;
60 u8 st : 3; /* subchannel type */
61 u8 zeroes : 3;
62 u8 unit_addr; /* unit address */
63 u16 devno; /* device number */
64 u8 path_mask;
65 u8 fla_valid_mask;
66 u16 sch; /* subchannel */
67 u8 chpid[8]; /* chpids 0-7 */
68 u16 fla[8]; /* full link addresses 0-7 */
69} __attribute__ ((packed));
70
71int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
Linus Torvalds1da177e2005-04-16 15:20:36 -070072{
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020073 unsigned long page;
74 struct chsc_ssd_area *ssd_area;
75 int ccode;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 int ret;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020077 int i;
78 int mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020080 page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 if (!page)
82 return -ENOMEM;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020083 ssd_area = (struct chsc_ssd_area *) page;
84 ssd_area->request.length = 0x0010;
85 ssd_area->request.code = 0x0004;
86 ssd_area->ssid = schid.ssid;
87 ssd_area->f_sch = schid.sch_no;
88 ssd_area->l_sch = schid.sch_no;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +020089
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020090 ccode = chsc(ssd_area);
91 /* Check response. */
92 if (ccode > 0) {
93 ret = (ccode == 3) ? -ENODEV : -EBUSY;
94 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +010096 ret = chsc_error_from_response(ssd_area->response.code);
97 if (ret != 0) {
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020098 CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
99 schid.ssid, schid.sch_no,
100 ssd_area->response.code);
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200101 goto out_free;
102 }
103 if (!ssd_area->sch_valid) {
104 ret = -ENODEV;
105 goto out_free;
106 }
107 /* Copy data */
108 ret = 0;
109 memset(ssd, 0, sizeof(struct chsc_ssd_info));
Cornelia Huckb279a4f2008-01-26 14:10:45 +0100110 if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
111 (ssd_area->st != SUBCHANNEL_TYPE_MSG))
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200112 goto out_free;
113 ssd->path_mask = ssd_area->path_mask;
114 ssd->fla_valid_mask = ssd_area->fla_valid_mask;
115 for (i = 0; i < 8; i++) {
116 mask = 0x80 >> i;
117 if (ssd_area->path_mask & mask) {
118 chp_id_init(&ssd->chpid[i]);
119 ssd->chpid[i].id = ssd_area->chpid[i];
120 }
121 if (ssd_area->fla_valid_mask & mask)
122 ssd->fla[i] = ssd_area->fla[i];
123 }
124out_free:
125 free_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 return ret;
127}
128
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100129static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
Cornelia Huck2ec22982006-12-08 15:54:26 +0100131 spin_lock_irq(sch->lock);
Cornelia Huckc820de32008-07-14 09:58:45 +0200132 if (sch->driver && sch->driver->chp_event)
133 if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 goto out_unreg;
Cornelia Huck2ec22982006-12-08 15:54:26 +0100135 spin_unlock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 return 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138out_unreg:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 sch->lpm = 0;
Stefan Bader387b7342007-04-27 16:01:33 +0200140 spin_unlock_irq(sch->lock);
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200141 css_schedule_eval(sch->schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 return 0;
143}
144
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200145void chsc_chp_offline(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146{
147 char dbf_txt[15];
148
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200149 sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 CIO_TRACE_EVENT(2, dbf_txt);
151
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200152 if (chp_get_status(chpid) <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 return;
Cornelia Huck22806dc2008-04-17 07:45:59 +0200154 /* Wait until previous actions have settled. */
155 css_wait_for_slow_path();
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100156 for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}
158
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100159static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800161 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800162 /*
163 * We don't know the device yet, but since a path
164 * may be available now to the device we'll have
165 * to do recognition again.
166 * Since we don't have any idea about which chpid
167 * that beast may be on we'll have to do a stsch
168 * on all devices, grr...
169 */
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800170 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800171 /* We're through */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200172 return -ENXIO;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800173
174 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200175 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800176 return 0;
177}
178
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100179static int __s390_process_res_acc(struct subchannel *sch, void *data)
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800180{
Cornelia Huck2ec22982006-12-08 15:54:26 +0100181 spin_lock_irq(sch->lock);
Cornelia Huckc820de32008-07-14 09:58:45 +0200182 if (sch->driver && sch->driver->chp_event)
183 sch->driver->chp_event(sch, data, CHP_ONLINE);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100184 spin_unlock_irq(sch->lock);
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100185
Peter Oberparleiterdd9963f2006-09-20 15:59:54 +0200186 return 0;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800187}
188
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200189static void s390_process_res_acc (struct res_acc_data *res_data)
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800190{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 char dbf_txt[15];
192
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200193 sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
194 res_data->chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 CIO_TRACE_EVENT( 2, dbf_txt);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800196 if (res_data->fla != 0) {
197 sprintf(dbf_txt, "fla%x", res_data->fla);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 CIO_TRACE_EVENT( 2, dbf_txt);
199 }
Cornelia Huck22806dc2008-04-17 07:45:59 +0200200 /* Wait until previous actions have settled. */
201 css_wait_for_slow_path();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 /*
203 * I/O resources may have become accessible.
204 * Scan through all subchannels that may be concerned and
205 * do a validation on those.
206 * The more information we have (info), the less scanning
207 * will we have to do.
208 */
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100209 for_each_subchannel_staged(__s390_process_res_acc,
210 s390_process_res_acc_new_sch, res_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211}
212
213static int
214__get_chpid_from_lir(void *data)
215{
216 struct lir {
217 u8 iq;
218 u8 ic;
219 u16 sci;
220 /* incident-node descriptor */
221 u32 indesc[28];
222 /* attached-node descriptor */
223 u32 andesc[28];
224 /* incident-specific information */
225 u32 isinfo[28];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100226 } __attribute__ ((packed)) *lir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
Cornelia Huck12975ae2006-10-11 15:31:47 +0200228 lir = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 if (!(lir->iq&0x80))
230 /* NULL link incident record */
231 return -EINVAL;
232 if (!(lir->indesc[0]&0xc0000000))
233 /* node descriptor not valid */
234 return -EINVAL;
235 if (!(lir->indesc[0]&0x10000000))
236 /* don't handle device-type nodes - FIXME */
237 return -EINVAL;
238 /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
239
240 return (u16) (lir->indesc[0]&0x000000ff);
241}
242
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100243struct chsc_sei_area {
244 struct chsc_header request;
245 u32 reserved1;
246 u32 reserved2;
247 u32 reserved3;
248 struct chsc_header response;
249 u32 reserved4;
250 u8 flags;
251 u8 vf; /* validity flags */
252 u8 rs; /* reporting source */
253 u8 cc; /* content code */
254 u16 fla; /* full link address */
255 u16 rsid; /* reporting source id */
256 u32 reserved5;
257 u32 reserved6;
258 u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */
259 /* ccdf has to be big enough for a link-incident record */
260} __attribute__ ((packed));
261
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200262static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200264 struct chp_id chpid;
265 int id;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100266
267 CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
268 sei_area->rs, sei_area->rsid);
269 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200270 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200271 id = __get_chpid_from_lir(sei_area->ccdf);
272 if (id < 0)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100273 CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200274 else {
275 chp_id_init(&chpid);
276 chpid.id = id;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200277 chsc_chp_offline(chpid);
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200278 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100279}
280
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200281static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100282{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800283 struct res_acc_data res_data;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200284 struct chp_id chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100285 int status;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100286
287 CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
288 "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
289 if (sei_area->rs != 4)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200290 return;
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200291 chp_id_init(&chpid);
292 chpid.id = sei_area->rsid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100293 /* allocate a new channel path structure, if needed */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200294 status = chp_get_status(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100295 if (status < 0)
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200296 chp_new(chpid);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100297 else if (!status)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200298 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100299 memset(&res_data, 0, sizeof(struct res_acc_data));
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200300 res_data.chpid = chpid;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100301 if ((sei_area->vf & 0xc0) != 0) {
302 res_data.fla = sei_area->fla;
303 if ((sei_area->vf & 0xc0) == 0xc0)
304 /* full link address */
305 res_data.fla_mask = 0xffff;
306 else
307 /* link address */
308 res_data.fla_mask = 0xff00;
309 }
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200310 s390_process_res_acc(&res_data);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100311}
312
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200313struct chp_config_data {
314 u8 map[32];
315 u8 op;
316 u8 pc;
317};
318
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200319static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200320{
321 struct chp_config_data *data;
322 struct chp_id chpid;
323 int num;
324
325 CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
326 if (sei_area->rs != 0)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200327 return;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200328 data = (struct chp_config_data *) &(sei_area->ccdf);
329 chp_id_init(&chpid);
330 for (num = 0; num <= __MAX_CHPID; num++) {
331 if (!chp_test_bit(data->map, num))
332 continue;
333 chpid.id = num;
334 printk(KERN_WARNING "cio: processing configure event %d for "
335 "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
336 switch (data->op) {
337 case 0:
338 chp_cfg_schedule(chpid, 1);
339 break;
340 case 1:
341 chp_cfg_schedule(chpid, 0);
342 break;
343 case 2:
344 chp_cfg_cancel_deconfigure(chpid);
345 break;
346 }
347 }
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200348}
349
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200350static void chsc_process_sei(struct chsc_sei_area *sei_area)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100351{
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100352 /* Check if we might have lost some information. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200353 if (sei_area->flags & 0x40) {
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100354 CIO_CRW_EVENT(2, "chsc: event overflow\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200355 css_schedule_eval_all();
356 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100357 /* which kind of information was stored? */
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100358 switch (sei_area->cc) {
359 case 1: /* link incident*/
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200360 chsc_process_sei_link_incident(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100361 break;
362 case 2: /* i/o resource accessibiliy */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200363 chsc_process_sei_res_acc(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100364 break;
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200365 case 8: /* channel-path-configuration notification */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200366 chsc_process_sei_chp_config(sei_area);
Peter Oberparleitere5854a52007-04-27 16:01:31 +0200367 break;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100368 default: /* other stuff */
369 CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
370 sei_area->cc);
371 break;
372 }
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100373}
374
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200375void chsc_process_crw(void)
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100376{
377 struct chsc_sei_area *sei_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379 if (!sei_page)
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200380 return;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100381 /* Access to sei_page is serialized through machine check handler
382 * thread, so no need for locking. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 sei_area = sei_page;
384
385 CIO_TRACE_EVENT( 2, "prcss");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 memset(sei_area, 0, sizeof(*sei_area));
Cornelia Huck495a5b42006-03-24 03:15:14 -0800388 sei_area->request.length = 0x0010;
389 sei_area->request.code = 0x000e;
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100390 if (chsc(sei_area))
391 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100393 if (sei_area->response.code == 0x0001) {
394 CIO_CRW_EVENT(4, "chsc: sei successful\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200395 chsc_process_sei(sei_area);
Peter Oberparleiter184357a2007-02-05 21:17:42 +0100396 } else {
397 CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 sei_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 break;
400 }
401 } while (sei_area->flags & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402}
403
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200404void chsc_chp_online(struct chp_id chpid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 char dbf_txt[15];
Cornelia Huckc820de32008-07-14 09:58:45 +0200407 struct res_acc_data res_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200409 sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 CIO_TRACE_EVENT(2, dbf_txt);
411
Cornelia Huck22806dc2008-04-17 07:45:59 +0200412 if (chp_get_status(chpid) != 0) {
Cornelia Huckc820de32008-07-14 09:58:45 +0200413 memset(&res_data, 0, sizeof(struct res_acc_data));
414 res_data.chpid = chpid;
Cornelia Huck22806dc2008-04-17 07:45:59 +0200415 /* Wait until previous actions have settled. */
416 css_wait_for_slow_path();
Cornelia Huckc820de32008-07-14 09:58:45 +0200417 for_each_subchannel_staged(__s390_process_res_acc, NULL,
418 &res_data);
Cornelia Huck22806dc2008-04-17 07:45:59 +0200419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420}
421
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200422static void __s390_subchannel_vary_chpid(struct subchannel *sch,
423 struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 unsigned long flags;
Cornelia Huckc820de32008-07-14 09:58:45 +0200426 struct res_acc_data res_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Cornelia Huckc820de32008-07-14 09:58:45 +0200428 memset(&res_data, 0, sizeof(struct res_acc_data));
429 res_data.chpid = chpid;
Cornelia Huck2ec22982006-12-08 15:54:26 +0100430 spin_lock_irqsave(sch->lock, flags);
Cornelia Huckc820de32008-07-14 09:58:45 +0200431 if (sch->driver && sch->driver->chp_event)
432 sch->driver->chp_event(sch, &res_data,
433 on ? CHP_VARY_ON : CHP_VARY_OFF);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100434 spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435}
436
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100437static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438{
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100439 struct chp_id *chpid = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
441 __s390_subchannel_vary_chpid(sch, *chpid, 0);
442 return 0;
443}
444
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100445static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446{
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100447 struct chp_id *chpid = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448
449 __s390_subchannel_vary_chpid(sch, *chpid, 1);
450 return 0;
451}
452
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800453static int
454__s390_vary_chpid_on(struct subchannel_id schid, void *data)
455{
456 struct schib schib;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800457
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800458 if (stsch_err(schid, &schib))
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800459 /* We're through */
460 return -ENXIO;
461 /* Put it on the slow path. */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200462 css_schedule_eval(schid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800463 return 0;
464}
465
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200466/**
467 * chsc_chp_vary - propagate channel-path vary operation to subchannels
468 * @chpid: channl-path ID
469 * @on: non-zero for vary online, zero for vary offline
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 */
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200471int chsc_chp_vary(struct chp_id chpid, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
Cornelia Huck22806dc2008-04-17 07:45:59 +0200473 /* Wait until previous actions have settled. */
474 css_wait_for_slow_path();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 /*
476 * Redo PathVerification on the devices the chpid connects to
477 */
478
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800479 if (on)
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100480 for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
481 __s390_vary_chpid_on, &chpid);
482 else
483 for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
484 NULL, &chpid);
485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 return 0;
487}
488
Cornelia Huck495a5b42006-03-24 03:15:14 -0800489static void
490chsc_remove_cmg_attr(struct channel_subsystem *css)
491{
492 int i;
493
494 for (i = 0; i <= __MAX_CHPID; i++) {
495 if (!css->chps[i])
496 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200497 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800498 }
499}
500
501static int
502chsc_add_cmg_attr(struct channel_subsystem *css)
503{
504 int i, ret;
505
506 ret = 0;
507 for (i = 0; i <= __MAX_CHPID; i++) {
508 if (!css->chps[i])
509 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200510 ret = chp_add_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800511 if (ret)
512 goto cleanup;
513 }
514 return ret;
515cleanup:
516 for (--i; i >= 0; i--) {
517 if (!css->chps[i])
518 continue;
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200519 chp_remove_cmg_attr(css->chps[i]);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800520 }
521 return ret;
522}
523
Cornelia Huck495a5b42006-03-24 03:15:14 -0800524static int
525__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
526{
527 struct {
528 struct chsc_header request;
529 u32 operation_code : 2;
530 u32 : 30;
531 u32 key : 4;
532 u32 : 28;
533 u32 zeroes1;
534 u32 cub_addr1;
535 u32 zeroes2;
536 u32 cub_addr2;
537 u32 reserved[13];
538 struct chsc_header response;
539 u32 status : 8;
540 u32 : 4;
541 u32 fmt : 4;
542 u32 : 16;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100543 } __attribute__ ((packed)) *secm_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800544 int ret, ccode;
545
546 secm_area = page;
547 secm_area->request.length = 0x0050;
548 secm_area->request.code = 0x0016;
549
550 secm_area->key = PAGE_DEFAULT_KEY;
551 secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
552 secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;
553
554 secm_area->operation_code = enable ? 0 : 1;
555
556 ccode = chsc(secm_area);
557 if (ccode > 0)
558 return (ccode == 3) ? -ENODEV : -EBUSY;
559
560 switch (secm_area->response.code) {
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100561 case 0x0102:
562 case 0x0103:
Cornelia Huck495a5b42006-03-24 03:15:14 -0800563 ret = -EINVAL;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800564 default:
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100565 ret = chsc_error_from_response(secm_area->response.code);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800566 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100567 if (ret != 0)
568 CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
569 secm_area->response.code);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800570 return ret;
571}
572
573int
574chsc_secm(struct channel_subsystem *css, int enable)
575{
576 void *secm_area;
577 int ret;
578
579 secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
580 if (!secm_area)
581 return -ENOMEM;
582
Cornelia Huck495a5b42006-03-24 03:15:14 -0800583 if (enable && !css->cm_enabled) {
584 css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
585 css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
586 if (!css->cub_addr1 || !css->cub_addr2) {
587 free_page((unsigned long)css->cub_addr1);
588 free_page((unsigned long)css->cub_addr2);
589 free_page((unsigned long)secm_area);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800590 return -ENOMEM;
591 }
592 }
593 ret = __chsc_do_secm(css, enable, secm_area);
594 if (!ret) {
595 css->cm_enabled = enable;
596 if (css->cm_enabled) {
597 ret = chsc_add_cmg_attr(css);
598 if (ret) {
599 memset(secm_area, 0, PAGE_SIZE);
600 __chsc_do_secm(css, 0, secm_area);
601 css->cm_enabled = 0;
602 }
603 } else
604 chsc_remove_cmg_attr(css);
605 }
Cornelia Huck8c4941c2007-04-27 16:01:38 +0200606 if (!css->cm_enabled) {
Cornelia Huck495a5b42006-03-24 03:15:14 -0800607 free_page((unsigned long)css->cub_addr1);
608 free_page((unsigned long)css->cub_addr2);
609 }
Cornelia Huck495a5b42006-03-24 03:15:14 -0800610 free_page((unsigned long)secm_area);
611 return ret;
612}
613
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200614int chsc_determine_channel_path_description(struct chp_id chpid,
615 struct channel_path_desc *desc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616{
617 int ccode, ret;
618
619 struct {
620 struct chsc_header request;
621 u32 : 24;
622 u32 first_chpid : 8;
623 u32 : 24;
624 u32 last_chpid : 8;
625 u32 zeroes1;
626 struct chsc_header response;
627 u32 zeroes2;
628 struct channel_path_desc desc;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100629 } __attribute__ ((packed)) *scpd_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
631 scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
632 if (!scpd_area)
633 return -ENOMEM;
634
Cornelia Huck495a5b42006-03-24 03:15:14 -0800635 scpd_area->request.length = 0x0010;
636 scpd_area->request.code = 0x0002;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200638 scpd_area->first_chpid = chpid.id;
639 scpd_area->last_chpid = chpid.id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
641 ccode = chsc(scpd_area);
642 if (ccode > 0) {
643 ret = (ccode == 3) ? -ENODEV : -EBUSY;
644 goto out;
645 }
646
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100647 ret = chsc_error_from_response(scpd_area->response.code);
648 if (ret == 0)
649 /* Success. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 memcpy(desc, &scpd_area->desc,
651 sizeof(struct channel_path_desc));
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100652 else
653 CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 scpd_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655out:
656 free_page((unsigned long)scpd_area);
657 return ret;
658}
659
Cornelia Huck495a5b42006-03-24 03:15:14 -0800660static void
661chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
662 struct cmg_chars *chars)
663{
664 switch (chp->cmg) {
665 case 2:
666 case 3:
667 chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
668 GFP_KERNEL);
669 if (chp->cmg_chars) {
670 int i, mask;
671 struct cmg_chars *cmg_chars;
672
673 cmg_chars = chp->cmg_chars;
674 for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
675 mask = 0x80 >> (i + 3);
676 if (cmcv & mask)
677 cmg_chars->values[i] = chars->values[i];
678 else
679 cmg_chars->values[i] = 0;
680 }
681 }
682 break;
683 default:
684 /* No cmg-dependent data. */
685 break;
686 }
687}
688
Peter Oberparleitere6b6e102007-04-27 16:01:28 +0200689int chsc_get_channel_measurement_chars(struct channel_path *chp)
Cornelia Huck495a5b42006-03-24 03:15:14 -0800690{
691 int ccode, ret;
692
693 struct {
694 struct chsc_header request;
695 u32 : 24;
696 u32 first_chpid : 8;
697 u32 : 24;
698 u32 last_chpid : 8;
699 u32 zeroes1;
700 struct chsc_header response;
701 u32 zeroes2;
702 u32 not_valid : 1;
703 u32 shared : 1;
704 u32 : 22;
705 u32 chpid : 8;
706 u32 cmcv : 5;
707 u32 : 11;
708 u32 cmgq : 8;
709 u32 cmg : 8;
710 u32 zeroes3;
711 u32 data[NR_MEASUREMENT_CHARS];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100712 } __attribute__ ((packed)) *scmc_area;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800713
714 scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
715 if (!scmc_area)
716 return -ENOMEM;
717
718 scmc_area->request.length = 0x0010;
719 scmc_area->request.code = 0x0022;
720
Peter Oberparleiterf86635f2007-04-27 16:01:26 +0200721 scmc_area->first_chpid = chp->chpid.id;
722 scmc_area->last_chpid = chp->chpid.id;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800723
724 ccode = chsc(scmc_area);
725 if (ccode > 0) {
726 ret = (ccode == 3) ? -ENODEV : -EBUSY;
727 goto out;
728 }
729
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100730 ret = chsc_error_from_response(scmc_area->response.code);
731 if (ret == 0) {
732 /* Success. */
Cornelia Huck495a5b42006-03-24 03:15:14 -0800733 if (!scmc_area->not_valid) {
734 chp->cmg = scmc_area->cmg;
735 chp->shared = scmc_area->shared;
736 chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
737 (struct cmg_chars *)
738 &scmc_area->data);
739 } else {
740 chp->cmg = -1;
741 chp->shared = -1;
742 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100743 } else {
744 CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n",
Cornelia Huck495a5b42006-03-24 03:15:14 -0800745 scmc_area->response.code);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800746 }
747out:
748 free_page((unsigned long)scmc_area);
749 return ret;
750}
751
Cornelia Huck4434a382007-07-27 12:29:21 +0200752int __init chsc_alloc_sei_area(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753{
754 sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
755 if (!sei_page)
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200756 CIO_MSG_EVENT(0, "Can't allocate page for processing of "
757 "chsc machine checks!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 return (sei_page ? 0 : -ENOMEM);
759}
760
Cornelia Huck4434a382007-07-27 12:29:21 +0200761void __init chsc_free_sei_area(void)
762{
763 kfree(sei_page);
764}
765
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800766int __init
767chsc_enable_facility(int operation_code)
768{
769 int ret;
770 struct {
771 struct chsc_header request;
772 u8 reserved1:4;
773 u8 format:4;
774 u8 reserved2;
775 u16 operation_code;
776 u32 reserved3;
777 u32 reserved4;
778 u32 operation_data_area[252];
779 struct chsc_header response;
780 u32 reserved5:4;
781 u32 format2:4;
782 u32 reserved6:24;
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100783 } __attribute__ ((packed)) *sda_area;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800784
785 sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
786 if (!sda_area)
787 return -ENOMEM;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800788 sda_area->request.length = 0x0400;
789 sda_area->request.code = 0x0031;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800790 sda_area->operation_code = operation_code;
791
792 ret = chsc(sda_area);
793 if (ret > 0) {
794 ret = (ret == 3) ? -ENODEV : -EBUSY;
795 goto out;
796 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100797
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800798 switch (sda_area->response.code) {
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100799 case 0x0101:
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800800 ret = -EOPNOTSUPP;
801 break;
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100802 default:
803 ret = chsc_error_from_response(sda_area->response.code);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800804 }
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100805 if (ret != 0)
806 CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
807 operation_code, sda_area->response.code);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800808 out:
809 free_page((unsigned long)sda_area);
810 return ret;
811}
812
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813struct css_general_char css_general_characteristics;
814struct css_chsc_char css_chsc_characteristics;
815
816int __init
817chsc_determine_css_characteristics(void)
818{
819 int result;
820 struct {
821 struct chsc_header request;
822 u32 reserved1;
823 u32 reserved2;
824 u32 reserved3;
825 struct chsc_header response;
826 u32 reserved4;
827 u32 general_char[510];
828 u32 chsc_char[518];
Peter Oberparleiter0f008aa2007-02-05 21:17:40 +0100829 } __attribute__ ((packed)) *scsc_area;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
831 scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100832 if (!scsc_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Cornelia Huck495a5b42006-03-24 03:15:14 -0800835 scsc_area->request.length = 0x0010;
836 scsc_area->request.code = 0x0010;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 result = chsc(scsc_area);
839 if (result) {
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100840 result = (result == 3) ? -ENODEV : -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 goto exit;
842 }
843
Cornelia Huckb9c9a212008-02-05 16:50:34 +0100844 result = chsc_error_from_response(scsc_area->response.code);
845 if (result == 0) {
846 memcpy(&css_general_characteristics, scsc_area->general_char,
847 sizeof(css_general_characteristics));
848 memcpy(&css_chsc_characteristics, scsc_area->chsc_char,
849 sizeof(css_chsc_characteristics));
850 } else
851 CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
852 scsc_area->response.code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853exit:
854 free_page ((unsigned long) scsc_area);
855 return result;
856}
857
858EXPORT_SYMBOL_GPL(css_general_characteristics);
859EXPORT_SYMBOL_GPL(css_chsc_characteristics);