diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r-- | drivers/s390/cio/chsc.c | 156 |
1 files changed, 112 insertions, 44 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 4d51a7c4eb8b..68e80e2734a4 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * S/390 common I/O routines -- channel subsystem call | 2 | * S/390 common I/O routines -- channel subsystem call |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 1999, 2010 | 4 | * Copyright IBM Corp. 1999,2012 |
5 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | 5 | * Author(s): Ingo Adlung (adlung@de.ibm.com) |
6 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 6 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
7 | * Arnd Bergmann (arndb@de.ibm.com) | 7 | * Arnd Bergmann (arndb@de.ibm.com) |
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/pci.h> | ||
17 | 18 | ||
18 | #include <asm/cio.h> | 19 | #include <asm/cio.h> |
19 | #include <asm/chpid.h> | 20 | #include <asm/chpid.h> |
@@ -260,26 +261,45 @@ __get_chpid_from_lir(void *data) | |||
260 | return (u16) (lir->indesc[0]&0x000000ff); | 261 | return (u16) (lir->indesc[0]&0x000000ff); |
261 | } | 262 | } |
262 | 263 | ||
263 | struct chsc_sei_area { | 264 | struct chsc_sei_nt0_area { |
264 | struct chsc_header request; | 265 | u8 flags; |
266 | u8 vf; /* validity flags */ | ||
267 | u8 rs; /* reporting source */ | ||
268 | u8 cc; /* content code */ | ||
269 | u16 fla; /* full link address */ | ||
270 | u16 rsid; /* reporting source id */ | ||
265 | u32 reserved1; | 271 | u32 reserved1; |
266 | u32 reserved2; | 272 | u32 reserved2; |
267 | u32 reserved3; | ||
268 | struct chsc_header response; | ||
269 | u32 reserved4; | ||
270 | u8 flags; | ||
271 | u8 vf; /* validity flags */ | ||
272 | u8 rs; /* reporting source */ | ||
273 | u8 cc; /* content code */ | ||
274 | u16 fla; /* full link address */ | ||
275 | u16 rsid; /* reporting source id */ | ||
276 | u32 reserved5; | ||
277 | u32 reserved6; | ||
278 | u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */ | ||
279 | /* ccdf has to be big enough for a link-incident record */ | 273 | /* ccdf has to be big enough for a link-incident record */ |
280 | } __attribute__ ((packed)); | 274 | u8 ccdf[PAGE_SIZE - 24 - 16]; /* content-code dependent field */ |
281 | 275 | } __packed; | |
282 | static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | 276 | |
277 | struct chsc_sei_nt2_area { | ||
278 | u8 flags; /* p and v bit */ | ||
279 | u8 reserved1; | ||
280 | u8 reserved2; | ||
281 | u8 cc; /* content code */ | ||
282 | u32 reserved3[13]; | ||
283 | u8 ccdf[PAGE_SIZE - 24 - 56]; /* content-code dependent field */ | ||
284 | } __packed; | ||
285 | |||
286 | #define CHSC_SEI_NT0 0ULL | ||
287 | #define CHSC_SEI_NT2 (1ULL << 61) | ||
288 | |||
289 | struct chsc_sei { | ||
290 | struct chsc_header request; | ||
291 | u32 reserved1; | ||
292 | u64 ntsm; /* notification type mask */ | ||
293 | struct chsc_header response; | ||
294 | u32 reserved2; | ||
295 | union { | ||
296 | struct chsc_sei_nt0_area nt0_area; | ||
297 | struct chsc_sei_nt2_area nt2_area; | ||
298 | u8 nt_area[PAGE_SIZE - 24]; | ||
299 | } u; | ||
300 | } __packed; | ||
301 | |||
302 | static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area) | ||
283 | { | 303 | { |
284 | struct chp_id chpid; | 304 | struct chp_id chpid; |
285 | int id; | 305 | int id; |
@@ -298,7 +318,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | |||
298 | } | 318 | } |
299 | } | 319 | } |
300 | 320 | ||
301 | static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | 321 | static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area) |
302 | { | 322 | { |
303 | struct chp_link link; | 323 | struct chp_link link; |
304 | struct chp_id chpid; | 324 | struct chp_id chpid; |
@@ -330,7 +350,7 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | |||
330 | s390_process_res_acc(&link); | 350 | s390_process_res_acc(&link); |
331 | } | 351 | } |
332 | 352 | ||
333 | static void chsc_process_sei_chp_avail(struct chsc_sei_area *sei_area) | 353 | static void chsc_process_sei_chp_avail(struct chsc_sei_nt0_area *sei_area) |
334 | { | 354 | { |
335 | struct channel_path *chp; | 355 | struct channel_path *chp; |
336 | struct chp_id chpid; | 356 | struct chp_id chpid; |
@@ -366,7 +386,7 @@ struct chp_config_data { | |||
366 | u8 pc; | 386 | u8 pc; |
367 | }; | 387 | }; |
368 | 388 | ||
369 | static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | 389 | static void chsc_process_sei_chp_config(struct chsc_sei_nt0_area *sei_area) |
370 | { | 390 | { |
371 | struct chp_config_data *data; | 391 | struct chp_config_data *data; |
372 | struct chp_id chpid; | 392 | struct chp_id chpid; |
@@ -398,7 +418,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | |||
398 | } | 418 | } |
399 | } | 419 | } |
400 | 420 | ||
401 | static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area) | 421 | static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area) |
402 | { | 422 | { |
403 | int ret; | 423 | int ret; |
404 | 424 | ||
@@ -412,13 +432,26 @@ static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area) | |||
412 | " failed (rc=%d).\n", ret); | 432 | " failed (rc=%d).\n", ret); |
413 | } | 433 | } |
414 | 434 | ||
415 | static void chsc_process_sei(struct chsc_sei_area *sei_area) | 435 | static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) |
416 | { | 436 | { |
417 | /* Check if we might have lost some information. */ | 437 | #ifdef CONFIG_PCI |
418 | if (sei_area->flags & 0x40) { | 438 | switch (sei_area->cc) { |
419 | CIO_CRW_EVENT(2, "chsc: event overflow\n"); | 439 | case 1: |
420 | css_schedule_eval_all(); | 440 | zpci_event_error(sei_area->ccdf); |
441 | break; | ||
442 | case 2: | ||
443 | zpci_event_availability(sei_area->ccdf); | ||
444 | break; | ||
445 | default: | ||
446 | CIO_CRW_EVENT(2, "chsc: unhandled sei content code %d\n", | ||
447 | sei_area->cc); | ||
448 | break; | ||
421 | } | 449 | } |
450 | #endif | ||
451 | } | ||
452 | |||
453 | static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) | ||
454 | { | ||
422 | /* which kind of information was stored? */ | 455 | /* which kind of information was stored? */ |
423 | switch (sei_area->cc) { | 456 | switch (sei_area->cc) { |
424 | case 1: /* link incident*/ | 457 | case 1: /* link incident*/ |
@@ -443,9 +476,51 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area) | |||
443 | } | 476 | } |
444 | } | 477 | } |
445 | 478 | ||
479 | static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) | ||
480 | { | ||
481 | do { | ||
482 | memset(sei, 0, sizeof(*sei)); | ||
483 | sei->request.length = 0x0010; | ||
484 | sei->request.code = 0x000e; | ||
485 | sei->ntsm = ntsm; | ||
486 | |||
487 | if (chsc(sei)) | ||
488 | break; | ||
489 | |||
490 | if (sei->response.code == 0x0001) { | ||
491 | CIO_CRW_EVENT(2, "chsc: sei successful\n"); | ||
492 | |||
493 | /* Check if we might have lost some information. */ | ||
494 | if (sei->u.nt0_area.flags & 0x40) { | ||
495 | CIO_CRW_EVENT(2, "chsc: event overflow\n"); | ||
496 | css_schedule_eval_all(); | ||
497 | } | ||
498 | |||
499 | switch (sei->ntsm) { | ||
500 | case CHSC_SEI_NT0: | ||
501 | chsc_process_sei_nt0(&sei->u.nt0_area); | ||
502 | return 1; | ||
503 | case CHSC_SEI_NT2: | ||
504 | chsc_process_sei_nt2(&sei->u.nt2_area); | ||
505 | return 1; | ||
506 | default: | ||
507 | CIO_CRW_EVENT(2, "chsc: unhandled nt (nt=%08Lx)\n", | ||
508 | sei->ntsm); | ||
509 | return 0; | ||
510 | } | ||
511 | } else { | ||
512 | CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", | ||
513 | sei->response.code); | ||
514 | break; | ||
515 | } | ||
516 | } while (sei->u.nt0_area.flags & 0x80); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
446 | static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) | 521 | static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) |
447 | { | 522 | { |
448 | struct chsc_sei_area *sei_area; | 523 | struct chsc_sei *sei; |
449 | 524 | ||
450 | if (overflow) { | 525 | if (overflow) { |
451 | css_schedule_eval_all(); | 526 | css_schedule_eval_all(); |
@@ -459,25 +534,18 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) | |||
459 | return; | 534 | return; |
460 | /* Access to sei_page is serialized through machine check handler | 535 | /* Access to sei_page is serialized through machine check handler |
461 | * thread, so no need for locking. */ | 536 | * thread, so no need for locking. */ |
462 | sei_area = sei_page; | 537 | sei = sei_page; |
463 | 538 | ||
464 | CIO_TRACE_EVENT(2, "prcss"); | 539 | CIO_TRACE_EVENT(2, "prcss"); |
465 | do { | ||
466 | memset(sei_area, 0, sizeof(*sei_area)); | ||
467 | sei_area->request.length = 0x0010; | ||
468 | sei_area->request.code = 0x000e; | ||
469 | if (chsc(sei_area)) | ||
470 | break; | ||
471 | 540 | ||
472 | if (sei_area->response.code == 0x0001) { | 541 | /* |
473 | CIO_CRW_EVENT(4, "chsc: sei successful\n"); | 542 | * The ntsm does not allow to select NT0 and NT2 together. We need to |
474 | chsc_process_sei(sei_area); | 543 | * first check for NT2, than additionally for NT0... |
475 | } else { | 544 | */ |
476 | CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", | 545 | #ifdef CONFIG_PCI |
477 | sei_area->response.code); | 546 | if (!__chsc_process_crw(sei, CHSC_SEI_NT2)) |
478 | break; | 547 | #endif |
479 | } | 548 | __chsc_process_crw(sei, CHSC_SEI_NT0); |
480 | } while (sei_area->flags & 0x80); | ||
481 | } | 549 | } |
482 | 550 | ||
483 | void chsc_chp_online(struct chp_id chpid) | 551 | void chsc_chp_online(struct chp_id chpid) |