diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
| -rw-r--r-- | drivers/s390/cio/chsc.c | 154 | 
1 files changed, 111 insertions, 43 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 4d51a7c4eb8..68e80e2734a 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)); +	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_area *sei_area) +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)  |