diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2012-11-29 08:34:48 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-11-30 11:47:24 -0500 |
commit | cbc0dd1f856b52b59c2c73a477b6cb210c8c66ad (patch) | |
tree | 064ceb4d1b2acb8716867b2a3fb65ae05bef1a31 | |
parent | 828b35f60eb0148f994bb13e328df94578b07142 (diff) |
s390/pci: CHSC PCI support for error and availability events
Add CHSC store-event-information support for PCI (notfication type 2)
and report error and availability events to the PCI architecture layer.
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/pci.h | 4 | ||||
-rw-r--r-- | arch/s390/pci/Makefile | 3 | ||||
-rw-r--r-- | arch/s390/pci/pci_event.c | 93 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.c | 156 |
4 files changed, 211 insertions, 45 deletions
diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index e9dc0090ac43..d3597dcfec33 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h | |||
@@ -127,6 +127,10 @@ void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *); | |||
127 | int zpci_msihash_init(void); | 127 | int zpci_msihash_init(void); |
128 | void zpci_msihash_exit(void); | 128 | void zpci_msihash_exit(void); |
129 | 129 | ||
130 | /* Error handling and recovery */ | ||
131 | void zpci_event_error(void *); | ||
132 | void zpci_event_availability(void *); | ||
133 | |||
130 | /* Helpers */ | 134 | /* Helpers */ |
131 | struct zpci_dev *get_zdev(struct pci_dev *); | 135 | struct zpci_dev *get_zdev(struct pci_dev *); |
132 | struct zpci_dev *get_zdev_by_fid(u32); | 136 | struct zpci_dev *get_zdev_by_fid(u32); |
diff --git a/arch/s390/pci/Makefile b/arch/s390/pci/Makefile index 4590596d8b25..7e36f42ba086 100644 --- a/arch/s390/pci/Makefile +++ b/arch/s390/pci/Makefile | |||
@@ -2,4 +2,5 @@ | |||
2 | # Makefile for the s390 PCI subsystem. | 2 | # Makefile for the s390 PCI subsystem. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_msi.o | 5 | obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_msi.o \ |
6 | pci_event.o | ||
diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c new file mode 100644 index 000000000000..dbed8cd3370c --- /dev/null +++ b/arch/s390/pci/pci_event.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2012 | ||
3 | * | ||
4 | * Author(s): | ||
5 | * Jan Glauber <jang@linux.vnet.ibm.com> | ||
6 | */ | ||
7 | |||
8 | #define COMPONENT "zPCI" | ||
9 | #define pr_fmt(fmt) COMPONENT ": " fmt | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/pci.h> | ||
13 | |||
14 | /* Content Code Description for PCI Function Error */ | ||
15 | struct zpci_ccdf_err { | ||
16 | u32 reserved1; | ||
17 | u32 fh; /* function handle */ | ||
18 | u32 fid; /* function id */ | ||
19 | u32 ett : 4; /* expected table type */ | ||
20 | u32 mvn : 12; /* MSI vector number */ | ||
21 | u32 dmaas : 8; /* DMA address space */ | ||
22 | u32 : 6; | ||
23 | u32 q : 1; /* event qualifier */ | ||
24 | u32 rw : 1; /* read/write */ | ||
25 | u64 faddr; /* failing address */ | ||
26 | u32 reserved3; | ||
27 | u16 reserved4; | ||
28 | u16 pec; /* PCI event code */ | ||
29 | } __packed; | ||
30 | |||
31 | /* Content Code Description for PCI Function Availability */ | ||
32 | struct zpci_ccdf_avail { | ||
33 | u32 reserved1; | ||
34 | u32 fh; /* function handle */ | ||
35 | u32 fid; /* function id */ | ||
36 | u32 reserved2; | ||
37 | u32 reserved3; | ||
38 | u32 reserved4; | ||
39 | u32 reserved5; | ||
40 | u16 reserved6; | ||
41 | u16 pec; /* PCI event code */ | ||
42 | } __packed; | ||
43 | |||
44 | static void zpci_event_log_err(struct zpci_ccdf_err *ccdf) | ||
45 | { | ||
46 | struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); | ||
47 | |||
48 | dev_err(&zdev->pdev->dev, "event code: 0x%x\n", ccdf->pec); | ||
49 | } | ||
50 | |||
51 | static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf) | ||
52 | { | ||
53 | struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); | ||
54 | |||
55 | pr_err("%s%s: availability event: fh: 0x%x fid: 0x%x event code: 0x%x reason:", | ||
56 | (zdev) ? dev_driver_string(&zdev->pdev->dev) : "?", | ||
57 | (zdev) ? dev_name(&zdev->pdev->dev) : "?", | ||
58 | ccdf->fh, ccdf->fid, ccdf->pec); | ||
59 | print_hex_dump(KERN_CONT, "ccdf", DUMP_PREFIX_OFFSET, | ||
60 | 16, 1, ccdf, sizeof(*ccdf), false); | ||
61 | |||
62 | switch (ccdf->pec) { | ||
63 | case 0x0301: | ||
64 | zpci_enable_device(zdev); | ||
65 | break; | ||
66 | case 0x0302: | ||
67 | clp_add_pci_device(ccdf->fid, ccdf->fh, 0); | ||
68 | break; | ||
69 | case 0x0306: | ||
70 | clp_find_pci_devices(); | ||
71 | break; | ||
72 | default: | ||
73 | break; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | void zpci_event_error(void *data) | ||
78 | { | ||
79 | struct zpci_ccdf_err *ccdf = data; | ||
80 | struct zpci_dev *zdev; | ||
81 | |||
82 | zpci_event_log_err(ccdf); | ||
83 | zdev = get_zdev_by_fid(ccdf->fid); | ||
84 | if (!zdev) { | ||
85 | pr_err("Error event for unknown fid: %x", ccdf->fid); | ||
86 | return; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | void zpci_event_availability(void *data) | ||
91 | { | ||
92 | zpci_event_log_avail(data); | ||
93 | } | ||
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) |