diff options
Diffstat (limited to 'drivers/s390')
-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 @@ /* * S/390 common I/O routines -- channel subsystem call * - * Copyright IBM Corp. 1999, 2010 + * Copyright IBM Corp. 1999,2012 * Author(s): Ingo Adlung (adlung@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/device.h> +#include <linux/pci.h> #include <asm/cio.h> #include <asm/chpid.h> @@ -260,26 +261,45 @@ __get_chpid_from_lir(void *data) return (u16) (lir->indesc[0]&0x000000ff); } -struct chsc_sei_area { - struct chsc_header request; +struct chsc_sei_nt0_area { + u8 flags; + u8 vf; /* validity flags */ + u8 rs; /* reporting source */ + u8 cc; /* content code */ + u16 fla; /* full link address */ + u16 rsid; /* reporting source id */ u32 reserved1; u32 reserved2; - u32 reserved3; - struct chsc_header response; - u32 reserved4; - u8 flags; - u8 vf; /* validity flags */ - u8 rs; /* reporting source */ - u8 cc; /* content code */ - u16 fla; /* full link address */ - u16 rsid; /* reporting source id */ - u32 reserved5; - u32 reserved6; - u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */ /* ccdf has to be big enough for a link-incident record */ -} __attribute__ ((packed)); - -static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) + u8 ccdf[PAGE_SIZE - 24 - 16]; /* content-code dependent field */ +} __packed; + +struct chsc_sei_nt2_area { + u8 flags; /* p and v bit */ + u8 reserved1; + u8 reserved2; + u8 cc; /* content code */ + u32 reserved3[13]; + u8 ccdf[PAGE_SIZE - 24 - 56]; /* content-code dependent field */ +} __packed; + +#define CHSC_SEI_NT0 0ULL +#define CHSC_SEI_NT2 (1ULL << 61) + +struct chsc_sei { + struct chsc_header request; + u32 reserved1; + u64 ntsm; /* notification type mask */ + struct chsc_header response; + u32 reserved2; + union { + struct chsc_sei_nt0_area nt0_area; + struct chsc_sei_nt2_area nt2_area; + u8 nt_area[PAGE_SIZE - 24]; + } u; +} __packed; + +static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area) { struct chp_id chpid; int id; @@ -298,7 +318,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) } } -static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) +static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area) { struct chp_link link; struct chp_id chpid; @@ -330,7 +350,7 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) s390_process_res_acc(&link); } -static void chsc_process_sei_chp_avail(struct chsc_sei_area *sei_area) +static void chsc_process_sei_chp_avail(struct chsc_sei_nt0_area *sei_area) { struct channel_path *chp; struct chp_id chpid; @@ -366,7 +386,7 @@ struct chp_config_data { u8 pc; }; -static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) +static void chsc_process_sei_chp_config(struct chsc_sei_nt0_area *sei_area) { struct chp_config_data *data; struct chp_id chpid; @@ -398,7 +418,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) } } -static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area) +static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area) { int ret; @@ -412,13 +432,26 @@ static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area) " failed (rc=%d).\n", ret); } -static void chsc_process_sei(struct chsc_sei_area *sei_area) +static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) { - /* Check if we might have lost some information. */ - if (sei_area->flags & 0x40) { - CIO_CRW_EVENT(2, "chsc: event overflow\n"); - css_schedule_eval_all(); +#ifdef CONFIG_PCI + switch (sei_area->cc) { + case 1: + zpci_event_error(sei_area->ccdf); + break; + case 2: + zpci_event_availability(sei_area->ccdf); + break; + default: + CIO_CRW_EVENT(2, "chsc: unhandled sei content code %d\n", + sei_area->cc); + break; } +#endif +} + +static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) +{ /* which kind of information was stored? */ switch (sei_area->cc) { case 1: /* link incident*/ @@ -443,9 +476,51 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area) } } +static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) +{ + do { + memset(sei, 0, sizeof(*sei)); + sei->request.length = 0x0010; + sei->request.code = 0x000e; + sei->ntsm = ntsm; + + if (chsc(sei)) + break; + + if (sei->response.code == 0x0001) { + CIO_CRW_EVENT(2, "chsc: sei successful\n"); + + /* Check if we might have lost some information. */ + if (sei->u.nt0_area.flags & 0x40) { + CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } + + switch (sei->ntsm) { + case CHSC_SEI_NT0: + chsc_process_sei_nt0(&sei->u.nt0_area); + return 1; + case CHSC_SEI_NT2: + chsc_process_sei_nt2(&sei->u.nt2_area); + return 1; + default: + CIO_CRW_EVENT(2, "chsc: unhandled nt (nt=%08Lx)\n", + sei->ntsm); + return 0; + } + } else { + CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", + sei->response.code); + break; + } + } while (sei->u.nt0_area.flags & 0x80); + + return 0; +} + static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { - struct chsc_sei_area *sei_area; + struct chsc_sei *sei; if (overflow) { css_schedule_eval_all(); @@ -459,25 +534,18 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) return; /* Access to sei_page is serialized through machine check handler * thread, so no need for locking. */ - sei_area = sei_page; + sei = sei_page; CIO_TRACE_EVENT(2, "prcss"); - do { - memset(sei_area, 0, sizeof(*sei_area)); - sei_area->request.length = 0x0010; - sei_area->request.code = 0x000e; - if (chsc(sei_area)) - break; - if (sei_area->response.code == 0x0001) { - CIO_CRW_EVENT(4, "chsc: sei successful\n"); - chsc_process_sei(sei_area); - } else { - CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", - sei_area->response.code); - break; - } - } while (sei_area->flags & 0x80); + /* + * The ntsm does not allow to select NT0 and NT2 together. We need to + * first check for NT2, than additionally for NT0... + */ +#ifdef CONFIG_PCI + if (!__chsc_process_crw(sei, CHSC_SEI_NT2)) +#endif + __chsc_process_crw(sei, CHSC_SEI_NT0); } void chsc_chp_online(struct chp_id chpid) |