/*
 *  drivers/s390/s390io.c
 *   S/390 common I/O routines
 *
 *  S390 version
 *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
 *                             IBM Corporation
 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
 *               Cornelia Huck (cohuck@de.ibm.com) 
 *    ChangeLog: 01/07/2001 Blacklist cleanup (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
 *               01/04/2001 Holger Smolinski (smolinsk@de.ibm.com)
 *                          Fixed lost interrupts and do_adapter_IO
 *               xx/xx/xxxx nnn          multiple changes not reflected
 *               03/12/2001 Ingo Adlung  blacklist= - changed to cio_ignore=  
 *               03/14/2001 Ingo Adlung  disable interrupts before start_IO
 *                                        in Path Group processing 
 *                                       decrease retry2 on busy while 
 *                                        disabling sync_isc; reset isc_cnt
 *                                        on io error during sync_isc enablement
 *               05/09/2001 Cornelia Huck added exploitation of debug feature
 *               05/16/2001 Cornelia Huck added /proc/deviceinfo/<devno>/
 *               05/22/2001 Cornelia Huck added /proc/cio_ignore
 *                                        un-ignore blacklisted devices by piping 
 *                                        to /proc/cio_ignore
 *               xx/xx/xxxx some bugfixes & cleanups
 *               08/02/2001 Cornelia Huck not already known devices can be blacklisted
 *                                        by piping to /proc/cio_ignore
 */

#include <linux/module.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/threads.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/smp.h>
#include <asm/pgtable.h>
#include <asm/delay.h>
#include <asm/processor.h>
#include <asm/lowcore.h>
#include <asm/idals.h>
#include <asm/uaccess.h> 
#include <asm/cpcmd.h>

#include <asm/s390io.h>
#include <asm/s390dyn.h>
#include <asm/s390mach.h>
#include <asm/debug.h>
#include <asm/queue.h>

#ifndef TRUE
#define TRUE  1
#define FALSE 0
#endif

#define SANITY_CHECK(irq) do { \
if (irq > highest_subchannel || irq < 0) \
		return (-ENODEV); \
	if (ioinfo[irq] == INVALID_STORAGE_AREA) \
		return (-ENODEV); \
	} while(0) 

#undef  CONFIG_DEBUG_IO
#define CONFIG_DEBUG_CRW

unsigned int           highest_subchannel;
ioinfo_t              *ioinfo_head = NULL;
ioinfo_t              *ioinfo_tail = NULL;
ioinfo_t              *ioinfo[__MAX_SUBCHANNELS] = {
	[0 ... (__MAX_SUBCHANNELS-1)] = INVALID_STORAGE_AREA
};

static atomic_t    sync_isc     = ATOMIC_INIT(-1);
static int         sync_isc_cnt = 0;      // synchronous irq processing lock

static spinlock_t  adapter_lock = SPIN_LOCK_UNLOCKED;
                                          // adapter interrupt lock
static psw_t      io_sync_wait;           // wait PSW for sync IO, prot. by sync_isc
static int        cons_dev          = -1; // identify console device
static int        init_IRQ_complete = 0;
static int        cio_show_msg      = 0;
static schib_t   *p_init_schib      = NULL;
static irb_t     *p_init_irb        = NULL;
static __u64      irq_IPL_TOD;
static adapter_int_handler_t adapter_handler   = NULL;

/* for use of debug feature */
debug_info_t *cio_debug_msg_id = NULL;  
debug_info_t *cio_debug_trace_id = NULL;
debug_info_t *cio_debug_crw_id = NULL;
int cio_debug_initialized = 0;

static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs);
static void s390_process_subchannels( void);
static void s390_device_recognition_all( void);
static void s390_device_recognition_irq( int irq);
#ifdef CONFIG_PROC_FS
static void s390_redo_validation(void);
#endif
static int  s390_validate_subchannel( int irq, int enable);
static int  s390_SenseID( int irq, senseid_t *sid, __u8 lpm);
static int  s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid);
static int  s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid);
static int  s390_process_IRQ( unsigned int irq );
static int  enable_subchannel( unsigned int irq);
static int  disable_subchannel( unsigned int irq);

#ifdef CONFIG_PROC_FS
static int chan_proc_init( void );
#endif 

static inline void do_adapter_IO( __u32 intparm );

int  s390_DevicePathVerification( int irq, __u8 domask );
int  s390_register_adapter_interrupt( adapter_int_handler_t handler );
int  s390_unregister_adapter_interrupt( adapter_int_handler_t handler );

extern int  do_none(unsigned int irq, int cpu, struct pt_regs * regs);
extern int  enable_none(unsigned int irq);
extern int  disable_none(unsigned int irq);

asmlinkage void do_IRQ( struct pt_regs regs );

#define MAX_CIO_PROCFS_ENTRIES 0x300
/* magic number; we want to have some room to spare */

int cio_procfs_device_create(int devno);
int cio_procfs_device_remove(int devno);
int cio_procfs_device_purge(void);

int cio_notoper_msg = 1;
int cio_proc_devinfo = 0; /* switch off the /proc/deviceinfo/ stuff by default
			     until problems are dealt with */

unsigned long s390_irq_count[NR_CPUS]; /* trace how many irqs have occured per cpu... */
int cio_count_irqs = 1;       /* toggle use here... */

/* 
 * "Blacklisting" of certain devices:
 * Device numbers given in the commandline as blacklist=... won't be known to Linux
 * These can be single devices or ranges of devices
 *
 * Introduced by Cornelia Huck <cohuck@de.ibm.com>
 * Most of it shamelessly taken from dasd.c 
 */

typedef struct dev_blacklist_range_t {
     struct dev_blacklist_range_t *next;  /* next range in list */
     unsigned int from;                   /* beginning of range */
     unsigned int to;                     /* end of range */
     int  kmalloced;	
    
} dev_blacklist_range_t;

static dev_blacklist_range_t *dev_blacklist_range_head = NULL; /* Anchor for list of ranges */
static dev_blacklist_range_t *dev_blacklist_unused_head = NULL;

static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
static int nr_blacklisted_ranges = 0;

/* Handling of the blacklist ranges */

static inline void blacklist_range_destroy( dev_blacklist_range_t *range,int locked )
{
	 long flags;
	 
	 if(!locked)
		 spin_lock_irqsave( &blacklist_lock, flags ); 
	 if(!remove_from_list((list **)&dev_blacklist_range_head,(list *)range))
		 BUG();
	 nr_blacklisted_ranges--;
	 if(range->kmalloced)
		 kfree(range);
	 else
		 add_to_list((list **)&dev_blacklist_unused_head,(list *)range); 
	 if(!locked)
		 spin_unlock_irqrestore( &blacklist_lock, flags );
}



/* 
 * Function: blacklist_range_add
 * Creates a range from the specified arguments and appends it to the list of
 * blacklisted devices
 */

static inline dev_blacklist_range_t *blacklist_range_add( int from, int to,int locked)
{
 
     dev_blacklist_range_t *range = NULL;
     unsigned long flags;

     if (to && (from>to)) {
	     printk(KERN_WARNING "Invalid blacklist range %x to %x, skipping\n", from, to);
	     return NULL;
     }
     if(!locked)
	     spin_lock_irqsave( &blacklist_lock, flags );
     if(dev_blacklist_unused_head)
	     range=(dev_blacklist_range_t *)
		     remove_listhead((list **)&dev_blacklist_unused_head);
     else if (init_IRQ_complete) {
	     if((range = ( dev_blacklist_range_t *) 
		 kmalloc( sizeof( dev_blacklist_range_t ), GFP_KERNEL)))
		     range->kmalloced=1;
     } else {
	     if((range = ( dev_blacklist_range_t *) 
		 alloc_bootmem( sizeof( dev_blacklist_range_t ) )))
		     range->kmalloced=0;
     }
     if (range)
     {
	     add_to_list((list **)&dev_blacklist_range_head,(list *)range);
	     range->from = from;
	     if (to == 0) {           /* only a single device is given */
		     range->to = from;
	     } else {
		     range->to = to;
	     }
	     nr_blacklisted_ranges++;
     }
     if(!locked)
	     spin_unlock_irqrestore( &blacklist_lock, flags );
     return range;
}

/* 
 * Function: blacklist_range_remove
 * Removes a range from the blacklist chain
 */

static inline void blacklist_range_remove( int from, int to )
{
     dev_blacklist_range_t *temp;
     long flags;

     spin_lock_irqsave( &blacklist_lock, flags );
     for ( temp = dev_blacklist_range_head; 
	   (temp->from != from) && (temp->to != to);
	   temp = temp->next );
     blacklist_range_destroy( temp,1 );
     spin_unlock_irqrestore( &blacklist_lock, flags );
}

/* Parsing the commandline for blacklist parameters */

/* 
 * Variable to hold the blacklisted devices given by the parameter line
 * blacklist=...
 */
char *blacklist[256] = {NULL, };

/*
 * Get the blacklist=... items from the parameter line
 */

static void blacklist_split_parm_string (char *str)
{
	char *tmp = str;
	int count = 0;
	do {
		char *end;
		int len;
		end = strchr (tmp, ',');
		if (end == NULL) {
			len = strlen (tmp) + 1;
		} else {
			len = (long) end - (long) tmp + 1;
			*end = '\0';
			end++;
		}
		blacklist[count] = alloc_bootmem (len * sizeof (char) );
		if (blacklist == NULL) {
			printk (KERN_WARNING "can't store blacklist= parameter no %d\n", count + 1);
			break;
		}
		memset (blacklist[count], 0, len * sizeof (char));
		memcpy (blacklist[count], tmp, len * sizeof (char));
		count++;
		tmp = end;
	} while (tmp != NULL && *tmp != '\0');
}

/*
 * The blacklist parameters as one concatenated string
 */

static char blacklist_parm_string[1024] __initdata = {0,};


/* 
 * function: blacklist_strtoul
 * Strip leading '0x' and interpret the values as Hex
 */
static inline int blacklist_strtoul (char *str, char **stra)
{
	char *temp = str;
	int val;
	if (*temp == '0') {
		temp++;		                /* strip leading zero */
		if (*temp == 'x')
			temp++;	                /* strip leading x */
	}
	val = simple_strtoul (temp, &temp, 16);	/* interpret anything as hex */
	*stra = temp;
	return val;
}

/*
 * Function: blacklist_parse
 * Parse the parameters given to blacklist=... 
 * Add the blacklisted devices to the blacklist chain
 */

static inline void blacklist_parse( char **str )
{
     char *temp;
     int from, to;

     while (*str) {
	  temp = *str;
	  from = 0;
	  to = 0;
	  
	  from = blacklist_strtoul( temp, &temp );
	  if (*temp == '-') {
	       temp++;
	       to = blacklist_strtoul( temp, &temp );
	  }
	  if (!blacklist_range_add( from, to,0 )) {
	       printk( KERN_WARNING "Blacklisting range from %X to %X failed!\n", from, to);
	  }
#ifdef CONFIG_DEBUG_IO
	  printk( "Blacklisted range from %X to %X\n", from, to );
#endif
	  str++;
     }
}


/*
 * Initialisation of blacklist 
 */

void __init blacklist_init( void )
{
#ifdef CONFIG_DEBUG_IO     
     printk( "Reading blacklist...\n");
#endif
     if (cio_debug_initialized)
	     debug_sprintf_event(cio_debug_msg_id, 6,
				 "Reading blacklist\n");
     blacklist_split_parm_string( blacklist_parm_string );
     blacklist_parse( blacklist );
}


/*
 * Get all the blacklist parameters from parameter line
 */

void __init blacklist_setup (char *str, int *ints)
{
	int len = strlen (blacklist_parm_string);
	if (len != 0) {
		strcat (blacklist_parm_string, ",");
	}
	strcat (blacklist_parm_string, str);
}

int __init blacklist_call_setup (char *str)
{
        int dummy;
#ifdef CONFIG_DEBUG_IO
	printk( "Reading blacklist parameters...\n" );
#endif
	if (cio_debug_initialized)
		debug_sprintf_event(cio_debug_msg_id, 6,
				    "Reading blacklist parameters\n");
        blacklist_setup(str,&dummy);
	
	blacklist_init(); /* Blacklist ranges must be ready when device recognition starts */
       
	return 1;
}

__setup ("cio_ignore=", blacklist_call_setup);

/* Checking if devices are blacklisted */

/*
 * Function: is_blacklisted
 * Returns 1 if the given devicenumber can be found in the blacklist, otherwise 0.
 */

static inline int is_blacklisted( int devno )
{
     dev_blacklist_range_t *temp;
     long flags;
     int retval=0;

     if (dev_blacklist_range_head == NULL) {  
	  /* no blacklist */
	  return 0;
     }

     spin_lock_irqsave( &blacklist_lock, flags ); 
     temp = dev_blacklist_range_head;
     while (temp) {
	  if ((temp->from <= devno) && (temp->to >= devno)) {
		  retval=1;                      /* Deviceno is blacklisted */
		  break;
	  }
	  temp = temp->next;
     }
     spin_unlock_irqrestore( &blacklist_lock, flags );
     return retval;
}

/*
 * Function: blacklist_free_all_ranges
 * set all blacklisted devices free...
 */

void blacklist_free_all_ranges(void) 
{
	dev_blacklist_range_t *tmp = dev_blacklist_range_head;
	unsigned long flags;

	spin_lock_irqsave( &blacklist_lock, flags ); 
	while (tmp) {
		blacklist_range_destroy(tmp,1);
		tmp = dev_blacklist_range_head;
	}
	spin_unlock_irqrestore( &blacklist_lock, flags );
}

/*
 * Function: blacklist_parse_proc_parameters
 * parse the stuff which is piped to /proc/cio_ignore
 */
void blacklist_parse_proc_parameters(char *buf)
{
	char *tmp;
	int i;
	char *end;
	int len = -1;
	char *param;
	int from = 0;
	int to = 0;
	int changed = 0;
	dev_blacklist_range_t *range, *temp;
	long flags;
	int err = 0;

	tmp = buf;
	if (strstr(tmp, "free ")) {
		for (i=0; i<5; i++) {
			tmp++;
		}
		if (strstr(tmp, "all")) {
			blacklist_free_all_ranges();
			s390_redo_validation();
		} else {
			while (tmp != NULL) {
				end = strchr(tmp, ',');
				if (end == NULL) {
					len = strlen(tmp) + 1;
				} else {
					len = (long)end - (long) tmp + 1;
					*end = '\0';
					end++;
				}
				param =  (char*) kmalloc(len * sizeof(char) + 1, GFP_KERNEL);
				strncpy(param, (const char *) tmp, len);
				tmp = end;
				from = blacklist_strtoul(param, &param);
				if (*param == '-') {
					param++;
					to = blacklist_strtoul(param, &param);
				} else {
					to = from;
				}
				spin_lock_irqsave( &blacklist_lock, flags ); 
				range = dev_blacklist_range_head;
				while (range != NULL) {
					temp = range->next;
					if ((from <= range->from) && (to >= range->to)) {
						blacklist_range_destroy(range,1);
						changed = 1;
					} else if ((from <= range->from) && (to>=range->from) && (to < range->to)) {
						blacklist_range_add(to+1, range->to,1);
						blacklist_range_destroy(range,1);
						changed = 1;
					} else if ((from > range->from) && (from<=range->to) && (to >= range->to)) {
						blacklist_range_add(range->from, from-1,1);
						blacklist_range_destroy(range,1);
						changed = 1;
					} else if ((from > range->from) && (to < range->to)) {
						blacklist_range_add(range->from, from-1,1);
						blacklist_range_add(to+1, range->to,1);
						blacklist_range_destroy(range,1);
						changed = 1;
					}
					range = temp;
				}
				spin_unlock_irqrestore( &blacklist_lock, flags );
				kfree(param);
			}
			if (changed)
				s390_redo_validation();
		}
	} else if (strstr(tmp, "add ")) {
		for (i=0;i<4;i++){
			tmp++;
		}
		while (tmp != NULL) {
			end = strchr(tmp, ',');
			if (end == NULL) {
				len = strlen(tmp) + 1;
			} else {
				len = (long)end - (long) tmp + 1;
				*end = '\0';
				end++;
			}
			param =  (char*) kmalloc(len * sizeof(char) + 1, GFP_KERNEL);
			strncpy(param, (const char *) tmp, len);
			tmp = end;
			from = blacklist_strtoul(param, &param);
			if (*param == '-') {
				param++;
				to = blacklist_strtoul(param, &param);
			} else {
				to = from;
			}
			spin_lock_irqsave( &blacklist_lock, flags ); 
			
			/*
			 * Don't allow for already known devices to be
			 * blacklisted
			 * The criterion is a bit dumb, devices which once were
			 * there but are already gone are also caught...
			 */
			
			err = 0;
			for (i=0; i<=highest_subchannel; i++) {
				if (ioinfo[i]!=INVALID_STORAGE_AREA) {
					if (   (ioinfo[i]->schib.pmcw.dev >= from)
					    && (ioinfo[i]->schib.pmcw.dev <= to)  ) {
						printk(KERN_WARNING "cio_ignore: Won't blacklist "
						       "already known devices, skipping range "
						       "%x to %x\n", from, to);
						err = 1;
						break;
					}
				}
			}

			/*
			 * Note: We allow for overlapping ranges here, 
			 * since the user might specify overlapping ranges
			 * and we walk through all ranges when freeing anyway.
			 */

			if (!err) 
				blacklist_range_add(from, to, 1);
			
			spin_unlock_irqrestore( &blacklist_lock, flags );
			kfree(param);
		}

	} else {
		printk("cio_ignore: Parse error; try using 'free all|<devno-range>,<devno-range>,...'\n");
		printk("or 'add <devno-range>,<devno-range>,...'\n");
	}
}

/* End of blacklist handling */


void s390_displayhex(char *str,void *ptr,s32 cnt);
void s390_displayhex2(char *str, void *ptr, s32 cnt, int level);

void s390_displayhex(char *str,void *ptr,s32 cnt)
{
	s32	cnt1,cnt2,maxcnt2;
	u32	*currptr=(__u32 *)ptr;

	printk("\n%s\n",str);

	for(cnt1=0;cnt1<cnt;cnt1+=16)
	{
		printk("%08lX ",(unsigned long)currptr);
		maxcnt2=cnt-cnt1;
		if(maxcnt2>16)
			maxcnt2=16;
		for(cnt2=0;cnt2<maxcnt2;cnt2+=4)
			printk("%08X ",*currptr++);
		printk("\n");
	}
}

void s390_displayhex2(char *str, void *ptr, s32 cnt, int level)
{
	s32 cnt1, cnt2, maxcnt2;
	u32 *currptr = (__u32 *)ptr;
	char buffer[cnt*12];

	debug_sprintf_event(cio_debug_msg_id, level, "%s\n", str);

	for (cnt1 = 0; cnt1<cnt; cnt1+=16) {
		sprintf(buffer, "%08lX ", (unsigned long)currptr);
		maxcnt2 = cnt - cnt1;
		if (maxcnt2 > 16)
			maxcnt2 = 16;
		for (cnt2 = 0; cnt2 < maxcnt2; cnt2 += 4)
			sprintf(buffer, "%08X ", *currptr++);
	}
	debug_sprintf_event(cio_debug_msg_id, level, "%s\n",buffer);
}

static int __init cio_setup( char *parm )
{
	if ( !strcmp( parm, "yes") )
	{
		cio_show_msg = 1;
	}
	else if ( !strcmp( parm, "no") )
	{
		cio_show_msg = 0;
	}
	else
	{
		printk( "cio_setup : invalid cio_msg parameter '%s'", parm);

	} /* endif */

	return 1;
}

__setup("cio_msg=", cio_setup);

static int __init cio_notoper_setup(char *parm)
{
	if (!strcmp(parm, "yes")) {
		cio_notoper_msg = 1;
	} else if (!strcmp(parm, "no")) {
		cio_notoper_msg = 0;
	} else {
		printk("cio_notoper_setup: invalid cio_notoper_msg parameter '%s'", parm);
	}

	return 1;
}
	
__setup("cio_notoper_msg=", cio_notoper_setup);

#ifdef CONFIG_PROC_FS
static int __init cio_proc_devinfo_setup(char *parm)
{
	if (!strcmp(parm, "yes")) {
		cio_proc_devinfo = 1;
	} else if (!strcmp(parm, "no")) {
		cio_proc_devinfo = 0;
	} else {
		printk( KERN_ERR "cio_proc_devinfo_setup: invalid parameter '%s'\n",parm);
	}

	return 1;
}

__setup("cio_proc_devinfo=", cio_proc_devinfo_setup);
#endif

/*
 * register for adapter interrupts
 *
 * With HiperSockets the zSeries architecture provides for
 *  means of adapter interrups, pseudo I/O interrupts that are
 *  not tied to an I/O subchannel, but to an adapter. However,
 *  it doesn't disclose the info how to enable/disable them, but
 *  to recognize them only. Perhaps we should consider them
 *  being shared interrupts, and thus build a linked list
 *  of adapter handlers ... to be evaluated ...
 */
int  s390_register_adapter_interrupt( adapter_int_handler_t handler )
{
	int ret = 0;
	
	if (cio_debug_initialized)
		debug_text_event(cio_debug_trace_id, 4, "rgaint");

	spin_lock( &adapter_lock );

	if ( handler == NULL )
		ret = -EINVAL;
	else if ( adapter_handler )
		ret = -EBUSY;
	else
		adapter_handler = handler;
  	
	spin_unlock( &adapter_lock ); 	

	return( ret);
}


int  s390_unregister_adapter_interrupt( adapter_int_handler_t handler )
{
	int ret = 0;
	
	if (cio_debug_initialized)
		debug_text_event(cio_debug_trace_id, 4, "urgaint");

	spin_lock( &adapter_lock ); 	

	if ( handler == NULL )
		ret = -EINVAL;
	else if ( handler != adapter_handler )
		ret = -EINVAL;
	else
		adapter_handler = NULL;
  	
	spin_unlock( &adapter_lock ); 	

	return( ret);
}

static inline void do_adapter_IO( __u32 intparm )
{
	if (cio_debug_initialized)
		debug_text_event(cio_debug_trace_id, 4, "doaio");
	
	spin_lock( &adapter_lock ); 	

	if ( adapter_handler )
		(*adapter_handler)( intparm );

	spin_unlock( &adapter_lock );

	return;  	
}

/*
 * Note : internal use of irqflags SA_PROBE for NOT path grouping 
 *
 */
int s390_request_irq_special( int                      irq,
                              io_handler_func_t        io_handler,
                              not_oper_handler_func_t  not_oper_handler,
                              unsigned long            irqflags,
                              const char              *devname,
                              void                    *dev_id)
{
	int		retval = 0;
	unsigned long	flags;
	char            dbf_txt[15];
	int             retry;

	if (irq >= __MAX_SUBCHANNELS)
		return -EINVAL;

	if ( !io_handler || !dev_id )
		return -EINVAL;

	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
		return -ENODEV;


	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "reqsp");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * The following block of code has to be executed atomically
	 */
	s390irq_spin_lock_irqsave( irq, flags);

	if ( !ioinfo[irq]->ui.flags.ready )
	{
		retry = 5;
		
		ioinfo[irq]->irq_desc.handler = io_handler;
		ioinfo[irq]->irq_desc.name    = devname;
		ioinfo[irq]->irq_desc.dev_id  = dev_id;
		ioinfo[irq]->ui.flags.ready   = 1;
		
		
		do {
			retval = enable_subchannel(irq);
			if (retval) {
				ioinfo[irq]->ui.flags.ready = 0;
				break;
			}
			
			stsch(irq,&ioinfo[irq]->schib);
			if (ioinfo[irq]->schib.pmcw.ena) 
				retry = 0;
			else
				retry--;

		} while (retry);
	}
	else
	{
		/*
		 *  interrupt already owned, and shared interrupts
		 *   aren't supported on S/390.
		 */
		retval = -EBUSY;

	} /* endif */

	s390irq_spin_unlock_irqrestore(irq,flags);



	if ( retval == 0 )
	{
		if ( !(irqflags & SA_PROBE))
			s390_DevicePathVerification( irq, 0 );

		ioinfo[irq]->ui.flags.newreq = 1;
		ioinfo[irq]->nopfunc         = not_oper_handler;  	
	}

	return retval;
}


int s390_request_irq( unsigned int   irq,
                      void           (*handler)(int, void *, struct pt_regs *),
                      unsigned long  irqflags,
                      const char    *devname,
                      void          *dev_id)
{
	int ret;

	ret = s390_request_irq_special( irq,
                                   (io_handler_func_t)handler,
                                   NULL,
                                   irqflags,
                                   devname,
                                   dev_id);

	if ( ret == 0 )
	{
		ioinfo[irq]->ui.flags.newreq = 0;

	} /* endif */

	return( ret);
}

void s390_free_irq(unsigned int irq, void *dev_id)
{
	unsigned long flags;
	int          ret;

	unsigned int count = 0;
	char dbf_txt[15];

	if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA )
	{
		return;

	} /* endif */

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 2, "free");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
	}

	s390irq_spin_lock_irqsave( irq, flags);

#ifdef  CONFIG_KERNEL_DEBUG
	if ( irq != cons_dev )
	{
		printk("Trying to free IRQ%d\n",irq);

	} /* endif */
#endif
	if (cio_debug_initialized)
		debug_sprintf_event(cio_debug_msg_id, 2, "Trying to free IRQ %d\n", irq);

	/*
	 * disable the device and reset all IRQ info if
	 *  the IRQ is actually owned by the handler ...
	 */
	if ( ioinfo[irq]->ui.flags.ready )
	{
		if ( dev_id == ioinfo[irq]->irq_desc.dev_id  )
		{
			/* start deregister */
			ioinfo[irq]->ui.flags.unready = 1;

			/*
			 * Try to stop IO first...
			 * ... it seems disable_subchannel is sometimes
			 * successfully called with IO still pending.
			 */
			halt_IO( irq,
				 0xC8C1D3E3,
				 DOIO_WAIT_FOR_INTERRUPT );

			do
			{
				ret = disable_subchannel( irq);

				count++;

				if ( ret == -EBUSY )
				{
					int iret;

					/*
					 * kill it !
					 * ... we first try sync and eventually
					 *  try terminating the current I/O by
					 *  an async request, twice halt, then
					 *  clear.
					 */
					if ( count < 2 )
					{              	
						iret = halt_IO( irq,
						                0xC8C1D3E3,
						                DOIO_WAIT_FOR_INTERRUPT );
   	
						if ( iret == -EBUSY )
						{
							halt_IO( irq, 0xC8C1D3E3, 0);
							s390irq_spin_unlock_irqrestore( irq, flags);
							udelay( 200000 ); /* 200 ms */
							s390irq_spin_lock_irqsave( irq, flags);

						} /* endif */
					}
					else
					{
						iret = clear_IO( irq,
						                 0x40C3D3D9,
						                 DOIO_WAIT_FOR_INTERRUPT );
   	
						if ( iret == -EBUSY )
						{
							clear_IO( irq, 0xC8C1D3E3, 0);
							s390irq_spin_unlock_irqrestore( irq, flags);
							udelay( 1000000 ); /* 1000 ms */
							s390irq_spin_lock_irqsave( irq, flags);

						} /* endif */

					} /* endif */

					if ( count == 2 )
					{
						/* give it a very last try ... */
						disable_subchannel( irq);

						if ( ioinfo[irq]->ui.flags.busy )
					        {
							printk( KERN_CRIT"free_irq(%04X) "
							       "- device %04X busy, retry "
							       "count exceeded\n",
								irq,
								ioinfo[irq]->devstat.devno);
							if (cio_debug_initialized)
								debug_sprintf_event(cio_debug_msg_id, 0,
										    "free_irq(%04X) - device %04X busy, retry count exceeded\n",
										    irq, ioinfo[irq]->devstat.devno);

						} /* endif */
						
						break; /* sigh, let's give up ... */

					} /* endif */

				} /* endif */
		
			} while ( ret == -EBUSY );

			ioinfo[irq]->ui.flags.ready   = 0;
			ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */

			ioinfo[irq]->nopfunc = NULL;

			s390irq_spin_unlock_irqrestore( irq, flags);
		}
		else
		{
			s390irq_spin_unlock_irqrestore( irq, flags);

			printk( "free_irq(%04X) : error, "
			        "dev_id does not match !\n", irq);
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_msg_id, 0, 
						    "free_irq(%04X) : error, dev_id does not match !\n", irq);

		} /* endif */

	}
	else
	{
		s390irq_spin_unlock_irqrestore( irq, flags);

		printk( "free_irq(%04X) : error, "
		        "no action block ... !\n", irq);
		if (cio_debug_initialized)
			debug_sprintf_event(cio_debug_msg_id, 0,
					    "free_irq(%04X) : error, no action block ... !\n", irq);

	} /* endif */

}

/*
 * Generic enable/disable code
 */
int disable_irq(unsigned int irq)
{
	unsigned long flags;
	int           ret;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if ( !ioinfo[irq]->ui.flags.ready )
		return -ENODEV;

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "disirq");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	s390irq_spin_lock_irqsave(irq, flags);
	ret = disable_subchannel(irq);
	s390irq_spin_unlock_irqrestore(irq, flags);

	synchronize_irq();

	return( ret);
}

int enable_irq(unsigned int irq)
{
	unsigned long flags;
	int           ret;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if ( !ioinfo[irq]->ui.flags.ready )
		return -ENODEV;

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "enirq");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	s390irq_spin_lock_irqsave(irq, flags);
	ret = enable_subchannel(irq);
	s390irq_spin_unlock_irqrestore(irq, flags);

	return(ret);
}

/*
 * Enable IRQ by modifying the subchannel
 */
static int enable_subchannel( unsigned int irq)
{
	int   ret = 0;
	int   ccode;
	int   retry = 5;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 2, "ensch");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
	}

	/*
	 * If a previous disable request is pending we reset it. However, this
	 *  status implies that the device may (still) be not-operational.
	 */
	if (  ioinfo[irq]->ui.flags.d_disable )
	{
		ioinfo[irq]->ui.flags.d_disable = 0;
		ret                             = 0;
	}
	else
	{
		ccode = stsch(irq, &(ioinfo[irq]->schib) );

		if ( ccode )
		{
			ret = -ENODEV;
		}
		else
		{
			ioinfo[irq]->schib.pmcw.ena = 1;

			if ( irq == cons_dev )
			{
				ioinfo[irq]->schib.pmcw.isc = 7;
			}
			else	
			{
				ioinfo[irq]->schib.pmcw.isc = 3;

			} /* endif */

			do
			{
				ccode = msch( irq, &(ioinfo[irq]->schib) );

				switch (ccode) {
				case 0: /* ok */
					ret = 0;
					retry = 0;
					break;

				case 1: /* status pending */

					ioinfo[irq]->ui.flags.s_pend = 1;
					s390_process_IRQ( irq );
					ioinfo[irq]->ui.flags.s_pend = 0;

					ret = -EIO;    /* might be overwritten */
					               /* ... on re-driving    */
					               /* ... the msch()       */
					retry--;
					break;

				case 2: /* busy */
					udelay(100);	/* allow for recovery */
					ret = -EBUSY;
					retry--;
					break;

				case 3: /* not oper */
					ioinfo[irq]->ui.flags.oper = 0;
					retry = 0;
					ret = -ENODEV;
					break;
				}

			} while ( retry );

		} /* endif */

	}  /* endif */

	return( ret );
}


/*
 * Disable IRQ by modifying the subchannel
 */
static int disable_subchannel( unsigned int irq)
{
	int  cc;          /* condition code */
	int  ret = 0;     /* function return value */
	int  retry = 5;
	char dbf_txt[15];

	SANITY_CHECK(irq);
	
	if ( ioinfo[irq]->ui.flags.busy )
	{
		/*
		 * the disable function must not be called while there are
		 *  requests pending for completion !
		 */
		ret = -EBUSY;
	}
	else
	{
		if (cio_debug_initialized) {
			debug_text_event(cio_debug_trace_id, 2, "dissch");
			sprintf(dbf_txt, "%x", irq);
			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
		}
		
		/*
		 * If device isn't operational we have to perform delayed
		 *  disabling when the next interrupt occurs - unless the
		 *  irq is re-requested prior to the interrupt to occur.
		 */
		cc = stsch(irq, &(ioinfo[irq]->schib) );

		if ( cc == 3 )
		{
			ioinfo[irq]->ui.flags.oper      = 0;
			ioinfo[irq]->ui.flags.d_disable = 1;

			ret = 0;
		}
		else // cc == 0
		{
			ioinfo[irq]->schib.pmcw.ena = 0;

			do
			{
				cc = msch( irq, &(ioinfo[irq]->schib) );

				switch (cc) {
				case 0: /* ok */
					retry = 0;
					ret = 0;   
					break;

				case 1: /* status pending */
					ioinfo[irq]->ui.flags.s_pend = 1;
					s390_process_IRQ( irq );
					ioinfo[irq]->ui.flags.s_pend = 0;

					ret = -EIO; /* might be overwritten  */
					            /* ... on re-driving the */
					            /* ... msch() call       */
					retry--;
					break;

				case 2: /* busy; this should not happen! */
					printk( KERN_CRIT"disable_subchannel(%04X) "
					        "- unexpected busy condition for "
						"device %04X received !\n",
					        irq,
					        ioinfo[irq]->devstat.devno);
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_msg_id, 0, 
								    "disable_subchannel(%04X) - unexpected busy condition for device %04X received !\n",
								    irq, ioinfo[irq]->devstat.devno);
					retry = 0;
					ret = -EBUSY;
					break;

				case 3: /* not oper */
					/*
					 * should hardly occur ?!
					 */
					ioinfo[irq]->ui.flags.oper      = 0;
					ioinfo[irq]->ui.flags.d_disable = 1;
					retry = 0;

					ret = 0; /* if the device has gone we */
					         /* ... don't need to disable */
					         /* ... it anymore !          */
					break;

				} /* endswitch */

			} while ( retry );

		} /* endif */

	} /* endif */

	return( ret);
}


void s390_init_IRQ( void )
{
	unsigned long flags;     /* PSW flags */
	long          cr6 __attribute__ ((aligned (8)));

	asm volatile ("STCK %0" : "=m" (irq_IPL_TOD));

	p_init_schib = alloc_bootmem_low( sizeof(schib_t));
	p_init_irb   = alloc_bootmem_low( sizeof(irb_t));
	
	
	/*
	 * As we don't know about the calling environment
	 *  we assure running disabled. Before leaving the
	 *  function we resestablish the old environment.
	 *
	 * Note : as we don't need a system wide lock, therefore
	 *        we shouldn't use cli(), but __cli() as this
	 *        affects the current CPU only.
	 */
	__save_flags(flags);
	__cli();

	/*
	 * disable all interrupts
	 */
	cr6 = 0;
	__ctl_load( cr6, 6, 6);

	s390_process_subchannels();

	if (cio_count_irqs) {
		int i;
		for (i=0; i<NR_CPUS; i++) 
			s390_irq_count[i]=0;
	}

	/*
	 * enable default I/O-interrupt sublass 3
	 */
	cr6 = 0x10000000;
	__ctl_load( cr6, 6, 6);

	s390_device_recognition_all();

	init_IRQ_complete = 1;

	__restore_flags(flags);

	return;
}


/*
 * dummy handler, used during init_IRQ() processing for compatibility only
 */
void  init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs)
{
   /* this is a dummy handler only ... */
}


int s390_start_IO( int            irq,      /* IRQ */
                   ccw1_t        *cpa,      /* logical channel prog addr */
                   unsigned long  user_intparm,  /* interruption parameter */
                   __u8           lpm,      /* logical path mask */
                   unsigned long  flag)     /* flags */
{
	int            ccode;
	int            ret = 0;
	char buffer[80];
	char dbf_txt[15];

	SANITY_CHECK(irq);

	/*
	 * The flag usage is mutal exclusive ...
	 */
	if (    (flag & DOIO_EARLY_NOTIFICATION)
	     && (flag & DOIO_REPORT_ALL     ) )
	{
		return( -EINVAL );

	} /* endif */

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "stIO");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * setup ORB
	 */
  	ioinfo[irq]->orb.intparm = (__u32)(long)&ioinfo[irq]->u_intparm;
	ioinfo[irq]->orb.fmt     = 1;

	ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH);
	ioinfo[irq]->orb.spnd =  (flag & DOIO_ALLOW_SUSPEND ? TRUE : FALSE);
	ioinfo[irq]->orb.ssic =  (    (flag & DOIO_ALLOW_SUSPEND )
	                           && (flag & DOIO_SUPPRESS_INTER) );

	if ( flag & DOIO_VALID_LPM )
	{
		ioinfo[irq]->orb.lpm = lpm;
	}
	else
	{
		ioinfo[irq]->orb.lpm = ioinfo[irq]->opm;

	} /* endif */

#ifdef CONFIG_ARCH_S390X
	/* 
	 * for 64 bit we always support 64 bit IDAWs with 4k page size only
	 */
	ioinfo[irq]->orb.c64 = 1;
	ioinfo[irq]->orb.i2k = 0;
#endif

	ioinfo[irq]->orb.cpa = (__u32)virt_to_phys( cpa);

	/*
	 * If sync processing was requested we lock the sync ISC, modify the
	 *  device to present interrupts for this ISC only and switch the
	 *  CPU to handle this ISC + the console ISC exclusively.
	 */
	if ( flag & DOIO_WAIT_FOR_INTERRUPT )
	{
		ret = enable_cpu_sync_isc( irq);
	
		if ( ret )
		{
			return( ret);
		}

	} /* endif */

	if ( flag & DOIO_DONT_CALL_INTHDLR )
	{
		ioinfo[irq]->ui.flags.repnone = 1;

	} /* endif */

	/*
	 * Issue "Start subchannel" and process condition code
	 */
	ccode = ssch( irq, &(ioinfo[irq]->orb) );

	switch ( ccode ) {
	case 0:

		if ( !ioinfo[irq]->ui.flags.w4sense )
		{
			/*
			 * init the device driver specific devstat irb area
			 *
			 * Note : donīt clear saved irb info in case of sense !
			 */
			memset( &((devstat_t *)ioinfo[irq]->irq_desc.dev_id)->ii.irb,
				'\0', sizeof( irb_t) );
		} /* endif */

		memset( &ioinfo[irq]->devstat.ii.irb,
		        '\0',
		        sizeof( irb_t) );

		/*
		 * initialize device status information
		 */
		ioinfo[irq]->ui.flags.busy   = 1;
		ioinfo[irq]->ui.flags.doio   = 1;

		ioinfo[irq]->u_intparm       = user_intparm;
		ioinfo[irq]->devstat.cstat   = 0;
		ioinfo[irq]->devstat.dstat   = 0;
		ioinfo[irq]->devstat.lpum    = 0;
		ioinfo[irq]->devstat.flag    = DEVSTAT_START_FUNCTION;
		ioinfo[irq]->devstat.scnt    = 0;

		ioinfo[irq]->ui.flags.fast   = 0;
		ioinfo[irq]->ui.flags.repall = 0;

		/*
		 * Check for either early (FAST) notification requests
		 *  or if we are to return all interrupt info.
		 * Default is to call IRQ handler at secondary status only
		 */
		if ( flag & DOIO_EARLY_NOTIFICATION )
		{
			ioinfo[irq]->ui.flags.fast = 1;
		}
		else if ( flag & DOIO_REPORT_ALL )
		{
			ioinfo[irq]->ui.flags.repall = 1;

		} /* endif */

		ioinfo[irq]->ulpm = ioinfo[irq]->orb.lpm;

		/*
		 * If synchronous I/O processing is requested, we have
		 *  to wait for the corresponding interrupt to occur by
		 *  polling the interrupt condition. However, as multiple
		 *  interrupts may be outstanding, we must not just wait
		 *  for the first interrupt, but must poll until ours
		 *  pops up.
		 */
		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
		{
			psw_t            io_new_psw;
			int              ccode;
			uint64_t         time_start;    	
			uint64_t         time_curr;    	

			int              ready    = 0;
			int              io_sub   = -1;
			struct _lowcore *lc       = NULL;
			int              do_retry = 1;

			/*
			 * We shouldn't perform a TPI loop, waiting for an
			 *  interrupt to occur, but should load a WAIT PSW
			 *  instead. Otherwise we may keep the channel subsystem
			 *  busy, not able to present the interrupt. When our
			 *  sync. interrupt arrived we reset the I/O old PSW to
			 *  its original value.
			 */
			memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t));

			ccode = iac();

			switch (ccode) {
			case 0:  		// primary-space
				io_sync_wait.mask =   _IO_PSW_MASK
				                    | _PSW_PRIM_SPACE_MODE
				                    | _PSW_IO_WAIT;
				break;
			case 1:			// secondary-space
				io_sync_wait.mask =   _IO_PSW_MASK
				                    | _PSW_SEC_SPACE_MODE
				                    | _PSW_IO_WAIT;
				break;
			case 2:			// access-register
				io_sync_wait.mask =   _IO_PSW_MASK
				                    | _PSW_ACC_REG_MODE
				                    | _PSW_IO_WAIT;
				break;
			case 3:			// home-space	
				io_sync_wait.mask =   _IO_PSW_MASK
				                    | _PSW_HOME_SPACE_MODE
				                    | _PSW_IO_WAIT;
				break;
			default:
				panic( "start_IO() : unexpected "
				       "address-space-control %d\n",
				       ccode);
				break;
			} /* endswitch */

			io_sync_wait.addr = FIX_PSW(&&io_wakeup);

			/*
			 * Martin didn't like modifying the new PSW, now we take
			 *  a fast exit in do_IRQ() instead
			 */
			*(__u32 *)__LC_SYNC_IO_WORD  = 1;

			asm volatile ("STCK %0" : "=m" (time_start));

			time_start = time_start >> 32;

			do
			{
				if ( flag & DOIO_TIMEOUT )
				{
					tpi_info_t tpi_info={0,};

					do
					{
						if ( tpi(&tpi_info) == 1 )
						{
							io_sub = tpi_info.irq;
							break;
						}
						else
						{
							udelay(100); /* usecs */
							asm volatile ("STCK %0" : "=m" (time_curr));

							if ( ((time_curr >> 32) - time_start ) >= 3 )
								do_retry = 0;          							

						} /* endif */
				
					} while ( do_retry );
				}
				else
				{
					__load_psw( io_sync_wait );

io_wakeup:
					io_sub  = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR;

				} /* endif */

 				if ( do_retry )
					ready = s390_process_IRQ( io_sub );

				/*
				 * surrender when retry count's exceeded ...
				 */
			} while ( !(     ( io_sub == irq )
			              && ( ready  == 1   ))
			            && do_retry             );

			*(__u32 *)__LC_SYNC_IO_WORD = 0;

			if ( !do_retry )
				ret = -ETIMEDOUT;

		} /* endif */

		break;

	case 1 :            /* status pending */

		ioinfo[irq]->devstat.flag =   DEVSTAT_START_FUNCTION
		                            | DEVSTAT_STATUS_PENDING;

		/*
		 * initialize the device driver specific devstat irb area
		 */
		memset( &((devstat_t *) ioinfo[irq]->irq_desc.dev_id)->ii.irb,
		        '\0', sizeof( irb_t) );

		/*
		 * Let the common interrupt handler process the pending status.
		 *  However, we must avoid calling the user action handler, as
		 *  it won't be prepared to handle a pending status during
		 *  do_IO() processing inline. This also implies that process_IRQ
		 *  must terminate synchronously - especially if device sensing
		 *  is required.
		 */
		ioinfo[irq]->ui.flags.s_pend   = 1;
		ioinfo[irq]->ui.flags.busy     = 1;
		ioinfo[irq]->ui.flags.doio     = 1;

		s390_process_IRQ( irq );

		ioinfo[irq]->ui.flags.s_pend   = 0;
		ioinfo[irq]->ui.flags.busy     = 0;
		ioinfo[irq]->ui.flags.doio     = 0;

		ioinfo[irq]->ui.flags.repall   = 0;
		ioinfo[irq]->ui.flags.w4final  = 0;

		ioinfo[irq]->devstat.flag     |= DEVSTAT_FINAL_STATUS;

		/*
		 * In multipath mode a condition code 3 implies the last path
		 *  has gone, except we have previously restricted the I/O to
		 *  a particular path. A condition code 1 (0 won't occur)
		 *  results in return code EIO as well as 3 with another path
		 *  than the one used (i.e. path available mask is non-zero).
		 */
		if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 )
		{
			if ( flag & DOIO_VALID_LPM )
			{
				ioinfo[irq]->opm &= ~(ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum);
			}
			else
			{
				ioinfo[irq]->opm = 0;

			} /* endif */
	
			if ( ioinfo[irq]->opm == 0 ) 	
			{
				ret                         = -ENODEV;
				ioinfo[irq]->ui.flags.oper  = 0;
			}
			else
			{
				ret = -EIO;

			} /* endif */

			ioinfo[irq]->devstat.flag  |= DEVSTAT_NOT_OPER;

#ifdef CONFIG_DEBUG_IO
			{

			stsch(irq, &(ioinfo[irq]->schib) );

			sprintf( buffer, "s390_start_IO(%04X) - irb for "
			         "device %04X, after status pending\n",
			         irq,
			         ioinfo[irq]->devstat.devno );

			s390_displayhex( buffer,
			                 &(ioinfo[irq]->devstat.ii.irb) ,
			                 sizeof(irb_t));

			sprintf( buffer, "s390_start_IO(%04X) - schib for "
			         "device %04X, after status pending\n",
			         irq,
			         ioinfo[irq]->devstat.devno );

			s390_displayhex( buffer,
			                 &(ioinfo[irq]->schib) ,
			                 sizeof(schib_t));


			if (ioinfo[irq]->devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL)
			{
				sprintf( buffer, "s390_start_IO(%04X) - sense "
				         "data for "
				         "device %04X, after status pending\n",
				         irq,
				         ioinfo[irq]->devstat.devno );

				s390_displayhex( buffer,
					ioinfo[irq]->irq_desc.dev_id->ii.sense.data,
					ioinfo[irq]->irq_desc.dev_id->rescnt);

			} /* endif */
			}
#endif
			if (cio_debug_initialized) {
				stsch(irq, &(ioinfo[irq]->schib) );
				
				sprintf( buffer, "s390_start_IO(%04X) - irb for "
					 "device %04X, after status pending\n",
					 irq,
					 ioinfo[irq]->devstat.devno );
				
				s390_displayhex2( buffer,
						 &(ioinfo[irq]->devstat.ii.irb) ,
						 sizeof(irb_t), 2);
				
				sprintf( buffer, "s390_start_IO(%04X) - schib for "
					 "device %04X, after status pending\n",
					 irq,
					 ioinfo[irq]->devstat.devno );
				
				s390_displayhex2( buffer,
						 &(ioinfo[irq]->schib) ,
						 sizeof(schib_t), 2);
				
				
				if (ioinfo[irq]->devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) {
					sprintf( buffer, "s390_start_IO(%04X) - sense "
						 "data for "
						 "device %04X, after status pending\n",
						 irq,
						 ioinfo[irq]->devstat.devno );
					
					s390_displayhex2( buffer,
							  ioinfo[irq]->irq_desc.dev_id->ii.sense.data,
							  ioinfo[irq]->irq_desc.dev_id->rescnt, 2);
					
				} /* endif */
			}
		}
		else
		{
			ret                         = -EIO;
			ioinfo[irq]->devstat.flag  &= ~DEVSTAT_NOT_OPER;
			ioinfo[irq]->ui.flags.oper  = 1;

		} /* endif */

		break;

	case 2 :            /* busy */

		ret = -EBUSY;
		break;

	default:            /* device/path not operational */
		
		if ( flag & DOIO_VALID_LPM )
		{
			ioinfo[irq]->opm &= ~lpm;
		}
		else
		{
			ioinfo[irq]->opm = 0;

		} /* endif */
	
		if ( ioinfo[irq]->opm == 0 ) 	
		{
			ioinfo[irq]->ui.flags.oper  = 0;
			ioinfo[irq]->devstat.flag  |= DEVSTAT_NOT_OPER;

		} /* endif */

		ret = -ENODEV;

		memcpy( ioinfo[irq]->irq_desc.dev_id,
		        &(ioinfo[irq]->devstat),
		        sizeof( devstat_t) );


#ifdef CONFIG_DEBUG_IO

   			stsch(irq, &(ioinfo[irq]->schib) );

			sprintf( buffer, "s390_start_IO(%04X) - schib for "
			         "device %04X, after 'not oper' status\n",
			         irq,
			         ioinfo[irq]->devstat.devno );

			s390_displayhex( buffer,
			                 &(ioinfo[irq]->schib),
			                 sizeof(schib_t));
#endif
		if (cio_debug_initialized) {
			stsch(irq, &(ioinfo[irq]->schib) );

			sprintf( buffer, "s390_start_IO(%04X) - schib for "
			         "device %04X, after 'not oper' status\n",
			         irq,
			         ioinfo[irq]->devstat.devno );

			s390_displayhex2( buffer,
			                 &(ioinfo[irq]->schib),
			                 sizeof(schib_t), 2);
		}
			
		break;

	} /* endswitch */

	if ( flag & DOIO_WAIT_FOR_INTERRUPT)
	{
		disable_cpu_sync_isc( irq );

	} /* endif */

	if ( flag & DOIO_DONT_CALL_INTHDLR )
	{
		ioinfo[irq]->ui.flags.repnone = 0;

   } /* endif */

	return( ret);
}

int do_IO( int            irq,          /* IRQ */
           ccw1_t        *cpa,          /* channel program address */
           unsigned long  user_intparm, /* interruption parameter */
           __u8           lpm,          /* logical path mask */
           unsigned long  flag)         /* flags : see above */
{
	int ret = 0;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	/* handler registered ? or free_irq() in process already ? */
	if ( !ioinfo[irq]->ui.flags.ready || ioinfo[irq]->ui.flags.unready )
	{
		return( -ENODEV );

	} /* endif */

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "doIO");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * Note: We ignore the device operational status - if not operational,
	 *        the SSCH will lead to an -ENODEV condition ...
	 */
	if ( !ioinfo[irq]->ui.flags.busy )         /* last I/O completed ? */
	{
		ret = s390_start_IO( irq, cpa, user_intparm, lpm, flag);
	}
	else if ( ioinfo[irq]->ui.flags.fast )
	{
		/*
		 * If primary status was received and ending status is missing,
		 *  the device driver won't be notified on the ending status
		 *  if early (fast) interrupt notification was requested.
		 *  Therefore we have to queue the next incoming request. If
		 *  halt_IO() is issued while there is a request queued, a HSCH
		 *  needs to be issued and the queued request must be deleted
		 *  but its intparm must be returned (see halt_IO() processing)
		 */
		if (     ioinfo[irq]->ui.flags.w4final
		      && !ioinfo[irq]->ui.flags.doio_q )
		{
			ioinfo[irq]->qflag    = flag;
			ioinfo[irq]->qcpa     = cpa;
			ioinfo[irq]->qintparm = user_intparm;
			ioinfo[irq]->qlpm     = lpm;
		}
		else
		{
			ret = -EBUSY;

		} /* endif */
	}
	else
	{
		ret = -EBUSY;

	} /* endif */

	return( ret );

}

/*
 * resume suspended I/O operation
 */
int resume_IO( int irq)
{
	int ret = 0;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "resIO");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * We allow for 'resume' requests only for active I/O operations
	 */
	if ( ioinfo[irq]->ui.flags.busy )
	{
		int ccode;

		ccode = rsch( irq);

		switch (ccode) {
		case 0 :
			break;

		case 1 :
		  	s390_process_IRQ( irq );
			ret = -EBUSY;
			break;

		case 2 :
			ret = -EINVAL;
			break;

		case 3 :
			/*
			 * useless to wait for request completion
			 *  as device is no longer operational !
			 */
			ioinfo[irq]->ui.flags.oper = 0;
			ioinfo[irq]->ui.flags.busy = 0;
			ret                        = -ENODEV;
			break;

		} /* endswitch */          	
		
	}
	else
	{
		ret = -ENOTCONN;

	} /* endif  */

	return( ret);
}

/*
 * Note: The "intparm" parameter is not used by the halt_IO() function
 *       itself, as no ORB is built for the HSCH instruction. However,
 *       it allows the device interrupt handler to associate the upcoming
 *       interrupt with the halt_IO() request.
 */
int halt_IO( int           irq,
             unsigned long user_intparm,
             unsigned long flag)  /* possible DOIO_WAIT_FOR_INTERRUPT */
{
	int            ret;
	int            ccode;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	/*
	 * we only allow for halt_IO if the device has an I/O handler associated
	 */
	if ( !ioinfo[irq]->ui.flags.ready )
	{
		ret = -ENODEV;
	}
	/*
	 * we ignore the halt_io() request if ending_status was received but
	 *  a SENSE operation is waiting for completion.
	 */
	else if ( ioinfo[irq]->ui.flags.w4sense )
	{
		ret = 0;
	}
#if 0
	/*
	 * We don't allow for halt_io with a sync do_IO() requests pending.
	 */
	else if (    ioinfo[irq]->ui.flags.syncio
	          && (flag & DOIO_WAIT_FOR_INTERRUPT))
	{
		ret = -EBUSY;
	}
#endif
	else
	{
		if (cio_debug_initialized) {
			debug_text_event(cio_debug_trace_id, 2, "haltIO");
			sprintf(dbf_txt, "%x", irq);
			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
		}
		/*
		 * If sync processing was requested we lock the sync ISC,
		 *  modify the device to present interrupts for this ISC only
		 *  and switch the CPU to handle this ISC + the console ISC
		 *  exclusively.
		 */
		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
		{
			ret = enable_cpu_sync_isc( irq);

			if ( ret )
			{
				return( ret);
  	
			} /* endif */

		} /* endif */

		/*
		 * Issue "Halt subchannel" and process condition code
		 */
		ccode = hsch( irq );

		switch ( ccode ) {
		case 0:

			ioinfo[irq]->ui.flags.haltio = 1;

			if ( !ioinfo[irq]->ui.flags.doio )
			{
				ioinfo[irq]->ui.flags.busy   = 1;
				ioinfo[irq]->u_intparm       = user_intparm;
				ioinfo[irq]->devstat.cstat   = 0;
				ioinfo[irq]->devstat.dstat   = 0;
				ioinfo[irq]->devstat.lpum    = 0;
				ioinfo[irq]->devstat.flag    = DEVSTAT_HALT_FUNCTION;
				ioinfo[irq]->devstat.scnt    = 0;

			}
			else
			{
				ioinfo[irq]->devstat.flag   |= DEVSTAT_HALT_FUNCTION;

			} /* endif */

			/*
			 * If synchronous I/O processing is requested, we have
			 *  to wait for the corresponding interrupt to occur by
			 *  polling the interrupt condition. However, as multiple
			 *  interrupts may be outstanding, we must not just wait
			 *  for the first interrupt, but must poll until ours
			 *  pops up.
			 */
			if ( flag & DOIO_WAIT_FOR_INTERRUPT )
			{
				int              io_sub;
				__u32            io_parm;
				psw_t            io_new_psw;
				int              ccode;
  	
				int              ready = 0;
				struct _lowcore *lc    = NULL;

				/*
				 * We shouldn't perform a TPI loop, waiting for
				 *  an interrupt to occur, but should load a
				 *  WAIT PSW instead. Otherwise we may keep the
				 *  channel subsystem busy, not able to present
				 *  the interrupt. When our sync. interrupt
				 *  arrived we reset the I/O old PSW to its
				 *  original value.
				 */
				memcpy( &io_new_psw,
				        &lc->io_new_psw,
				        sizeof(psw_t));

				ccode = iac();

				switch (ccode) {
				case 0:  		// primary-space
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_PRIM_SPACE_MODE
					                    | _PSW_IO_WAIT;
					break;
				case 1:			// secondary-space
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_SEC_SPACE_MODE
					                    | _PSW_IO_WAIT;
					break;
				case 2:			// access-register
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_ACC_REG_MODE
					                    | _PSW_IO_WAIT;
					break;
				case 3:			// home-space	
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_HOME_SPACE_MODE
					                    | _PSW_IO_WAIT;
					break;
				default:
					panic( "halt_IO() : unexpected "
					       "address-space-control %d\n",
					       ccode);
					break;
				} /* endswitch */

				io_sync_wait.addr = FIX_PSW(&&hio_wakeup);

				/*
				 * Martin didn't like modifying the new PSW, now we take
				 *  a fast exit in do_IRQ() instead
				 */
				*(__u32 *)__LC_SYNC_IO_WORD  = 1;

				do
				{

					__load_psw( io_sync_wait );
hio_wakeup:
				   io_parm = *(__u32 *)__LC_IO_INT_PARM;
				   io_sub  = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR;

				   ready = s390_process_IRQ( io_sub );

				} while ( !((io_sub == irq) && (ready == 1)) );

				*(__u32 *)__LC_SYNC_IO_WORD = 0;

			} /* endif */

			ret = 0;
			break;

		case 1 :            /* status pending */
	
			ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING;

			/*
			 * initialize the device driver specific devstat irb area
			 */
			memset( &ioinfo[irq]->irq_desc.dev_id->ii.irb,
			        '\0', sizeof( irb_t) );

			/*
			 * Let the common interrupt handler process the pending
			 *  status. However, we must avoid calling the user
			 *  action handler, as it won't be prepared to handle
			 *  a pending status during do_IO() processing inline.
			 *  This also implies that s390_process_IRQ must
			 *  terminate synchronously - especially if device
			 *  sensing is required.
			 */
			ioinfo[irq]->ui.flags.s_pend   = 1;
			ioinfo[irq]->ui.flags.busy     = 1;
			ioinfo[irq]->ui.flags.doio     = 1;

			s390_process_IRQ( irq );
			
			ioinfo[irq]->ui.flags.s_pend   = 0;
			ioinfo[irq]->ui.flags.busy     = 0;
			ioinfo[irq]->ui.flags.doio     = 0;
			ioinfo[irq]->ui.flags.repall   = 0;
			ioinfo[irq]->ui.flags.w4final  = 0;

			ioinfo[irq]->devstat.flag     |= DEVSTAT_FINAL_STATUS;

			/*
			 * In multipath mode a condition code 3 implies the last
			 *  path has gone, except we have previously restricted
			 *  the I/O to a particular path. A condition code 1
			 *  (0 won't occur) results in return code EIO as well
			 *  as 3 with another path than the one used (i.e. path available mask is non-zero).
			 */
			if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 )
			{
				ret                         = -ENODEV;
				ioinfo[irq]->devstat.flag  |= DEVSTAT_NOT_OPER;
				ioinfo[irq]->ui.flags.oper  = 0;
			}
			else
			{
				ret                         = -EIO;
				ioinfo[irq]->devstat.flag  &= ~DEVSTAT_NOT_OPER;
				ioinfo[irq]->ui.flags.oper  = 1;

			} /* endif */

			break;

		case 2 :            /* busy */

			ret = -EBUSY;
			break;

		default:            /* device not operational */

			ret = -ENODEV;
			break;

		} /* endswitch */

		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
		{
			disable_cpu_sync_isc( irq );

		} /* endif */

	} /* endif */

	return( ret );
}

/*
 * Note: The "intparm" parameter is not used by the clear_IO() function
 *       itself, as no ORB is built for the CSCH instruction. However,
 *       it allows the device interrupt handler to associate the upcoming
 *       interrupt with the clear_IO() request.
 */
int clear_IO( int           irq,
              unsigned long user_intparm,
              unsigned long flag)  /* possible DOIO_WAIT_FOR_INTERRUPT */
{
	int            ret = 0;
	int            ccode;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
	{
		return( -ENODEV);
   }

	/*
	 * we only allow for clear_IO if the device has an I/O handler associated
	 */
	if ( !ioinfo[irq]->ui.flags.ready )
	{
		ret = -ENODEV;
	}
	/*
	 * we ignore the clear_io() request if ending_status was received but
	 *  a SENSE operation is waiting for completion.
	 */
	else if ( ioinfo[irq]->ui.flags.w4sense )
	{
		ret = 0;
	}
#if 0
	/*
	 * We don't allow for clear_io with a sync do_IO() requests pending.
	 *  Concurrent I/O is possible in SMP environments only, but the
	 *  sync. I/O request can be gated to one CPU at a time only.
	 */
	else if ( ioinfo[irq]->ui.flags.syncio )
	{
		ret = -EBUSY;
	}
#endif
	else
	{
		if (cio_debug_initialized) {
			debug_text_event(cio_debug_trace_id, 2, "clearIO");
			sprintf(dbf_txt, "%x", irq);
			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
		}
		/*
		 * If sync processing was requested we lock the sync ISC,
		 *  modify the device to present interrupts for this ISC only
		 *  and switch the CPU to handle this ISC + the console ISC
		 *  exclusively.
		 */
		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
		{
			ret = enable_cpu_sync_isc( irq);

			if ( ret )
			{
				return( ret);
  	
			} /* endif */

		} /* endif */

		/*
		 * Issue "Clear subchannel" and process condition code
		 */
		ccode = csch( irq );

		switch ( ccode ) {
		case 0:

			ioinfo[irq]->ui.flags.haltio = 1;

			if ( !ioinfo[irq]->ui.flags.doio )
			{
				ioinfo[irq]->ui.flags.busy   = 1;
				ioinfo[irq]->u_intparm       = user_intparm;
				ioinfo[irq]->devstat.cstat   = 0;
				ioinfo[irq]->devstat.dstat   = 0;
				ioinfo[irq]->devstat.lpum    = 0;
				ioinfo[irq]->devstat.flag    = DEVSTAT_CLEAR_FUNCTION;
				ioinfo[irq]->devstat.scnt    = 0;

			}
			else
			{
				ioinfo[irq]->devstat.flag   |= DEVSTAT_CLEAR_FUNCTION;

			} /* endif */

			/*
			 * If synchronous I/O processing is requested, we have
			 *  to wait for the corresponding interrupt to occur by
			 *  polling the interrupt condition. However, as multiple
			 *  interrupts may be outstanding, we must not just wait
			 *  for the first interrupt, but must poll until ours
			 *  pops up.
			 */
			if ( flag & DOIO_WAIT_FOR_INTERRUPT )
			{
				int              io_sub;
				__u32            io_parm;
				psw_t            io_new_psw;
				int              ccode;
  	
				int              ready = 0;
				struct _lowcore *lc    = NULL;

				/*
				 * We shouldn't perform a TPI loop, waiting for
				 *  an interrupt to occur, but should load a
				 *  WAIT PSW instead. Otherwise we may keep the
				 *  channel subsystem busy, not able to present
				 *  the interrupt. When our sync. interrupt
				 *  arrived we reset the I/O old PSW to its
				 *  original value.
				 */
				memcpy( &io_new_psw,
				        &lc->io_new_psw,
				        sizeof(psw_t));

				ccode = iac();

				switch (ccode) {
				case 0:  		// primary-space
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_PRIM_SPACE_MODE
					                    | _PSW_IO_WAIT;
					break;
				case 1:			// secondary-space
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_SEC_SPACE_MODE
					                    | _PSW_IO_WAIT;
					break;
				case 2:			// access-register
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_ACC_REG_MODE
					                    | _PSW_IO_WAIT;
					break;
				case 3:			// home-space	
					io_sync_wait.mask =   _IO_PSW_MASK
					                    | _PSW_HOME_SPACE_MODE
					                    | _PSW_IO_WAIT;
					break;
				default:
					panic( "clear_IO() : unexpected "
					       "address-space-control %d\n",
					       ccode);
					break;
				} /* endswitch */

				io_sync_wait.addr = FIX_PSW(&&cio_wakeup);

				/*
				 * Martin didn't like modifying the new PSW, now we take
				 *  a fast exit in do_IRQ() instead
				 */
				*(__u32 *)__LC_SYNC_IO_WORD  = 1;

				do
				{

					__load_psw( io_sync_wait );
cio_wakeup:
				   io_parm = *(__u32 *)__LC_IO_INT_PARM;
				   io_sub  = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR;

				   ready = s390_process_IRQ( io_sub );

				} while ( !((io_sub == irq) && (ready == 1)) );

				*(__u32 *)__LC_SYNC_IO_WORD = 0;

			} /* endif */

			ret = 0;
			break;

		case 1 :            /* no status pending for csh */
			BUG();
			break;


		case 2 :            /* no busy for csh*/
			BUG();
			break;

		default:            /* device not operational */

			ret = -ENODEV;
			break;

		} /* endswitch */

		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
		{
			disable_cpu_sync_isc( irq );

		} /* endif */

	} /* endif */

	return( ret );
}


/*
 * do_IRQ() handles all normal I/O device IRQ's (the special
 *          SMP cross-CPU interrupts have their own specific
 *          handlers).
 *
 */
asmlinkage void do_IRQ( struct pt_regs regs )
{
	/*
	 * Get interrupt info from lowcore
	 */
	volatile tpi_info_t *tpi_info = (tpi_info_t*)(__LC_SUBCHANNEL_ID);
	int cpu = smp_processor_id();

	/*
	 * take fast exit if CPU is in sync. I/O state
	 *
	 * Note: we have to turn off the WAIT bit and re-disable
	 *       interrupts prior to return as this was the initial
	 *       entry condition to synchronous I/O.
	 */
 	if (    *(__u32 *)__LC_SYNC_IO_WORD )
	{
		regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT);
		return;
	} /* endif */

#ifdef CONFIG_FAST_IRQ
	do {
#endif /*  CONFIG_FAST_IRQ */
		
		/*
		 * Non I/O-subchannel thin interrupts are processed differently
		 */
		if ( tpi_info->adapter_IO == 1 &&
		     tpi_info->int_type == IO_INTERRUPT_TYPE )
		{
			irq_enter(cpu, -1);
			do_adapter_IO( tpi_info->intparm );
			irq_exit(cpu, -1);
		} 
		else 
		{
			unsigned int irq = tpi_info->irq;
	
			/*
			 * fix me !!!
			 *
			 * instead of boxing the device, we need to schedule device
			 * recognition, the interrupt stays pending. We need to
			 * dynamically allocate an ioinfo structure, etc..
			 */
			if ( ioinfo[irq] == INVALID_STORAGE_AREA )
			{
				return;	/* this keeps the device boxed ... */
			}
	
			irq_enter(cpu, irq);
			s390irq_spin_lock( irq );
			s390_process_IRQ( irq );
			s390irq_spin_unlock( irq );
			irq_exit(cpu, irq);
		}

#ifdef CONFIG_FAST_IRQ

		/*
		 * Are more interrupts pending?
		 * If so, the tpi instruction will update the lowcore 
		 * to hold the info for the next interrupt.
		 */
	} while ( tpi( NULL ) != 0 );

#endif /*  CONFIG_FAST_IRQ */

	return;
}


/*
 * s390_process_IRQ() handles status pending situations and interrupts
 *
 * Called by : do_IRQ()             - for "real" interrupts
 *             s390_start_IO, halt_IO()
 *                                  - status pending cond. after SSCH, or HSCH
 *             disable_subchannel() - status pending conditions (after MSCH)
 *
 * Returns: 0 - no ending status received, no further action taken
 *          1 - interrupt handler was called with ending status
 */
int s390_process_IRQ( unsigned int irq )
{
	int                    ccode;      /* cond code from tsch() operation */
	int                    irb_cc;     /* cond code from irb */
	int                    sdevstat;   /* struct devstat size to copy */
	unsigned int           fctl;       /* function control */
	unsigned int           stctl;      /* status   control */
	unsigned int           actl;       /* activity control */

	int               issense         = 0;
	int               ending_status   = 0;
	int               allow4handler   = 1;
	int               chnchk          = 0;
	devstat_t        *dp;
	devstat_t        *udp;

	char dbf_txt[15];
	char buffer[80];


	if (cio_count_irqs) {
		int cpu = smp_processor_id();
		s390_irq_count[cpu]++;
	}
	
	

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 3, "procIRQ");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 3, dbf_txt);
	}

	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
	{
		/* we can't properly process the interrupt ... */
		tsch( irq, p_init_irb );
		return( 1 );

	} /* endif */

	dp  = &ioinfo[irq]->devstat;
	udp = ioinfo[irq]->irq_desc.dev_id;
	

	/*
	 * It might be possible that a device was not-oper. at the time
	 *  of free_irq() processing. This means the handler is no longer
	 *  available when the device possibly becomes ready again. In
	 *  this case we perform delayed disable_subchannel() processing.
	 */
	if ( !ioinfo[irq]->ui.flags.ready )
	{
		if ( !ioinfo[irq]->ui.flags.d_disable )
		{
#ifdef CONFIG_DEBUG_IO
			printk( KERN_CRIT"s390_process_IRQ(%04X) "
			        "- no interrupt handler registered "
					  "for device %04X !\n",
			        irq,
			        ioinfo[irq]->devstat.devno);
#endif /* CONFIG_DEBUG_IO */
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_msg_id, 0,
						    "s390_process_IRQ(%04X) - no interrupt handler registered for device %04X !\n",
						    irq, ioinfo[irq]->devstat.devno);
		} /* endif */

	} /* endif */

	/*
	 * retrieve the i/o interrupt information (irb),
	 *  update the device specific status information
	 *  and possibly call the interrupt handler.
	 *
	 * Note 1: At this time we don't process the resulting
	 *         condition code (ccode) from tsch(), although
	 *         we probably should.
	 *
	 * Note 2: Here we will have to check for channel
	 *         check conditions and call a channel check
	 *         handler.
	 *
	 * Note 3: If a start function was issued, the interruption
	 *         parameter relates to it. If a halt function was
	 *         issued for an idle device, the intparm must not
	 *         be taken from lowcore, but from the devstat area.
	 */
	ccode = tsch( irq, &(dp->ii.irb) );

	//
	// We must only accumulate the status if the device is busy already
	//
	if ( ioinfo[irq]->ui.flags.busy )
	{
		dp->dstat   |= dp->ii.irb.scsw.dstat;
		dp->cstat   |= dp->ii.irb.scsw.cstat;
                dp->intparm  = ioinfo[irq]->u_intparm;

	}
	else
	{
		dp->dstat    = dp->ii.irb.scsw.dstat;
		dp->cstat    = dp->ii.irb.scsw.cstat;

		dp->flag     = 0;   // reset status flags
		dp->intparm  = 0;

	} /* endif */

	dp->lpum = dp->ii.irb.esw.esw1.lpum;

	/*
	 * reset device-busy bit if no longer set in irb
	 */
	if (    (dp->dstat & DEV_STAT_BUSY                   ) 
	     && ((dp->ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0))
	{
		dp->dstat &= ~DEV_STAT_BUSY;

	} /* endif */

	/*
	 * Save residual count and CCW information in case primary and
	 *  secondary status are presented with different interrupts.
	 */
	if ( dp->ii.irb.scsw.stctl
	     & ( SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_INTER_STATUS ) ) {

		/*
		 * If the subchannel status shows status pending
		 * and we received a check condition, the count
		 * information is not meaningful.
		 */
		
		 if ( !(    (dp->ii.irb.scsw.stctl & SCSW_STCTL_STATUS_PEND) 
			 && (   dp->ii.irb.scsw.cstat 
			      & (   SCHN_STAT_CHN_DATA_CHK
				  | SCHN_STAT_CHN_CTRL_CHK
				  | SCHN_STAT_INTF_CTRL_CHK
				  | SCHN_STAT_PROG_CHECK
				  | SCHN_STAT_PROT_CHECK
				  | SCHN_STAT_CHAIN_CHECK )))) {

			 dp->rescnt = dp->ii.irb.scsw.count;
		 } else {
			 dp->rescnt = SENSE_MAX_COUNT;
		 }

		dp->cpa    = dp->ii.irb.scsw.cpa;

#ifdef CONFIG_DEBUG_IO
		if ( irq != cons_dev )
			printk( "s390_process_IRQ( %04X ) : "
			        "residual count from irb after tsch() %d\n",
			        irq, dp->rescnt );
#endif
		if (cio_debug_initialized)
			debug_sprintf_event(cio_debug_msg_id, 6,
					    "s390_process_IRQ( %04X ) : residual count from irq after tsch() %d\n",
					    irq, dp->rescnt);

	} /* endif */

	irb_cc = dp->ii.irb.scsw.cc;

	//
	// check for any kind of channel or interface control check but don't
	//  issue the message for the console device
	//
	if (    (dp->ii.irb.scsw.cstat
	            & (  SCHN_STAT_CHN_DATA_CHK
	               | SCHN_STAT_CHN_CTRL_CHK
			 | SCHN_STAT_INTF_CTRL_CHK )))
	{
		if (irq != cons_dev)
			printk( "Channel-Check or Interface-Control-Check "
				"received\n"
				" ... device %04X on subchannel %04X, dev_stat "
				": %02X sch_stat : %02X\n",
				ioinfo[irq]->devstat.devno,
				irq,
				dp->dstat,
				dp->cstat);
		if (cio_debug_initialized) {
			debug_sprintf_event(cio_debug_msg_id, 0,
					    "Channel-Check or Interface-Control-Check received\n");
			debug_sprintf_event(cio_debug_msg_id, 0,
					    "... device %04X on subchannel %04X, dev_stat: %02X sch_stat: %02X\n",
					    ioinfo[irq]->devstat.devno, irq, dp->dstat, dp->cstat);
		}

		chnchk = 1;

	} /* endif */

	if( dp->ii.irb.scsw.ectl==0)
	{
		issense=0;
	}
	else if (    (dp->ii.irb.scsw.stctl == SCSW_STCTL_STATUS_PEND)
	          && (dp->ii.irb.scsw.eswf  == 0                     ))
	{
		issense = 0;
	}
	else if (   (dp->ii.irb.scsw.stctl ==
		      (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_INTER_STATUS)) 
		 && ((dp->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED) == 0)  )
	{
		issense = 0;
	}
	else
	{
		issense = dp->ii.irb.esw.esw0.erw.cons;

	} /* endif */

	if ( issense )
	{
		dp->scnt  = dp->ii.irb.esw.esw0.erw.scnt;
		dp->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
                  	
		sdevstat = sizeof( devstat_t);

#ifdef CONFIG_DEBUG_IO
		if ( irq != cons_dev )
			printk( "s390_process_IRQ( %04X ) : "
			        "concurrent sense bytes avail %d\n",
			        irq, dp->scnt );
#endif
		if (cio_debug_initialized)
			debug_sprintf_event(cio_debug_msg_id, 4,
					    "s390_process_IRQ( %04X ): concurrent sense bytes avail %d\n",
					    irq, dp->scnt);
	}
	else
	{
		/* don't copy the sense data area ! */
		sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT;

	} /* endif */

	switch ( irb_cc ) {
	case 1:      /* status pending */

		dp->flag |= DEVSTAT_STATUS_PENDING;

	case 0:      /* normal i/o interruption */

		fctl  = dp->ii.irb.scsw.fctl;
		stctl = dp->ii.irb.scsw.stctl;
		actl  = dp->ii.irb.scsw.actl;

		if ( chnchk )
		{
			sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
			         "device %04X after channel check\n",
			         irq,
			         dp->devno );

			s390_displayhex( buffer,
			                 &(dp->ii.irb) ,
			                 sizeof(irb_t));
			if (cio_debug_initialized) {
				
				sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
			         "device %04X after channel check\n",
			         irq,
			         dp->devno );

				s390_displayhex2( buffer,
						  &(dp->ii.irb) ,
						  sizeof(irb_t), 0);
			}
		} /* endif */
			
		ioinfo[irq]->stctl |= stctl;

		ending_status =    ( stctl & SCSW_STCTL_SEC_STATUS                          )
			|| ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)         )
			|| ( (fctl == SCSW_FCTL_HALT_FUNC)  && (stctl == SCSW_STCTL_STATUS_PEND) )
			|| ( (fctl == SCSW_FCTL_CLEAR_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) );

		/*
		 * Check for unsolicited interrupts - for debug purposes only
		 *
		 * We only consider an interrupt as unsolicited, if the device was not
		 *  actively in use (busy) and an interrupt other than an ALERT status
		 *  was received.
		 *
		 * Note: We must not issue a message to the console, if the
		 *       unsolicited interrupt applies to the console device
		 *       itself !
		 */
		if (    !( stctl & SCSW_STCTL_ALERT_STATUS )
		     &&  ( ioinfo[irq]->ui.flags.busy == 0 ) )
		{
#ifdef CONFIG_DEBUG_IO
			if (irq != cons_dev)
				printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n"
					" ... device status : %02X subchannel status : %02X\n",
					dp->devno,
					irq,
					dp->dstat,
					dp->cstat);
			
			sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
			         "device %04X, ending_status %d\n",
			         irq,
			         dp->devno,
			         ending_status);

			s390_displayhex( buffer,
			                 &(dp->ii.irb) ,
			                 sizeof(irb_t));
#endif
			if (cio_debug_initialized) {
				debug_sprintf_event(cio_debug_msg_id, 2, 
						    "Unsolicited interrupt received for device %04X on subchannel %04X\n"
						    " ... device status : %02X subchannel status : %02X\n",
						    dp->devno,
						    irq,
						    dp->dstat,
						    dp->cstat);
				sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
					 "device %04X, ending_status %d\n",
					 irq,
					 dp->devno,
					 ending_status);
				
				s390_displayhex2( buffer,
						 &(dp->ii.irb) ,
						 sizeof(irb_t), 2);	
			}
		} /* endif */

		/*
		 * take fast exit if no handler is available
		 */
		if ( !ioinfo[irq]->ui.flags.ready )
			return( ending_status );     		

		/*
		 * Check whether we must issue a SENSE CCW ourselves if there is no
		 *  concurrent sense facility installed for the subchannel.
		 *
		 * Note: We should check for ioinfo[irq]->ui.flags.consns but VM
		 *       violates the ESA/390 architecture and doesn't present an
		 *       operand exception for virtual devices without concurrent
		 *       sense facility available/supported when enabling the
		 *       concurrent sense facility.
		 */
		if (    (    (dp->ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK )
		          && (!issense                                    ) )
		     || (ioinfo[irq]->ui.flags.delsense && ending_status    ) )
		{
			int            ret_io;
			ccw1_t        *s_ccw  = &ioinfo[irq]->senseccw;
			unsigned long  s_flag = 0;

			if ( ending_status )
			{
				/*
				 * We copy the current status information into the device driver
				 *  status area. Then we can use the local devstat area for device
				 *  sensing. When finally calling the IRQ handler we must not overlay
				 *  the original device status but copy the sense data only.
				 */
				memcpy( udp, dp, sizeof( devstat_t) );

				s_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
				s_ccw->cda      = (__u32)virt_to_phys( ioinfo[irq]->sense_data );
				s_ccw->count    = SENSE_MAX_COUNT;
				s_ccw->flags    = CCW_FLAG_SLI;

				/*
				 * If free_irq() or a sync do_IO/s390_start_IO() is in
				 *  process we have to sense synchronously
				 */
				if ( ioinfo[irq]->ui.flags.unready || ioinfo[irq]->ui.flags.syncio )
				{
					s_flag = DOIO_WAIT_FOR_INTERRUPT;

				} /* endif */

				/*
				 * Reset status info
				 *
				 * It does not matter whether this is a sync. or async.
				 *  SENSE request, but we have to assure we don't call
				 *  the irq handler now, but keep the irq in busy state.
				 *  In sync. mode s390_process_IRQ() is called recursively,
				 *  while in async. mode we re-enter do_IRQ() with the
				 *  next interrupt.
				 *
				 * Note : this may be a delayed sense request !
				 */
				allow4handler                  = 0;

				ioinfo[irq]->ui.flags.fast     = 0;
				ioinfo[irq]->ui.flags.repall   = 0;
				ioinfo[irq]->ui.flags.w4final  = 0;
				ioinfo[irq]->ui.flags.delsense = 0;

				dp->cstat     = 0;
				dp->dstat     = 0;
				dp->rescnt    = SENSE_MAX_COUNT;

				ioinfo[irq]->ui.flags.w4sense  = 1;
			
				ret_io = s390_start_IO( irq,
				                        s_ccw,
				                        0xE2C5D5E2,  // = SENSe
				                        0,           // n/a
				                        s_flag);
			}
			else
			{
				/*
				 * we received an Unit Check but we have no final
				 *  status yet, therefore we must delay the SENSE
				 *  processing. However, we must not report this
				 *  intermediate status to the device interrupt
				 *  handler.
				 */
				ioinfo[irq]->ui.flags.fast     = 0;
				ioinfo[irq]->ui.flags.repall   = 0;

				ioinfo[irq]->ui.flags.delsense = 1;
				allow4handler                  = 0;

			} /* endif */

		} /* endif */

		/*
		 * we allow for the device action handler if .
		 *  - we received ending status
		 *  - the action handler requested to see all interrupts
		 *  - we received an intermediate status
		 *  - fast notification was requested (primary status)
		 *  - unsollicited interrupts
		 *
		 */
		if ( allow4handler )
		{
			allow4handler =    ending_status
				|| ( ioinfo[irq]->ui.flags.repall                                      )
				|| ( stctl & SCSW_STCTL_INTER_STATUS                                   )
				|| ( (ioinfo[irq]->ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) )
				|| ( ioinfo[irq]->ui.flags.oper == 0                                   );

		} /* endif */

		/*
		 * We used to copy the device status information right before
		 *  calling the device action handler. However, in status
		 *  pending situations during do_IO() or halt_IO(), as well as
		 *  enable_subchannel/disable_subchannel processing we must
		 *  synchronously return the status information and must not
		 *  call the device action handler.
		 *
		 */
		if ( allow4handler )
		{
			/*
			 * if we were waiting for sense data we copy the sense
			 *  bytes only as the original status information was
			 *  saved prior to sense already.
			 */
			if ( ioinfo[irq]->ui.flags.w4sense )
			{
				int sense_count = SENSE_MAX_COUNT-ioinfo[irq]->devstat.rescnt;

#ifdef CONFIG_DEBUG_IO
				if ( irq != cons_dev )
					printk( "s390_process_IRQ( %04X ) : "
						"BASIC SENSE bytes avail %d\n",
						irq, sense_count );
#endif
				if (cio_debug_initialized)
					debug_sprintf_event(cio_debug_msg_id, 4,
							    "s390_process_IRQ( %04X ): BASIC SENSE bytes avail %d\n",
							    irq, sense_count);
				ioinfo[irq]->ui.flags.w4sense = 0;
				udp->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
				udp->scnt  = sense_count;

				if ( sense_count >= 0 )
				{
					memcpy( udp->ii.sense.data,
					        ioinfo[irq]->sense_data,
					        sense_count);
				}
				else
				{
					panic( "s390_process_IRQ(%04x) encountered "
					       "negative sense count\n",
					       irq);

				} /* endif */
			}
			else
			{
				memcpy( udp, dp, sdevstat );

			}  /* endif */

		} /* endif */

		/*
		 * for status pending situations other than deferred interrupt
		 *  conditions detected by s390_process_IRQ() itself we must not
		 *  call the handler. This will synchronously be reported back
		 *  to the caller instead, e.g. when detected during do_IO().
		 */
		if (    ioinfo[irq]->ui.flags.s_pend
		     || ioinfo[irq]->ui.flags.unready
		     || ioinfo[irq]->ui.flags.repnone )
		{		
			if ( ending_status )
			{

				ioinfo[irq]->ui.flags.busy     = 0;
				ioinfo[irq]->ui.flags.doio     = 0;
				ioinfo[irq]->ui.flags.haltio   = 0;
				ioinfo[irq]->ui.flags.fast     = 0;
				ioinfo[irq]->ui.flags.repall   = 0;
				ioinfo[irq]->ui.flags.w4final  = 0;

				dp->flag  |= DEVSTAT_FINAL_STATUS;
				udp->flag |= DEVSTAT_FINAL_STATUS;

			} /* endif */

			allow4handler = 0;

		} /* endif */

		/*
		 * Call device action handler if applicable
		 */
		if ( allow4handler )
		{

			/*
			 *  We only reset the busy condition when we are sure that no further
			 *   interrupt is pending for the current I/O request (ending_status).
			 */
			if ( ending_status || !ioinfo[irq]->ui.flags.oper )
			{
				ioinfo[irq]->ui.flags.oper     = 1;  /* dev IS oper */

				ioinfo[irq]->ui.flags.busy     = 0;
				ioinfo[irq]->ui.flags.doio     = 0;
				ioinfo[irq]->ui.flags.haltio   = 0;
				ioinfo[irq]->ui.flags.fast     = 0;
				ioinfo[irq]->ui.flags.repall   = 0;
				ioinfo[irq]->ui.flags.w4final  = 0;

				dp->flag  |= DEVSTAT_FINAL_STATUS;
				udp->flag |= DEVSTAT_FINAL_STATUS;

				ioinfo[irq]->irq_desc.handler( irq, udp, NULL );

				//
				// reset intparm after final status or we will badly present unsolicited
				//  interrupts with a intparm value possibly no longer valid.
				//
				dp->intparm   = 0;

				//
				// Was there anything queued ? Start the pending channel program
				//  if there is one.
				//
				if ( ioinfo[irq]->ui.flags.doio_q )
				{
					int ret;

					ret = s390_start_IO( irq,
					                     ioinfo[irq]->qcpa,
					                     ioinfo[irq]->qintparm,
						             ioinfo[irq]->qlpm,
					                     ioinfo[irq]->qflag);

					ioinfo[irq]->ui.flags.doio_q = 0;

					/*
					 * If s390_start_IO() failed call the device's interrupt
					 *  handler, the IRQ related devstat area was setup by
					 *  s390_start_IO() accordingly already (status pending
					 *  condition).
					 */
					if ( ret )
					{
						ioinfo[irq]->irq_desc.handler( irq, udp, NULL );

					} /* endif */

				} /* endif */

			}
			else
			{
				ioinfo[irq]->ui.flags.w4final = 1;

				/*
				 * Eventually reset subchannel PCI status and
				 *  set the PCI or SUSPENDED flag in the user
				 *  device status block if appropriate.
				 */
				if ( dp->cstat & SCHN_STAT_PCI )
				{
					udp->flag |= DEVSTAT_PCI;
					dp->cstat &= ~SCHN_STAT_PCI;
				}

				if ( actl & SCSW_ACTL_SUSPENDED )
				{
					udp->flag |= DEVSTAT_SUSPENDED;

				} /* endif */

				ioinfo[irq]->irq_desc.handler( irq, udp, NULL );

			} /* endif */

		} /* endif */

		break;

	case 3:      /* device/path not operational */

		ioinfo[irq]->ui.flags.busy    = 0;
		ioinfo[irq]->ui.flags.doio    = 0;
		ioinfo[irq]->ui.flags.haltio  = 0;

		dp->cstat    = 0;
		dp->dstat    = 0;

		if ( ioinfo[irq]->ulpm != ioinfo[irq]->opm )
		{
			/*
			 * either it was the only path or it was restricted ...
			 */
			ioinfo[irq]->opm &= ~(ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum);
		}
		else
		{
			ioinfo[irq]->opm = 0;

		} /* endif */
	
		if ( ioinfo[irq]->opm == 0 ) 	
		{
			ioinfo[irq]->ui.flags.oper  = 0;

		} /* endif */

		ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
		ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS;

		/*
		 * When we find a device "not oper" we save the status
		 *  information into the device status area and call the
		 *  device specific interrupt handler.
		 *
		 * Note: currently we don't have any way to reenable
		 *       the device unless an unsolicited interrupt
		 *       is presented. We don't check for spurious
		 *       interrupts on "not oper" conditions.
		 */

		if (    ( ioinfo[irq]->ui.flags.fast    )
		     && ( ioinfo[irq]->ui.flags.w4final ) )
		{
			/*
			 * If a new request was queued already, we have
			 *  to simulate the "not oper" status for the
			 *  queued request by switching the "intparm" value
			 *  and notify the interrupt handler.
			 */
			if ( ioinfo[irq]->ui.flags.doio_q )
			{
				ioinfo[irq]->devstat.intparm = ioinfo[irq]->qintparm;

			} /* endif */

		} /* endif */

		ioinfo[irq]->ui.flags.fast    = 0;
		ioinfo[irq]->ui.flags.repall  = 0;
		ioinfo[irq]->ui.flags.w4final = 0;

		/*
		 * take fast exit if no handler is available
		 */
		if ( !ioinfo[irq]->ui.flags.ready )
			return( ending_status );     		

		memcpy( udp, &(ioinfo[irq]->devstat), sdevstat );

		ioinfo[irq]->devstat.intparm  = 0;

		if ( !ioinfo[irq]->ui.flags.s_pend )
		{
			ioinfo[irq]->irq_desc.handler( irq, udp, NULL );

		} /* endif */

		ending_status    = 1;

		break;

	} /* endswitch */

	return( ending_status );
}

/*
 * Set the special i/o-interruption sublass 7 for the
 *  device specified by parameter irq. There can only
 *  be a single device been operated on this special
 *  isc. This function is aimed being able to check
 *  on special device interrupts in disabled state,
 *  without having to delay I/O processing (by queueing)
 *  for non-console devices.
 *
 * Setting of this isc is done by set_cons_dev(), while
 *  reset_cons_dev() resets this isc and re-enables the
 *  default isc3 for this device. wait_cons_dev() allows
 *  to actively wait on an interrupt for this device in
 *  disabed state. When the interrupt condition is
 *  encountered, wait_cons_dev(9 calls do_IRQ() to have
 *  the console device driver processing the interrupt.
 */
int set_cons_dev( int irq )
{
	int           ccode;
	unsigned long cr6 __attribute__ ((aligned (8)));
	int           rc = 0;
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if ( cons_dev != -1  )
	{
		rc = -EBUSY;
	}
	else
	{
		if (cio_debug_initialized) {
			debug_text_event(cio_debug_trace_id, 4, "scons");
			sprintf(dbf_txt, "%x", irq);
			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
		}
		
		/*
		 * modify the indicated console device to operate
		 *  on special console interrupt sublass 7
		 */
		ccode = stsch( irq, &(ioinfo[irq]->schib) );

		if (ccode)
		{
			rc                         = -ENODEV;
			ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
		}
		else
		{
			ioinfo[irq]->schib.pmcw.isc = 7;

			ccode = msch( irq, &(ioinfo[irq]->schib) );

			if (ccode)
			{
				rc = -EIO;
			}
			else
			{
				cons_dev = irq;

				/*
				 * enable console I/O-interrupt sublass 7
				 */
				__ctl_store( cr6, 6, 6);
				cr6 |= 0x01000000;
				__ctl_load( cr6, 6, 6);

			} /* endif */

		} /* endif */

	} /* endif */

	return( rc);
}

int reset_cons_dev( int irq)
{
	int     rc = 0;
	int     ccode;
	long    cr6 __attribute__ ((aligned (8)));
	char dbf_txt[15];

	SANITY_CHECK(irq);

	if ( cons_dev != -1  )
	{
		rc = -EBUSY;
	}
	else
	{
		if (cio_debug_initialized) {
			debug_text_event(cio_debug_trace_id, 4, "rcons");
			sprintf(dbf_txt, "%x", irq);
			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
		}

		/*
		 * reset the indicated console device to operate
		 *  on default console interrupt sublass 3
		 */
		ccode = stsch( irq, &(ioinfo[irq]->schib) );

		if (ccode)
		{
			rc                         = -ENODEV;
			ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
		}
		else
		{

			ioinfo[irq]->schib.pmcw.isc = 3;

			ccode = msch( irq, &(ioinfo[irq]->schib) );

			if (ccode)
			{
				rc = -EIO;
			}
			else
			{
				cons_dev = -1;

				/*
				 * disable special console I/O-interrupt sublass 7
				 */
				__ctl_store( cr6, 6, 6);
				cr6 &= 0xFEFFFFFF;
				__ctl_load( cr6, 6, 6);

			} /* endif */

		} /* endif */

	} /* endif */

	return( rc);
}

int wait_cons_dev( int irq )
{
	int              rc = 0;
	long             save_cr6;
	char dbf_txt[15];

	if ( irq == cons_dev )
	{

		if (cio_debug_initialized) {
			debug_text_event(cio_debug_trace_id, 4, "wcons");
			sprintf(dbf_txt, "%x", irq);
			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
		}

		/*
		 * before entering the spinlock we may already have
		 *  processed the interrupt on a different CPU ...
		 */
		if ( ioinfo[irq]->ui.flags.busy == 1 )
		{
			long cr6 __attribute__ ((aligned (8)));

			/*
			 * disable all, but isc 7 (console device)
			 */
			__ctl_store( cr6, 6, 6);
			save_cr6  = cr6;
			cr6      &= 0x01FFFFFF;
			__ctl_load( cr6, 6, 6);

			do {
				tpi_info_t tpi_info = {0,};
				if (tpi(&tpi_info) == 1) {
					s390_process_IRQ( tpi_info.irq );
				} else {
					s390irq_spin_unlock(irq);
					udelay(100);
					s390irq_spin_lock(irq);
				}
				eieio();
			} while (ioinfo[irq]->ui.flags.busy == 1);

			/*
			 * restore previous isc value
			 */
			cr6 = save_cr6;
			__ctl_load( cr6, 6, 6);

		} /* endif */

	}
	else
	{
		rc = EINVAL;

	} /* endif */


	return(rc);
}


int enable_cpu_sync_isc( int irq )
{
	int             ccode;
	long            cr6 __attribute__ ((aligned (8)));

	int             retry = 3;
	int             rc    = 0;
	char dbf_txt[15];

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "enisc");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/* This one spins until it can get the sync_isc lock for irq# irq */

	if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA )
	{
		if ( atomic_read( &sync_isc ) != irq )
			atomic_compare_and_swap_spin( -1, irq, &sync_isc );

		sync_isc_cnt++;
		
		if ( sync_isc_cnt > 255 ) /* fixme : magic number */
		{
			panic("Too many recursive calls to enable_sync_isc");

		}
		/*
		 * we only run the STSCH/MSCH path for the first enablement
		 */
		else if ( sync_isc_cnt == 1 )
		{
			ioinfo[irq]->ui.flags.syncio = 1;

			ccode = stsch( irq, &(ioinfo[irq]->schib) );

			if ( !ccode )
			{
				ioinfo[irq]->schib.pmcw.isc = 5;

				do
				{
					ccode = msch( irq,
					              &(ioinfo[irq]->schib) );

					switch (ccode) {
					case 0: 
						/*
						 * enable special isc
						 */
						__ctl_store( cr6, 6, 6);
						cr6 |= 0x04000000;  // enable sync isc 5
						cr6 &= 0xEFFFFFFF;  // disable standard isc 3
						__ctl_load( cr6, 6, 6);
						retry = 0;
						break;
					
					case 1:
						//
						// process pending status
						//
						ioinfo[irq]->ui.flags.s_pend = 1;
						s390_process_IRQ( irq );
						ioinfo[irq]->ui.flags.s_pend = 0;
						
						rc = -EIO;  /* might be overwritten... */
						retry--;
						break;

					case 2: /* busy */
						retry = 0;
						rc = -EBUSY; 
						break;

					case 3: /* not oper*/
						retry = 0;
						rc = -ENODEV; 
						break;
					
					}

				} while ( retry );

			}
			else
			{
				rc = -ENODEV;     // device is not-operational

			} /* endif */

		} /* endif */


		if ( rc )	/* can only happen if stsch/msch fails */
		{
			sync_isc_cnt = 0;
			atomic_set( &sync_isc, -1);
		}
	}
	else
	{
#ifdef CONFIG_SYNC_ISC_PARANOIA
		panic( "enable_sync_isc: called with invalid %x\n", irq );
#endif

		rc = -EINVAL;

	} /* endif */

	return( rc);
}

int disable_cpu_sync_isc( int irq)
{
	int     rc         = 0;
	int     retry1     = 5;
	int     retry2     = 5;
	int     clear_pend = 0;

	int     ccode;
	long    cr6 __attribute__ ((aligned (8)));

	char dbf_txt[15];

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "disisc");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA )
	{
		/*
		 * We disable if we're the top user only, as we may
		 *  run recursively ...	
		 * We must not decrease the count immediately; during
		 *  msch() processing we may face another pending
		 *  status we have to process recursively (sync).
		 */

#ifdef CONFIG_SYNC_ISC_PARANOIA
		if ( atomic_read( &sync_isc ) != irq )
			panic( "disable_sync_isc: called for %x while %x locked\n",
				irq, atomic_read( &sync_isc ) );
#endif

		if ( sync_isc_cnt == 1 )
		{
			ccode = stsch( irq, &(ioinfo[irq]->schib) );

			ioinfo[irq]->schib.pmcw.isc = 3;

			do
			{
			        retry2 = 5;
				do
				{
					ccode = msch( irq, &(ioinfo[irq]->schib) );

					switch ( ccode ) {
					case 0:
						/*
						 * disable special interrupt subclass in CPU
						 */
						__ctl_store( cr6, 6, 6);
						cr6 &= 0xFBFFFFFF; // disable sync isc 5
						cr6 |= 0x10000000; // enable standard isc 3
						__ctl_load( cr6, 6, 6);

						retry2 = 0;
						break;

					case 1: /* status pending */
						ioinfo[irq]->ui.flags.s_pend = 1;
						s390_process_IRQ( irq );
						ioinfo[irq]->ui.flags.s_pend = 0;

						retry2--;
						break;

					case 2: /* busy */
					        retry2--;
						udelay( 100); // give it time
						break;
						
					default: /* not oper */
						retry2 = 0;
						break;
					} /* endswitch */

				} while ( retry2 );

				retry1--;

				/* try stopping it ... */
				if ( (ccode) && !clear_pend )
				{
					clear_IO( irq, 0x00004711, 0 );
					clear_pend = 1;
               	
				} /* endif */

				udelay( 100);

			} while ( retry1 && ccode );

			ioinfo[irq]->ui.flags.syncio = 0;
		
			sync_isc_cnt = 0;	
			atomic_set( &sync_isc, -1);

		}
		else
		{
			sync_isc_cnt--;

		} /* endif */
	}
	else
	{
#ifdef CONFIG_SYNC_ISC_PARANOIA
		if ( atomic_read( &sync_isc ) != -1 )
			panic( "disable_sync_isc: called with invalid %x while %x locked\n", 
				irq, atomic_read( &sync_isc ) );
#endif

		rc = -EINVAL;

	} /* endif */

	return( rc);
}

//
// Input :
//   devno - device number
//   ps    - pointer to sense ID data area
//
// Output : none
//
void VM_virtual_device_info( __u16      devno,
                             senseid_t *ps )
{
	diag210_t  *p_diag_data;
	int        ccode;

	int        error = 0;

	if (cio_debug_initialized) 
		debug_text_event(cio_debug_trace_id, 4, "VMvdinf");

	if ( init_IRQ_complete )
	{
		p_diag_data = kmalloc( sizeof( diag210_t), GFP_DMA );
	}
	else
	{
		p_diag_data = alloc_bootmem_low( sizeof( diag210_t));

	} /* endif */

	p_diag_data->vrdcdvno = devno;
	p_diag_data->vrdclen  = sizeof( diag210_t);
	ccode                 = diag210( (diag210_t *)virt_to_phys( p_diag_data ) );
	ps->reserved          = 0xff;

	switch (p_diag_data->vrdcvcla) {
	case 0x80:

		switch (p_diag_data->vrdcvtyp) {
		case 00:

			ps->cu_type   = 0x3215;

			break;

		default:

			error = 1;

			break;

		} /* endswitch */

		break;

	case 0x40:

		switch (p_diag_data->vrdcvtyp) {
		case 0xC0:

			ps->cu_type   = 0x5080;

			break;

		case 0x80:

			ps->cu_type   = 0x2250;

			break;

		case 0x04:

			ps->cu_type   = 0x3277;

			break;

		case 0x01:

			ps->cu_type   = 0x3278;

			break;

		default:

			error = 1;

			break;

		} /* endswitch */

		break;

	case 0x20:

		switch (p_diag_data->vrdcvtyp) {
		case 0x84:

			ps->cu_type   = 0x3505;

			break;

		case 0x82:

			ps->cu_type   = 0x2540;

			break;

		case 0x81:

			ps->cu_type   = 0x2501;

			break;

		default:

			error = 1;

			break;

		} /* endswitch */

		break;

	case 0x10:

		switch (p_diag_data->vrdcvtyp) {
		case 0x84:

			ps->cu_type   = 0x3525;

			break;

		case 0x82:

			ps->cu_type   = 0x2540;

			break;

		case 0x4F:
		case 0x4E:
		case 0x48:

			ps->cu_type   = 0x3820;

			break;

		case 0x4D:
		case 0x49:
		case 0x45:

			ps->cu_type   = 0x3800;

			break;

		case 0x4B:

			ps->cu_type   = 0x4248;

			break;

		case 0x4A:

			ps->cu_type   = 0x4245;

			break;

		case 0x47:

			ps->cu_type   = 0x3262;

			break;

		case 0x43:

			ps->cu_type   = 0x3203;

			break;

		case 0x42:

			ps->cu_type   = 0x3211;

			break;

		case 0x41:

			ps->cu_type   = 0x1403;

			break;

		default:

			error = 1;

			break;

		} /* endswitch */

		break;

	case 0x08:

		switch (p_diag_data->vrdcvtyp) {
		case 0x82:

			ps->cu_type   = 0x3422;

			break;

		case 0x81:

			ps->cu_type   = 0x3490;

			break;

		case 0x10:

			ps->cu_type   = 0x3420;

			break;

		case 0x02:

			ps->cu_type   = 0x3430;

			break;

		case 0x01:

			ps->cu_type   = 0x3480;

			break;

		case 0x42:

			ps->cu_type   = 0x3424;

			break;

		case 0x44:

			ps->cu_type   = 0x9348;

			break;

		default:

			error = 1;

			break;

		} /* endswitch */

		break;

	case 02: /* special device class ... */

		switch (p_diag_data->vrdcvtyp) {
		case 0x20: /* OSA */

			ps->cu_type   = 0x3088;
			ps->cu_model  = 0x60;

			break;

		default:

			error = 1;
      	break;

		} /* endswitch */

		break;

	default:

		error = 1;

		break;

	} /* endswitch */

	if ( init_IRQ_complete )
	{
		kfree( p_diag_data );
	}
	else
	{
		free_bootmem( (unsigned long)p_diag_data, sizeof( diag210_t) );

	} /* endif */

	if ( error )
	{
		printk( "DIAG X'210' for "
		        "device %04X returned "
		        "(cc = %d): vdev class : %02X, "
			"vdev type : %04X \n ...  rdev class : %02X, rdev type : %04X, rdev model: %02X\n",
			devno,
			ccode,
			p_diag_data->vrdcvcla,
			p_diag_data->vrdcvtyp,
			p_diag_data->vrdcrccl,
			p_diag_data->vrdccrty,
			p_diag_data->vrdccrmd );
		if (cio_debug_initialized)
			debug_sprintf_event( cio_debug_msg_id, 0,
					     "DIAG X'210' for "
					     "device %04X returned "
					     "(cc = %d): vdev class : %02X, "
					     "vdev type : %04X \n ...  rdev class : %02X, rdev type : %04X, rdev model: %02X\n",
					     devno,
					     ccode,
					     p_diag_data->vrdcvcla,
					     p_diag_data->vrdcvtyp,
					     p_diag_data->vrdcrccl,
					     p_diag_data->vrdccrty,
					     p_diag_data->vrdccrmd );

	} /* endif */

}

/*
 * This routine returns the characteristics for the device
 *  specified. Some old devices might not provide the necessary
 *  command code information during SenseID processing. In this
 *  case the function returns -EINVAL. Otherwise the function
 *  allocates a decice specific data buffer and provides the
 *  device characteristics together with the buffer size. Its
 *  the callers responability to release the kernel memory if
 *  not longer needed. In case of persistent I/O problems -EBUSY
 *  is returned.
 *
 *  The function may be called enabled or disabled. However, the
 *   caller must have locked the irq it is requesting data for.
 *
 * Note : It would have been nice to collect this information
 *         during init_IRQ() processing but this is not possible
 *
 *         a) without statically pre-allocation fixed size buffers
 *            as virtual memory management isn't available yet.
 *
 *         b) without unnecessarily increase system startup by
 *            evaluating devices eventually not used at all.
 */
int read_dev_chars( int irq, void **buffer, int length )
{
	unsigned int  flags;
	ccw1_t       *rdc_ccw;
	devstat_t     devstat;
	char         *rdc_buf;
	int           devflag = 0;

	int           ret      = 0;
	int           emulated = 0;
	int           retry    = 5;

	char dbf_txt[15];

	if ( !buffer || !length )
	{
		return( -EINVAL );

	} /* endif */

	SANITY_CHECK(irq);

	if ( ioinfo[irq]->ui.flags.oper == 0 )
	{
		return( -ENODEV );

	} /* endif */

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "rddevch");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * Before playing around with irq locks we should assure
	 *   running disabled on (just) our CPU. Sync. I/O requests
	 *   also require to run disabled.
	 *
	 * Note : as no global lock is required, we must not use
	 *        cli(), but __cli() instead.  	
	 */
	__save_flags(flags);
	__cli();

	rdc_ccw = &ioinfo[irq]->senseccw;

	if ( !ioinfo[irq]->ui.flags.ready )
	{
		ret = request_irq( irq,
		                   init_IRQ_handler,
		                   SA_PROBE, "RDC", &devstat );

		if ( !ret )
		{
			emulated = 1;

		} /* endif */

	} /* endif */

	if ( !ret )
	{
		if ( ! *buffer )
		{
			rdc_buf  = kmalloc( length, GFP_KERNEL);
		}
		else
		{
			rdc_buf = *buffer;

		} /* endif */

		if ( !rdc_buf )
		{
			ret = -ENOMEM;
		}
		else
		{
			do
			{
				rdc_ccw->cmd_code = CCW_CMD_RDC;
				rdc_ccw->count    = length;
				rdc_ccw->flags    = CCW_FLAG_SLI;
				ret = set_normalized_cda( rdc_ccw, (unsigned long)rdc_buf );
				if (!ret) {

					memset( ioinfo[irq]->irq_desc.dev_id,
						'\0',
						sizeof( devstat_t));

					ret = s390_start_IO( irq,
							     rdc_ccw,
							     0x00524443, // RDC
							     0,          // n/a
							     DOIO_WAIT_FOR_INTERRUPT
							     | DOIO_DONT_CALL_INTHDLR );
					retry--;
					devflag = ioinfo[irq]->irq_desc.dev_id->flag;   
					
					clear_normalized_cda( rdc_ccw);  
				} else {
					udelay(100);  //wait for recovery
					retry--;
				}

			} while (    ( retry                                     )
			          && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) );

		} /* endif */

		if ( !retry )
		{
			ret = (ret==-ENOMEM)?-ENOMEM:-EBUSY;

		} /* endif */

		__restore_flags(flags);

		/*
		 * on success we update the user input parms
		 */
		if ( !ret )
		{
			*buffer = rdc_buf;

		} /* endif */

		if ( emulated )
		{
			free_irq( irq, &devstat);

		} /* endif */

	} /* endif */

	return( ret );
}

/*
 *  Read Configuration data
 */
int read_conf_data( int irq, void **buffer, int *length, __u8 lpm )
{
	unsigned long flags;
	int           ciw_cnt;

	int           found  = 0; // RCD CIW found
	int           ret    = 0; // return code

	char dbf_txt[15];

	SANITY_CHECK(irq);

	if ( !buffer || !length )
	{
		return( -EINVAL);
	}
	else if ( ioinfo[irq]->ui.flags.oper == 0 )
	{
		return( -ENODEV );
	}
	else if ( ioinfo[irq]->ui.flags.esid == 0 )
	{
		return( -EOPNOTSUPP );

	} /* endif */

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "rdconf");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * scan for RCD command in extended SenseID data
	 */
	
	for ( ciw_cnt = 0; (found == 0) && (ciw_cnt < 62); ciw_cnt++ )
	{
		if ( ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD )
		{
			/*
			 * paranoia check ...
			 */
			if ( ioinfo[irq]->senseid.ciw[ciw_cnt].cmd != 0 )
			{
				found = 1;

			} /* endif */

			break;

		} /* endif */

	} /* endfor */

	if ( found )
	{
		devstat_t  devstat;  /* inline device status area */
		devstat_t *pdevstat;
		int        ioflags;

		ccw1_t    *rcd_ccw  = &ioinfo[irq]->senseccw;
		char      *rcd_buf  = NULL;
		int        emulated = 0; /* no i/O handler installed */
		int        retry    = 5; /* retry count */

		__save_flags(flags);
		__cli();

		if ( !ioinfo[irq]->ui.flags.ready )
		{
			pdevstat = &devstat;
			ret      = request_irq( irq,
			                        init_IRQ_handler,
			                        SA_PROBE, "RCD", pdevstat );

			if ( !ret )
			{
				emulated = 1;

			} /* endif */
		}
		else
		{
			pdevstat = ioinfo[irq]->irq_desc.dev_id;

		} /* endif */

		if ( !ret )
		{
			if ( init_IRQ_complete )
			{
				rcd_buf = kmalloc( ioinfo[irq]->senseid.ciw[ciw_cnt].count,
				                   GFP_DMA);
			}
			else
			{
				rcd_buf = alloc_bootmem_low( ioinfo[irq]->senseid.ciw[ciw_cnt].count);

   		} /* endif */

			if ( rcd_buf == NULL )
			{
				ret = -ENOMEM;	

			} /* endif */

			if ( !ret )
			{
				memset( rcd_buf,
				        '\0',
				        ioinfo[irq]->senseid.ciw[ciw_cnt].count);
       	
				do
				{
					rcd_ccw->cmd_code = ioinfo[irq]->senseid.ciw[ciw_cnt].cmd;
					rcd_ccw->cda      = (__u32)virt_to_phys( rcd_buf );
					rcd_ccw->count    = ioinfo[irq]->senseid.ciw[ciw_cnt].count;
					rcd_ccw->flags    = CCW_FLAG_SLI;

					memset( pdevstat, '\0', sizeof( devstat_t));

					if ( lpm )
					{
						ioflags = DOIO_WAIT_FOR_INTERRUPT
						          | DOIO_VALID_LPM    					
						          | DOIO_DONT_CALL_INTHDLR;
					}
					else
					{
						ioflags =   DOIO_WAIT_FOR_INTERRUPT
						          | DOIO_DONT_CALL_INTHDLR;
						             					
					} /* endif */

					ret = s390_start_IO( irq,
					                     rcd_ccw,
					                     0x00524344,  // == RCD
					                     lpm,
					                     ioflags );
					switch ( ret ) {
					case 0:
					case -EIO:
						
						if ( !(pdevstat->flag & (   DEVSTAT_STATUS_PENDING
									    | DEVSTAT_NOT_OPER
									    | DEVSTAT_FLAG_SENSE_AVAIL ) ) )
						{
							retry = 0;  // we got it ...
						}
						else
						{
							retry--;    // try again ...
								
						} /* endif */
						
						break;

					default:   // -EBUSY, -ENODEV, ???
						retry = 0;
						
					} /* endswitch */
					
				} while ( retry );

			} /* endif */

			__restore_flags( flags );

		} /* endif */

		/*
		 * on success we update the user input parms
		 */
		if ( ret == 0 )
		{
			*length = ioinfo[irq]->senseid.ciw[ciw_cnt].count;
			*buffer = rcd_buf;
		}
		else
		{
			if ( rcd_buf != NULL )
			{
				if ( init_IRQ_complete )
				{
					kfree( rcd_buf );
				}
				else
				{
					free_bootmem( (unsigned long)rcd_buf,
					              ioinfo[irq]->senseid.ciw[ciw_cnt].count);

   			} /* endif */

			} /* endif */

			*buffer = NULL;
			*length = 0;
    	
		} /* endif */

		if ( emulated )
			free_irq( irq, pdevstat);
	}
	else
	{
		ret = -EOPNOTSUPP;

	} /* endif */

	return( ret );

}

int get_dev_info( int irq, s390_dev_info_t * pdi)
{
	return( get_dev_info_by_irq( irq, pdi));
}

static int __inline__ get_next_available_irq( ioinfo_t *pi)
{
	int ret_val = -ENODEV;

	while ( pi!=NULL ) {
		if ( pi->ui.flags.oper ) {
			ret_val = pi->irq;
			break;
		} else {
			pi = pi->next;
		}
	}

	return ret_val;
}


int get_irq_first( void )
{
   int ret_irq;

	if ( ioinfo_head )
	{
		if ( ioinfo_head->ui.flags.oper )
		{
			ret_irq = ioinfo_head->irq;
		}
		else if ( ioinfo_head->next )
		{
			ret_irq = get_next_available_irq( ioinfo_head->next );

		}
		else
		{
			ret_irq = -ENODEV;
   	
		} /* endif */
	}
	else
	{
		ret_irq = -ENODEV;

	} /* endif */

	return ret_irq;
}

int get_irq_next( int irq )
{
	int ret_irq;	

	if ( ioinfo[irq] != INVALID_STORAGE_AREA )
	{
		if ( ioinfo[irq]->next )
		{
			if ( ioinfo[irq]->next->ui.flags.oper )
			{
				ret_irq = ioinfo[irq]->next->irq;
			}
			else
			{
				ret_irq = get_next_available_irq( ioinfo[irq]->next );

			} /* endif */
		}
		else
		{
			ret_irq = -ENODEV;     	

		} /* endif */
	}
	else
	{
		ret_irq = -EINVAL;

	} /* endif */

	return ret_irq;
}

int get_dev_info_by_irq( int irq, s390_dev_info_t *pdi)
{

	SANITY_CHECK(irq);

	if ( pdi == NULL )
		return -EINVAL;

	pdi->devno = ioinfo[irq]->schib.pmcw.dev;
	pdi->irq   = irq;
	
	if (   ioinfo[irq]->ui.flags.oper
	    && !ioinfo[irq]->ui.flags.unknown ) 
	{
		pdi->status = 0;
		memcpy( &(pdi->sid_data),
			&ioinfo[irq]->senseid,
			sizeof( senseid_t));
	}
	else if ( ioinfo[irq]->ui.flags.unknown )
	{
		pdi->status = DEVSTAT_UNKNOWN_DEV;
		memset( &(pdi->sid_data),
			'\0',
			sizeof( senseid_t));
		pdi->sid_data.cu_type = 0xFFFF;
			
	}
	else
	{
		pdi->status = DEVSTAT_NOT_OPER;
		memset( &(pdi->sid_data),
			'\0',
			sizeof( senseid_t));
		pdi->sid_data.cu_type = 0xFFFF;
		
	} /* endif */
	
	if ( ioinfo[irq]->ui.flags.ready )
		pdi->status |= DEVSTAT_DEVICE_OWNED;


	return 0;
}


int get_dev_info_by_devno( __u16 devno, s390_dev_info_t *pdi)
{
	int i;
	int rc = -ENODEV;

	if ( devno > 0x0000ffff )
		return -ENODEV;
        if ( pdi == NULL )
		return -EINVAL;

	for ( i=0; i <= highest_subchannel; i++ ) {
		
		if (    ioinfo[i] != INVALID_STORAGE_AREA
		     && ioinfo[i]->schib.pmcw.dev == devno )
		{
			
			pdi->irq   = i;
			pdi->devno = devno;
			
			if (    ioinfo[i]->ui.flags.oper
			     && !ioinfo[i]->ui.flags.unknown )
			{
				pdi->status = 0;
				memcpy( &(pdi->sid_data),
					&ioinfo[i]->senseid,
					sizeof( senseid_t));
			}
			else if ( ioinfo[i]->ui.flags.unknown )
			{
				pdi->status = DEVSTAT_UNKNOWN_DEV;

				memset( &(pdi->sid_data),
					'\0',
					sizeof( senseid_t));

				pdi->sid_data.cu_type = 0xFFFF;
			}
			else
			{
				pdi->status = DEVSTAT_NOT_OPER;
				
				memset( &(pdi->sid_data),
					'\0',
					sizeof( senseid_t));

				pdi->sid_data.cu_type = 0xFFFF;

			} /* endif */

			if ( ioinfo[i]->ui.flags.ready )
				pdi->status |= DEVSTAT_DEVICE_OWNED;

			rc = 0; /* found */
			break;

		} /* endif */

	} /* endfor */

	return( rc);

}

int get_irq_by_devno( __u16 devno )
{
	int i;
	int rc = -1;

	if ( devno <= 0x0000ffff )
	{
		for ( i=0; i <= highest_subchannel; i++ )
		{
			if (    (ioinfo[i] != INVALID_STORAGE_AREA )
			     && (ioinfo[i]->schib.pmcw.dev == devno)
			     && (ioinfo[i]->schib.pmcw.dnv == 1    ) )
			{
				rc = i;
				break;

			} /* endif */

		} /* endfor */
	
	} /* endif */

	return( rc);
}

unsigned int get_devno_by_irq( int irq )
{

	if (    ( irq > highest_subchannel            )
	     || ( irq < 0                             )
	     || ( ioinfo[irq] == INVALID_STORAGE_AREA ) )
	{
		return -1;
	
	} /* endif */

	/*
	 * we don't need to check for the device be operational
	 *  as the initial STSCH will always present the device
	 *  number defined by the IOCDS regardless of the device
	 *  existing or not. However, there could be subchannels
	 *  defined who's device number isn't valid ...
	 */
	if ( ioinfo[irq]->schib.pmcw.dnv )
		return( ioinfo[irq]->schib.pmcw.dev );
	else
		return -1;
}

/*
 * s390_device_recognition_irq
 *
 * Used for individual device recognition. Issues the device
 *  independant SenseID command to obtain info the device type.
 *
 */
void s390_device_recognition_irq( int irq )
{
	int           ret;
	char dbf_txt[15];

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "devrec");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * We issue the SenseID command on I/O subchannels we think are
	 *  operational only.
	 */
	if (    ( ioinfo[irq] != INVALID_STORAGE_AREA )	
	     && ( ioinfo[irq]->schib.pmcw.st == 0     )
	     && ( ioinfo[irq]->ui.flags.oper == 1     ) )
	{
		int       irq_ret;
		devstat_t devstat;

		irq_ret = request_irq( irq,
		                       init_IRQ_handler,
		                       SA_PROBE,
		                       "INIT",
		                       &devstat);

		if ( !irq_ret )
		{
			ret = enable_cpu_sync_isc( irq );

			if ( !ret ) 
			{
				ioinfo[irq]->ui.flags.unknown = 0;

				memset( &ioinfo[irq]->senseid, '\0', sizeof( senseid_t));

				s390_SenseID( irq, &ioinfo[irq]->senseid, 0xff );
#if 0	/* FIXME */
				/*
				 * We initially check the configuration data for
				 *  those devices with more than a single path
				 */
				if ( ioinfo[irq]->schib.pmcw.pim != 0x80 )
				{
					char     *prcd;
					int       lrcd;

					ret = read_conf_data( irq, (void **)&prcd, &lrcd, 0 );

					if ( !ret )	// on success only ...
					{
						char buffer[80];
#ifdef CONFIG_DEBUG_IO
						sprintf( buffer,
						         "RCD for device(%04X)/"
						         "subchannel(%04X) returns :\n",
						         ioinfo[irq]->schib.pmcw.dev,
						         irq );

						s390_displayhex( buffer, prcd, lrcd );
#endif      				
						if (cio_debug_initialized) {
							sprintf( buffer,
								 "RCD for device(%04X)/"
								 "subchannel(%04X) returns :\n",
								 ioinfo[irq]->schib.pmcw.dev,
								 irq );
							
							s390_displayhex2( buffer, prcd, lrcd, 2);
						}
						if ( init_IRQ_complete )
						{
							kfree( prcd );
						}
						else
						{
							free_bootmem( (unsigned long)prcd, lrcd );

 			  			} /* endif */

					} /* endif */
					
				} /* endif */
#endif

				disable_cpu_sync_isc( irq );

			} /* endif */  	

			free_irq( irq, &devstat );

		} /* endif */
		
	} /* endif */

}

/*
 * s390_device_recognition_all
 *
 * Used for system wide device recognition.
 *
 */
void s390_device_recognition_all( void)
{
	int irq = 0; /* let's start with subchannel 0 ... */

	do
	{
		s390_device_recognition_irq( irq );

		irq ++;

	} while ( irq <= highest_subchannel );

}

/*
 * Function: s390_redo_validation
 * Look for no longer blacklisted devices
 * FIXME: there must be a better way to do this...
 */

void s390_redo_validation(void) 
{
	int irq = 0;
	int ret;

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 0, "redoval");
	}
	do {
		if (ioinfo[irq] == INVALID_STORAGE_AREA) {
			ret = s390_validate_subchannel(irq, 0);
			if (!ret) {
				s390_device_recognition_irq(irq);
				if (ioinfo[irq]->ui.flags.oper) {
					devreg_t *pdevreg;
					
					pdevreg = s390_search_devreg( ioinfo[irq] );
					if ( pdevreg != NULL ) {
						if ( pdevreg->oper_func != NULL )
							pdevreg->oper_func( irq, pdevreg );
						
					} 
				}
#ifdef CONFIG_PROC_FS
				if (cio_proc_devinfo) 
					if (irq < MAX_CIO_PROCFS_ENTRIES) {
						cio_procfs_device_create(ioinfo[irq]->devno);
				}
#endif
			}
		}
		irq++;
	} while (irq<=highest_subchannel);
}

/*
 * s390_search_devices
 *
 * Determines all subchannels available to the system.
 *
 */
void s390_process_subchannels( void)
{
	int   ret;
	int   irq = 0;   /* Evaluate all subchannels starting with 0 ... */

	do
	{
		ret = s390_validate_subchannel( irq, 0);

		if ( ret != -ENXIO)
			irq++;
	
  	} while ( (ret != -ENXIO) && (irq < __MAX_SUBCHANNELS) );

	highest_subchannel = (--irq);

	printk( "Highest subchannel number detected (hex) : %04X\n",
	        highest_subchannel);
	if (cio_debug_initialized)
		debug_sprintf_event(cio_debug_msg_id, 0, 
				    "Highest subchannel number detected (hex) : %04X\n",
				    highest_subchannel);	
}

/*
 * s390_validate_subchannel()
 *
 * Process the subchannel for the requested irq. Returns 1 for valid
 *  subchannels, otherwise 0.
 */
int s390_validate_subchannel( int irq, int enable )
{

	int      retry;     /* retry count for status pending conditions */
	int      ccode;     /* condition code for stsch() only */
	int      ccode2;    /* condition code for other I/O routines */
	schib_t *p_schib;
	int      ret;
	
	char dbf_txt[15];

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "valsch");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	/*
	 * The first subchannel that is not-operational (ccode==3)
	 *  indicates that there aren't any more devices available.
	 */
	if (    ( init_IRQ_complete                   )
	     && ( ioinfo[irq] != INVALID_STORAGE_AREA ) )
	{
		p_schib = &ioinfo[irq]->schib;
	}
	else
	{
		p_schib = p_init_schib;

	} /* endif */

	/*
	 * If we knew the device before we assume the worst case ... 	
	 */
	if ( ioinfo[irq] != INVALID_STORAGE_AREA )
	{
		ioinfo[irq]->ui.flags.oper = 0;
		ioinfo[irq]->ui.flags.dval = 0;

	} /* endif */

	ccode = stsch( irq, p_schib);

	if ( !ccode )
	{
		/*
		 * ... just being curious we check for non I/O subchannels
		 */
		if ( p_schib->pmcw.st )
		{
			printk( "Subchannel %04X reports "
			        "non-I/O subchannel type %04X\n",
			        irq,
			        p_schib->pmcw.st);
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_msg_id, 0,
						    "Subchannel %04X reports non-I/O subchannel type %04X\n",
						    irq, p_schib->pmcw.st);

			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
				ioinfo[irq]->ui.flags.oper = 0;

		} /* endif */

		if ( p_schib->pmcw.dnv )
		{
		     if ( is_blacklisted( p_schib->pmcw.dev )) {
			  /* 
			   * This device must not be known to Linux. So we simply say that 
			   * there is no device and return ENODEV.
			   */
#ifdef CONFIG_DEBUG_IO
			  printk( "Blacklisted device detected at devno %04X\n", p_schib->pmcw.dev );
#endif
			  if (cio_debug_initialized)
				  debug_sprintf_event(cio_debug_msg_id, 0,
						      "Blacklisted device detected at devno %04X\n",
						      p_schib->pmcw.dev);
			  ret = -ENODEV;
		     } else {
		        if ( ioinfo[irq] == INVALID_STORAGE_AREA )
			{	
				if ( !init_IRQ_complete )
				{
					ioinfo[irq] =
					   (ioinfo_t *)alloc_bootmem_low( sizeof(ioinfo_t));
				}
				else
				{
					ioinfo[irq] =
					   (ioinfo_t *)kmalloc( sizeof(ioinfo_t),
				                           GFP_DMA );

				} /* endif */

				memset( ioinfo[irq], '\0', sizeof( ioinfo_t));
				memcpy( &ioinfo[irq]->schib,
			           p_init_schib,
			           sizeof( schib_t));
			
				/*
				 * We have to insert the new ioinfo element
				 *  into the linked list, either at its head,
				 *  its tail or insert it.
				 */
				if ( ioinfo_head == NULL )  /* first element */
				{
					ioinfo_head = ioinfo[irq];
					ioinfo_tail = ioinfo[irq];
				}
				else if ( irq < ioinfo_head->irq ) /* new head */
				{
					ioinfo[irq]->next = ioinfo_head;
					ioinfo_head->prev = ioinfo[irq];
					ioinfo_head       = ioinfo[irq];
				}
				else if ( irq > ioinfo_tail->irq ) /* new tail */
				{
					ioinfo_tail->next = ioinfo[irq];
					ioinfo[irq]->prev = ioinfo_tail;
					ioinfo_tail       = ioinfo[irq];
				}
				else /* insert element */
				{
					ioinfo_t *pi = ioinfo_head;

					for (pi=ioinfo_head; pi!=NULL; pi=pi->next) {

						if ( irq < pi->next->irq )
						{
							ioinfo[irq]->next = pi->next;
							ioinfo[irq]->prev = pi;
							pi->next->prev    = ioinfo[irq];
							pi->next          = ioinfo[irq];
							break;
						
						} /* endif */
					}
				} /* endif */

			} /* endif */

			// initialize some values ...	
			ioinfo[irq]->ui.flags.pgid_supp = 1;

			ioinfo[irq]->opm =   ioinfo[irq]->schib.pmcw.pim
			                   & ioinfo[irq]->schib.pmcw.pam
			                   & ioinfo[irq]->schib.pmcw.pom;

			if ( cio_show_msg )
			{
				printk( KERN_INFO"Detected device %04X "
				        "on subchannel %04X"
				        " - PIM = %02X, PAM = %02X, POM = %02X\n",
				        ioinfo[irq]->schib.pmcw.dev,
				        irq,
			   	     ioinfo[irq]->schib.pmcw.pim,
			      	  ioinfo[irq]->schib.pmcw.pam,
			 	       ioinfo[irq]->schib.pmcw.pom);

			} /* endif */
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_msg_id, 0,
						    "Detected device %04X "
						    "on subchannel %04X"
						    " - PIM = %02X, PAM = %02X, POM = %02X\n",
						    ioinfo[irq]->schib.pmcw.dev,
						    irq,
						    ioinfo[irq]->schib.pmcw.pim,
						    ioinfo[irq]->schib.pmcw.pam,
						    ioinfo[irq]->schib.pmcw.pom);

			/*
			 * initialize ioinfo structure
			 */
			ioinfo[irq]->irq             = irq;
			if(!ioinfo[irq]->ui.flags.ready)
			{
				ioinfo[irq]->nopfunc         = NULL;
				ioinfo[irq]->ui.flags.busy   = 0;
				ioinfo[irq]->ui.flags.dval   = 1;
				ioinfo[irq]->devstat.intparm = 0;
				
			}
			ioinfo[irq]->devstat.devno   = ioinfo[irq]->schib.pmcw.dev;
			ioinfo[irq]->devno           = ioinfo[irq]->schib.pmcw.dev;

			/*
			 * We should have at least one CHPID ...
			 */
			if ( ioinfo[irq]->opm )
			{
				/*
				 * We now have to initially ...
				 *  ... set "interruption sublass"
				 *  ... enable "concurrent sense"
				 *  ... enable "multipath mode" if more than one
				 *        CHPID is available. This is done regardless
				 *        whether multiple paths are available for us.
				 *
				 * Note : we don't enable the device here, this is temporarily
				 *        done during device sensing below.
				 */
				ioinfo[irq]->schib.pmcw.isc     = 3; /* could be smth. else */
				ioinfo[irq]->schib.pmcw.csense  = 1; /* concurrent sense */
				ioinfo[irq]->schib.pmcw.ena     = enable;
				ioinfo[irq]->schib.pmcw.intparm =
				                     ioinfo[irq]->schib.pmcw.dev;

				if (    ( ioinfo[irq]->opm != 0x80 )
				     && ( ioinfo[irq]->opm != 0x40 )
				     && ( ioinfo[irq]->opm != 0x20 )
				     && ( ioinfo[irq]->opm != 0x10 )
				     && ( ioinfo[irq]->opm != 0x08 )
				     && ( ioinfo[irq]->opm != 0x04 )
				     && ( ioinfo[irq]->opm != 0x02 )
				     && ( ioinfo[irq]->opm != 0x01 ) )
				{
					ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */

				} /* endif */

				retry = 5;

				do
				{
					ccode2 = msch_err( irq, &ioinfo[irq]->schib);

					switch (ccode2) {
					case 0:  // successful completion
						//
						// concurrent sense facility available ...
						//
						ioinfo[irq]->ui.flags.oper   = 1;
						ioinfo[irq]->ui.flags.consns = 1;
						ret                          = 0;
						break;
      	
					case 1:  // status pending
						//
						// How can we have a pending status as
						//  device is disabled for interrupts ?
						//  Anyway, process it ...
						//
						ioinfo[irq]->ui.flags.s_pend = 1;
						s390_process_IRQ( irq);
						ioinfo[irq]->ui.flags.s_pend = 0;
						retry--;
						ret = -EIO;
						break;
   	
					case 2:  // busy
						/*
						 * we mark it not-oper as we can't
						 *  properly operate it !
						 */
						ioinfo[irq]->ui.flags.oper = 0;
						udelay( 100);	/* allow for recovery */
						retry--;
						ret = -EBUSY;
						break;

					case 3:  // not operational
						ioinfo[irq]->ui.flags.oper = 0;
						retry                      = 0;
						ret = -ENODEV;
						break;

					default:
#define PGMCHK_OPERAND_EXC      0x15

						if ( (ccode2 & PGMCHK_OPERAND_EXC) == PGMCHK_OPERAND_EXC )
						{
							/*
							 * re-issue the modify subchannel without trying to
							 *  enable the concurrent sense facility
							 */
							ioinfo[irq]->schib.pmcw.csense = 0;
   	
							ccode2 = msch_err( irq, &ioinfo[irq]->schib);

							if ( ccode2 != 0 )
							{
								printk( " ... msch() (2) failed with CC = %X\n",
								        ccode2 );
								if (cio_debug_initialized)
									debug_sprintf_event(cio_debug_msg_id, 0,
											    "msch() (2) failed with CC=%X\n",
											    ccode2);
								ioinfo[irq]->ui.flags.oper = 0;
								ret                        = -EIO;
							}
							else
							{
								ioinfo[irq]->ui.flags.oper   = 1;
								ioinfo[irq]->ui.flags.consns = 0;
								ret                          = 0;

							} /* endif */
						}
						else
						{
							printk( " ... msch() (1) failed with CC = %X\n",
							        ccode2);
							if (cio_debug_initialized)
								debug_sprintf_event(cio_debug_msg_id, 0,
										    "msch() (1) failed with CC = %X\n",
										    ccode2);
							ioinfo[irq]->ui.flags.oper = 0;
							ret                        = -EIO;

						} /* endif */
   	
						retry  = 0;
						break;

					} /* endswitch */

				} while ( ccode2 && retry );

				if ( (ccode2 != 0) && (ccode2 != 3) && (!retry) )
				{
					printk( " ... msch() retry count for "
					        "subchannel %04X exceeded, CC = %d\n",
					        irq,
					        ccode2);
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_msg_id, 0,
								    " ... msch() retry count for "
								    "subchannel %04X exceeded, CC = %d\n",
								    irq, ccode2);		    

				} /* endif */
			}
			else
			{
				/* no path available ... */
				ioinfo[irq]->ui.flags.oper = 0;
				ret                        = -ENODEV;    	

			} /* endif */
		     }
		}
		else
		{
			ret = -ENODEV;

		} /* endif */
	}
	else
	{
		ret = -ENXIO;

	} /* endif */

	return( ret );
}

/*
 * s390_SenseID
 *
 * Try to obtain the 'control unit'/'device type' information
 *  associated with the subchannel.
 *
 * The function is primarily meant to be called without irq
 *  action handler in place. However, it also allows for
 *  use with an action handler in place. If there is already
 *  an action handler registered assure it can handle the
 *  s390_SenseID() related device interrupts - interruption
 *  parameter used is 0x00E2C9C4 ( SID ).
 */
int s390_SenseID( int irq, senseid_t *sid, __u8 lpm )
{
	ccw1_t    *sense_ccw;     /* ccw area for SenseID command */
	senseid_t  isid;          /* internal sid */				
	devstat_t  devstat;       /* required by request_irq() */
	__u8       pathmask;      /* calulate path mask */
	__u8       domask;        /* path mask to use */
	int        inlreq;        /* inline request_irq() */
	int        irq_ret;       /* return code */
	devstat_t *pdevstat;      /* ptr to devstat in use */
	int        retry;         /* retry count */
	int        io_retry;      /* retry indicator */

	senseid_t *psid     = sid;/* start with the external buffer */	
	int        sbuffer  = 0;  /* switch SID data buffer */

	char dbf_txt[15];

	int failure = 0;          /* nothing went wrong yet */

	SANITY_CHECK(irq);

	if ( ioinfo[irq]->ui.flags.oper == 0 )
	{
		return( -ENODEV );

	} /* endif */

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "snsID");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	inlreq = 0; /* to make the compiler quiet... */

	if ( !ioinfo[irq]->ui.flags.ready )
	{

		pdevstat = &devstat;

		/*
		 * Perform SENSE ID command processing. We have to request device
		 *  ownership and provide a dummy I/O handler. We issue sync. I/O
		 *  requests and evaluate the devstat area on return therefore
		 *  we don't need a real I/O handler in place.
		 */
		irq_ret = request_irq( irq, init_IRQ_handler, SA_PROBE, "SID", &devstat);

		if ( irq_ret == 0 )
			inlreq = 1;
	}
	else
	{
		inlreq   = 0;
		irq_ret  = 0;
		pdevstat = ioinfo[irq]->irq_desc.dev_id;

  	} /* endif */

	if ( irq_ret == 0 )
	{
		int i;

		s390irq_spin_lock( irq);

		if ( init_IRQ_complete )
		{
			sense_ccw = kmalloc( 2*sizeof( ccw1_t), GFP_DMA);
		}
		else	
		{
			sense_ccw = alloc_bootmem_low( 2*sizeof( ccw1_t));

		} /* endif */

		// more than one path installed ?
		if ( ioinfo[irq]->schib.pmcw.pim != 0x80 )
		{
			sense_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN;
			sense_ccw[0].cda      = 0;
			sense_ccw[0].count    = 0;
			sense_ccw[0].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;

			sense_ccw[1].cmd_code = CCW_CMD_SENSE_ID;
			sense_ccw[1].cda      = (__u32)virt_to_phys( sid );
			sense_ccw[1].count    = sizeof( senseid_t);
			sense_ccw[1].flags    = CCW_FLAG_SLI;
		}
		else
		{
			sense_ccw[0].cmd_code = CCW_CMD_SENSE_ID;
			sense_ccw[0].cda      = (__u32)virt_to_phys( sid );
			sense_ccw[0].count    = sizeof( senseid_t);
			sense_ccw[0].flags    = CCW_FLAG_SLI;

		} /* endif */

		for ( i = 0 ; (i < 8) ; i++ )
		{
			pathmask = 0x80 >> i;						

			domask = ioinfo[irq]->opm & pathmask;

			if ( lpm )
				domask &= lpm;

			if ( domask )
			{
				failure = 0;
				
				psid->reserved   = 0;
				psid->cu_type    = 0xFFFF;  /* initialize fields ... */
				psid->cu_model   = 0;
				psid->dev_type   = 0;
				psid->dev_model  = 0;

				retry            = 5;  /* retry count    */
				io_retry         = 1;  /* enable retries */

				/*
				 * We now issue a SenseID request. In case of BUSY,
				 *  STATUS PENDING or non-CMD_REJECT error conditions
				 *  we run simple retries.
				 */
				do
				{
					memset( pdevstat, '\0', sizeof( devstat_t) );

					irq_ret = s390_start_IO( irq,
					                         sense_ccw,
					                         0x00E2C9C4,  // == SID
								 domask,
					                         DOIO_WAIT_FOR_INTERRUPT
					                          | DOIO_TIMEOUT
					                          | DOIO_VALID_LPM
					                          | DOIO_DONT_CALL_INTHDLR );


					if ( psid->cu_type  == 0xFFFF )
					{

						failure = 1;

						if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
						{
#ifdef CONFIG_DEBUG_IO
							printk( "SenseID : device %04X on "
							        "Subchannel %04X "
							        "reports pending status, "
							        "retry : %d\n",
							        ioinfo[irq]->schib.pmcw.dev,
								irq,
							        retry);
#endif
							if (cio_debug_initialized)
								debug_sprintf_event(cio_debug_msg_id, 2,
										    "SenseID : device %04X on "
										    "Subchannel %04X "
										    "reports pending status, "
										    "retry : %d\n",
										    ioinfo[irq]->schib.pmcw.dev,
										    irq,
										    retry);		    
						} /* endif */

						if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
						{
							/*
							 * if the device doesn't support the SenseID
							 *  command further retries wouldn't help ...
							 */
							if (  pdevstat->ii.sense.data[0]
							    & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ) )
							{
#ifdef CONFIG_DEBUG_IO
								printk( "SenseID : device %04X on "
								        "Subchannel %04X "
								        "reports cmd reject or "
								        "intervention required\n",
								        ioinfo[irq]->schib.pmcw.dev,
								        irq);
#endif
								if (cio_debug_initialized)
									debug_sprintf_event(cio_debug_msg_id, 2,
											    "SenseID : device %04X on "
											    "Subchannel %04X "
											    "reports cmd reject or "
											    "intervention required\n",
											    ioinfo[irq]->schib.pmcw.dev,
											    irq);		    
								io_retry = 0;
							}

							else
							{
#ifdef CONFIG_DEBUG_IO							
								printk( "SenseID : UC on "
								        "dev %04X, "
								        "retry %d, "
								        "lpum %02X, "
								        "cnt %02d, "
								        "sns :"
								        " %02X%02X%02X%02X "
								        "%02X%02X%02X%02X ...\n",
								        ioinfo[irq]->schib.pmcw.dev,
								        retry,
								        pdevstat->lpum,
								        pdevstat->scnt,
								        pdevstat->ii.sense.data[0],
								        pdevstat->ii.sense.data[1],
								        pdevstat->ii.sense.data[2],
								        pdevstat->ii.sense.data[3],
								        pdevstat->ii.sense.data[4],
								        pdevstat->ii.sense.data[5],
								        pdevstat->ii.sense.data[6],
								        pdevstat->ii.sense.data[7]);
#endif
								if (cio_debug_initialized) {
									debug_sprintf_event(cio_debug_msg_id, 2,
											    "SenseID : UC on "
											    "dev %04X, "
											    "retry %d, "
											    "lpum %02X, "
											    "cnt %02d, "
											    "sns :"
											    " %02X%02X%02X%02X "
											    "%02X%02X%02X%02X ...\n",
											    ioinfo[irq]->schib.pmcw.dev,
											    retry,
											    pdevstat->lpum,
											    pdevstat->scnt,
											    pdevstat->ii.sense.data[0],
											    pdevstat->ii.sense.data[1],
											    pdevstat->ii.sense.data[2],
											    pdevstat->ii.sense.data[3],
											    pdevstat->ii.sense.data[4],
											    pdevstat->ii.sense.data[5],
											    pdevstat->ii.sense.data[6],
											    pdevstat->ii.sense.data[7]);	    
									if (psid->reserved != 0xFF) 
										debug_sprintf_event(cio_debug_msg_id, 2,
												    "SenseID was not properly "
												    "executed!\n");
								}
							} /* endif */

						}
						else if (    ( pdevstat->flag & DEVSTAT_NOT_OPER )
							  || ( irq_ret        == -ENODEV         ) )
						{
#ifdef CONFIG_DEBUG_IO
							printk( "SenseID : path %02X for "
							        "device %04X on "
							        "subchannel %04X "
							        "is 'not operational'\n",
							        domask,
							        ioinfo[irq]->schib.pmcw.dev,
							        irq);
#endif
							if (cio_debug_initialized)
								debug_sprintf_event(cio_debug_msg_id, 2, 
										    "SenseID : path %02X for "
										    "device %04X on "
										    "subchannel %04X "
										    "is 'not operational'\n",
										    domask,
										    ioinfo[irq]->schib.pmcw.dev,
										    irq);		    

							io_retry          = 0;
							ioinfo[irq]->opm &= ~domask;
      	
						}

						else if (     ( pdevstat->flag !=
							       (   DEVSTAT_START_FUNCTION
								 | DEVSTAT_FINAL_STATUS    ) )
							   && !( pdevstat->flag &
								DEVSTAT_STATUS_PENDING       ) )
						{
#ifdef CONFIG_DEBUG_IO
							printk( "SenseID : start_IO() for "
							        "device %04X on "
							        "subchannel %04X "
							        "returns %d, retry %d, "
							        "status %04X\n",
							        ioinfo[irq]->schib.pmcw.dev,
							        irq,
							        irq_ret,
							        retry,
							        pdevstat->flag);
#endif
							if (cio_debug_initialized)
								debug_sprintf_event(cio_debug_msg_id, 2,
										    "SenseID : start_IO() for "
										    "device %04X on "
										    "subchannel %04X "
										    "returns %d, retry %d, "
										    "status %04X\n",
										    ioinfo[irq]->schib.pmcw.dev,
										    irq,
										    irq_ret,
										    retry,
										    pdevstat->flag);		    

						} /* endif */

					}
					else   // we got it ...
					{
						if (psid->reserved != 0xFF) {
							/* No, we failed after all... */
							failure = 1;
							retry--;

						} else {
					       
							if ( !sbuffer )	// switch buffers
							{
								/*
								 * we report back the
								 *  first hit only
								 */
								psid = &isid;
								
								if ( ioinfo[irq]->schib.pmcw.pim != 0x80 )
								{
									sense_ccw[1].cda = (__u32)virt_to_phys( psid );
								}
								else
								{
									sense_ccw[0].cda = (__u32)virt_to_phys( psid );

								} /* endif */

								/*
								 * if just the very first
								 *  was requested to be
								 *  sensed disable further
								 *  scans.
								 */	
								if ( !lpm )
									lpm = domask;
								
								sbuffer = 1;
								
							} /* endif */

							if ( pdevstat->rescnt < (sizeof( senseid_t) - 8) )
							{
								ioinfo[irq]->ui.flags.esid = 1;
       							
							} /* endif */

							io_retry = 0;
						
						}

					} /* endif */

					if ( io_retry )
					{
						retry--;

						if ( retry == 0 )
						{
							io_retry = 0;

						} /* endif */
      	
					} /* endif */

					if ((failure) && (io_retry)) {
						/* reset fields... */

						failure = 0;
						
						psid->reserved   = 0;
						psid->cu_type    = 0xFFFF;  
						psid->cu_model   = 0;
						psid->dev_type   = 0;
						psid->dev_model  = 0;
					}
						
				} while ( (io_retry) );

 			} /* endif - domask */

		} /* endfor */

		if ( init_IRQ_complete )
		{
			kfree( sense_ccw );
		}
		else	
		{
			free_bootmem( (unsigned long)sense_ccw, 2*sizeof(ccw1_t) );

		} /* endif */

		s390irq_spin_unlock( irq);

		/*
		 * If we installed the irq action handler we have to
		 *  release it too.
		 */
		if ( inlreq )
			free_irq( irq, pdevstat);

		/*
		 * if running under VM check there ... perhaps we should do
		 *  only if we suffered a command reject, but it doesn't harm
		 */
		if (    ( sid->cu_type == 0xFFFF    )
		     && ( MACHINE_IS_VM             ) )
		{
			VM_virtual_device_info( ioinfo[irq]->schib.pmcw.dev,
			                        sid );
		} /* endif */

		if ( sid->cu_type == 0xFFFF )
		{
			/*
			 * SenseID CU-type of 0xffff indicates that no device
			 *  information could be retrieved (pre-init value).
			 *
			 * If we can't couldn't identify the device type we
			 *  consider the device "not operational".
			 */
#ifdef CONFIG_DEBUG_IO
			printk( "SenseID : unknown device %04X on subchannel %04X\n",
			        ioinfo[irq]->schib.pmcw.dev,
			        irq);
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_msg_id, 2,
						    "SenseID : unknown device %04X on subchannel %04X\n",
						    ioinfo[irq]->schib.pmcw.dev,
						    irq);		    
			ioinfo[irq]->ui.flags.unknown = 1;

		} /* endif */

	
		/*
		 * Issue device info message if unit was operational .
		 */
		if ( !ioinfo[irq]->ui.flags.unknown ) {
			if ( sid->dev_type != 0 ) {
				if ( cio_show_msg ) 
					printk( KERN_INFO"SenseID : device %04X reports: "
						"CU  Type/Mod = %04X/%02X,"
						" Dev Type/Mod = %04X/%02X\n",
						ioinfo[irq]->schib.pmcw.dev,
						sid->cu_type,
						sid->cu_model,
						sid->dev_type,
						sid->dev_model);
				if (cio_debug_initialized)
					debug_sprintf_event(cio_debug_msg_id, 2,
							    "SenseID : device %04X reports: "
							    "CU  Type/Mod = %04X/%02X,"
							    " Dev Type/Mod = %04X/%02X\n",
							    ioinfo[irq]->schib.pmcw.dev,
							    sid->cu_type,
							    sid->cu_model,
							    sid->dev_type,
							    sid->dev_model);
			} else {
				if ( cio_show_msg ) 
					printk( KERN_INFO"SenseID : device %04X reports:"
						" Dev Type/Mod = %04X/%02X\n",
						ioinfo[irq]->schib.pmcw.dev,
						sid->cu_type,
						sid->cu_model);
				if (cio_debug_initialized)
					debug_sprintf_event(cio_debug_msg_id, 2,
							    "SenseID : device %04X reports:"
							    " Dev Type/Mod = %04X/%02X\n",
							    ioinfo[irq]->schib.pmcw.dev,
							    sid->cu_type,
							    sid->cu_model);		    
			} /* endif */

		} /* endif */

		if ( !ioinfo[irq]->ui.flags.unknown )
			irq_ret = 0;
		else
			irq_ret = -ENODEV;

	} /* endif */

   return( irq_ret );
}

static int __inline__ s390_SetMultiPath( int irq )
{
	int cc;

	cc = stsch( irq, &ioinfo[irq]->schib );

	if ( !cc )
	{
		ioinfo[irq]->schib.pmcw.mp = 1;     /* multipath mode */

		cc = msch( irq, &ioinfo[irq]->schib );

	} /* endif */

	return( cc);
}

/*
 * Device Path Verification
 *
 * Path verification is accomplished by checking which paths (CHPIDs) are
 *  available. Further, a path group ID is set, if possible in multipath
 *  mode, otherwise in single path mode.
 *
 * Note : This function must not be called during normal device recognition,
 *         but during device driver initiated request_irq() processing only.
 */
int s390_DevicePathVerification( int irq, __u8 usermask )
{
	int  ccode;
	__u8 pathmask;
	__u8 domask;

	int ret = 0;

	char dbf_txt[15];

	if (cio_debug_initialized) {
		debug_text_event(cio_debug_trace_id, 4, "dpver");
		sprintf(dbf_txt, "%x", irq);
		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
	}

	if ( ioinfo[irq]->ui.flags.pgid_supp == 0 )
	{
		return( 0);	// just exit ...

	} /* endif */

	ccode = stsch( irq, &(ioinfo[irq]->schib) );

	if ( ccode )
	{
		ret = -ENODEV;
	}
	else if ( ioinfo[irq]->schib.pmcw.pim == 0x80 )
	{
		/*
		 * no error, just not required for single path only devices
		 */	
		ioinfo[irq]->ui.flags.pgid_supp = 0;
		ret = 0;
	}
	else
	{
		int    i;
		pgid_t pgid;
		__u8   dev_path;
		int    first  = 1;

		ioinfo[irq]->opm =   ioinfo[irq]->schib.pmcw.pim
		                   & ioinfo[irq]->schib.pmcw.pam
		                   & ioinfo[irq]->schib.pmcw.pom;

		if ( usermask )
		{
			dev_path = usermask;
		}
		else
		{
			dev_path = ioinfo[irq]->opm;

		} /* endif */

		/*
		 * let's build a path group ID if we don't have one yet
		 */
		if ( ioinfo[irq]->ui.flags.pgid == 0)
		{
			ioinfo[irq]->pgid.cpu_addr  = *(__u16 *)__LC_CPUADDR;
			ioinfo[irq]->pgid.cpu_id    = ((cpuid_t *)__LC_CPUID)->ident;
			ioinfo[irq]->pgid.cpu_model = ((cpuid_t *)__LC_CPUID)->machine;
			ioinfo[irq]->pgid.tod_high  = *(__u32 *)&irq_IPL_TOD;

			ioinfo[irq]->ui.flags.pgid  = 1;

		} /* endif */     		

		memcpy( &pgid, &ioinfo[irq]->pgid, sizeof(pgid_t));

		for ( i = 0; i < 8 && !ret ; i++)
		{
			pathmask = 0x80 >> i;						

			domask = dev_path & pathmask;

			if ( domask )
			{
				ret = s390_SetPGID( irq, domask, &pgid );

				/*
				 * For the *first* path we are prepared
				 *  for recovery
				 *
				 *  - If we fail setting the PGID we assume its
				 *     using  a different PGID already (VM) we
				 *     try to sense.
				 */
				if ( ret == -EOPNOTSUPP && first )
				{
					*(int *)&pgid = 0;
					
					ret   = s390_SensePGID( irq, domask, &pgid);
					first = 0;

					if ( ret == 0 )
					{
						/*
						 * Check whether we retrieved
						 *  a reasonable PGID ...
						 */	
						if ( pgid.inf.ps.state1 == SNID_STATE1_GROUPED )
						{
							memcpy( &(ioinfo[irq]->pgid),
							        &pgid,
							        sizeof(pgid_t) );
						}
						else // ungrouped or garbage ...
						{
							ret = -EOPNOTSUPP;

						} /* endif */
					}
					else
					{
						ioinfo[irq]->ui.flags.pgid_supp = 0;

#ifdef CONFIG_DEBUG_IO
						printk( "PathVerification(%04X) "
						        "- Device %04X doesn't "
						        " support path grouping\n",
						        irq,
						        ioinfo[irq]->schib.pmcw.dev);
#endif
						if (cio_debug_initialized)
							debug_sprintf_event(cio_debug_msg_id, 2,
									    "PathVerification(%04X) "
									    "- Device %04X doesn't "
									    " support path grouping\n",
									    irq,
									    ioinfo[irq]->schib.pmcw.dev);		    

					} /* endif */
				}
				else if ( ret == -EIO ) 
				{
#ifdef CONFIG_DEBUG_IO
					printk("PathVerification(%04X) - I/O error "
					       "on device %04X\n", irq,
					       ioinfo[irq]->schib.pmcw.dev);
#endif

					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_msg_id, 2,
								    "PathVerification(%04X) - I/O error "
								    "on device %04X\n", irq,
								    ioinfo[irq]->schib.pmcw.dev);

					ioinfo[irq]->ui.flags.pgid_supp = 0;
		    
				} else {
#ifdef CONFIG_DEBUG_IO
					printk( "PathVerification(%04X) "
						"- Unexpected error on device %04X\n",
						irq,
						ioinfo[irq]->schib.pmcw.dev);
#endif
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_msg_id, 2,
								    "PathVerification(%04X) - "
								    "Unexpected error on device %04X\n",
								    irq,
								    ioinfo[irq]->schib.pmcw.dev);		    
					
					ioinfo[irq]->ui.flags.pgid_supp = 0;
					
				} /* endif */

			} /* endif */
			
		} /* endfor */

	} /* endif */

	return ret;

}

/*
 * s390_SetPGID
 *
 * Set Path Group ID
 *
 */
int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid )
{
	ccw1_t    *spid_ccw;    /* ccw area for SPID command */
	devstat_t  devstat;     /* required by request_irq() */
	devstat_t *pdevstat = &devstat;
        unsigned long flags;


	int        irq_ret = 0; /* return code */
	int        retry   = 5; /* retry count */
	int        inlreq  = 0; /* inline request_irq() */
	int        mpath   = 1; /* try multi-path first */

	SANITY_CHECK(irq);

	if ( ioinfo[irq]->ui.flags.oper == 0 )
	{
		return( -ENODEV );

	} /* endif */

	if ( !ioinfo[irq]->ui.flags.ready )
	{
		/*
		 * Perform SetPGID command processing. We have to request device
		 *  ownership and provide a dummy I/O handler. We issue sync. I/O
		 *  requests and evaluate the devstat area on return therefore
		 *  we don't need a real I/O handler in place.
		 */
		irq_ret = request_irq( irq,
		                       init_IRQ_handler,
		                       SA_PROBE,
		                       "SPID",
		                       pdevstat);

		if ( irq_ret == 0 )
			inlreq = 1;
	}
	else
	{
		pdevstat = ioinfo[irq]->irq_desc.dev_id;

	} /* endif */

	if ( irq_ret == 0 )
	{
		s390irq_spin_lock_irqsave( irq, flags);

		if ( init_IRQ_complete )
		{
			spid_ccw = kmalloc( 2*sizeof( ccw1_t), GFP_DMA);
		}
		else	
		{
			spid_ccw = alloc_bootmem_low( 2*sizeof( ccw1_t));

		} /* endif */

		spid_ccw[0].cmd_code = 0x5B;	/* suspend multipath reconnect */
		spid_ccw[0].cda      = 0;
		spid_ccw[0].count    = 0;
		spid_ccw[0].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;

		spid_ccw[1].cmd_code = CCW_CMD_SET_PGID;
		spid_ccw[1].cda      = (__u32)virt_to_phys( pgid );
		spid_ccw[1].count    = sizeof( pgid_t);
		spid_ccw[1].flags    = CCW_FLAG_SLI;

		pgid->inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH;

		/*
		 * We now issue a SetPGID request. In case of BUSY
		 *  or STATUS PENDING conditions we retry 5 times.
		 */
		do
		{
			memset( pdevstat, '\0', sizeof( devstat_t) );

			irq_ret = s390_start_IO( irq,
			                         spid_ccw,
			                         0xE2D7C9C4,  // == SPID
			                         lpm,         // n/a
			                         DOIO_WAIT_FOR_INTERRUPT
			                          | DOIO_VALID_LPM
			                          | DOIO_DONT_CALL_INTHDLR );

			if ( !irq_ret )
			{
				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
				{
#ifdef CONFIG_DEBUG_IO
					printk( "SPID - Device %04X "
					        "on Subchannel %04X "
					        "reports pending status, "
					        "retry : %d\n",
					        ioinfo[irq]->schib.pmcw.dev,
					        irq,
					        retry);
#endif
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_msg_id, 2,
								    "SPID - Device %04X "
								    "on Subchannel %04X "
								    "reports pending status, "
								    "retry : %d\n",
								    ioinfo[irq]->schib.pmcw.dev,
								    irq,
								    retry);	    
					retry--;
					irq_ret = -EIO;
				} /* endif */

				if ( pdevstat->flag == (   DEVSTAT_START_FUNCTION
				                         | DEVSTAT_FINAL_STATUS   ) )
				{
					retry = 0;	// successfully set ...
					irq_ret = 0;
				}
				else if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
				{
					/*
					 * If the device doesn't support the
					 *  Sense Path Group ID command
					 *  further retries wouldn't help ...
					 */
					if ( pdevstat->ii.sense.data[0] & SNS0_CMD_REJECT )
					{
						if ( mpath )
						{
							pgid->inf.fc =   SPID_FUNC_SINGLE_PATH
							               | SPID_FUNC_ESTABLISH;
							mpath        = 0;
							retry--;
							irq_ret = -EIO;
						}
						else
						{
							irq_ret = -EOPNOTSUPP;
							retry   = 0;			

						} /* endif */
					}
					else
					{
#ifdef CONFIG_DEBUG_IO
						printk( "SPID - device %04X,"
						        " unit check,"
						        " retry %d, cnt %02d,"
						        " sns :"
						        " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
						        ioinfo[irq]->schib.pmcw.dev,
						        retry,
						        pdevstat->scnt,
						        pdevstat->ii.sense.data[0],
						        pdevstat->ii.sense.data[1],
						        pdevstat->ii.sense.data[2],
						        pdevstat->ii.sense.data[3],
						        pdevstat->ii.sense.data[4],
						        pdevstat->ii.sense.data[5],
						        pdevstat->ii.sense.data[6],
						        pdevstat->ii.sense.data[7]);
#endif

						if (cio_debug_initialized)
							debug_sprintf_event(cio_debug_msg_id, 2,
									    "SPID - device %04X,"
									    " unit check,"
									    " retry %d, cnt %02d,"
									    " sns :"
									    " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
									    ioinfo[irq]->schib.pmcw.dev,
									    retry,
									    pdevstat->scnt,
									    pdevstat->ii.sense.data[0],
									    pdevstat->ii.sense.data[1],
									    pdevstat->ii.sense.data[2],
									    pdevstat->ii.sense.data[3],
									    pdevstat->ii.sense.data[4],
									    pdevstat->ii.sense.data[5],
									    pdevstat->ii.sense.data[6],
									    pdevstat->ii.sense.data[7]);

						retry--;
						irq_ret = -EIO;
		    
					} /* endif */

				}
				else if ( pdevstat->flag & DEVSTAT_NOT_OPER )
				{
					/* don't issue warnings during startup unless requested*/
					if (init_IRQ_complete || cio_notoper_msg) {   
						
						printk( "SPID - Device %04X "
							"on Subchannel %04X "
							"became 'not operational'\n",
							ioinfo[irq]->schib.pmcw.dev,
							irq);
						if (cio_debug_initialized)
							debug_sprintf_event(cio_debug_msg_id, 2,
									    "SPID - Device %04X "
									    "on Subchannel %04X "
									    "became 'not operational'\n",
									    ioinfo[irq]->schib.pmcw.dev,
									    irq);		    
					}

					retry = 0;
					irq_ret = -EIO;

				} /* endif */
			}
			else if ( irq_ret != -ENODEV )
			{
				retry--;
				irq_ret = -EIO;
			}
			else
			{
				retry = 0;
				irq_ret = -ENODEV;

			} /* endif */

		} while ( retry > 0 );


		if ( init_IRQ_complete )
		{
			kfree( spid_ccw );
		}
		else	
		{
			free_bootmem( (unsigned long)spid_ccw, 2*sizeof(ccw1_t) );

		} /* endif */

		s390irq_spin_unlock_irqrestore( irq, flags);

		/*
		 * If we installed the irq action handler we have to
		 *  release it too.
		 */
		if ( inlreq )
			free_irq( irq, pdevstat);

	} /* endif */

   return( irq_ret );
}


/*
 * s390_SensePGID
 *
 * Sense Path Group ID
 *
 */
int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid )
{
	ccw1_t    *snid_ccw;    /* ccw area for SNID command */
	devstat_t  devstat;     /* required by request_irq() */
	devstat_t *pdevstat = &devstat;

	int        irq_ret = 0; /* return code */
	int        retry   = 5; /* retry count */
	int        inlreq  = 0; /* inline request_irq() */
	unsigned long flags;

	SANITY_CHECK(irq);

	if ( ioinfo[irq]->ui.flags.oper == 0 )
	{
		return( -ENODEV );

	} /* endif */

	if ( !ioinfo[irq]->ui.flags.ready )
	{
		/*
		 * Perform SENSE PGID command processing. We have to request device
		 *  ownership and provide a dummy I/O handler. We issue sync. I/O
		 *  requests and evaluate the devstat area on return therefore
		 *  we don't need a real I/O handler in place.
		 */
		irq_ret = request_irq( irq,
		                       init_IRQ_handler,
		                       SA_PROBE,
		                       "SNID",
		                       pdevstat);

		if ( irq_ret == 0 )
			inlreq = 1;

   }
	else
	{
		pdevstat = ioinfo[irq]->irq_desc.dev_id;

	} /* endif */

	if ( irq_ret == 0 )
	{
		s390irq_spin_lock_irqsave( irq, flags);

		if ( init_IRQ_complete )
		{
			snid_ccw = kmalloc( sizeof( ccw1_t), GFP_DMA);
		}
		else	
		{
			snid_ccw = alloc_bootmem_low( sizeof( ccw1_t));

		} /* endif */

		snid_ccw->cmd_code = CCW_CMD_SENSE_PGID;
		snid_ccw->cda      = (__u32)virt_to_phys( pgid );
		snid_ccw->count    = sizeof( pgid_t);
		snid_ccw->flags    = CCW_FLAG_SLI;

		/*
		 * We now issue a SensePGID request. In case of BUSY
		 *  or STATUS PENDING conditions we retry 5 times.
		 */
		do
		{
			memset( pdevstat, '\0', sizeof( devstat_t) );

			irq_ret = s390_start_IO( irq,
			                         snid_ccw,
			                         0xE2D5C9C4,  // == SNID
			                         lpm,         // n/a
			                         DOIO_WAIT_FOR_INTERRUPT
			                          | DOIO_VALID_LPM
			                          | DOIO_DONT_CALL_INTHDLR );

			if ( irq_ret == 0 )
			{
				if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
				{
					/*
					 * If the device doesn't support the
					 *  Sense Path Group ID command
					 *  further retries wouldn't help ...
					 */
					if ( pdevstat->ii.sense.data[0] & SNS0_CMD_REJECT )
					{
						retry   = 0;
						irq_ret = -EOPNOTSUPP;
					}
					else
					{
#ifdef CONFIG_DEBUG_IO
						printk( "SNID - device %04X,"
						        " unit check,"
						        " flag %04X, "
						        " retry %d, cnt %02d,"
						        " sns :"
						        " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
						        ioinfo[irq]->schib.pmcw.dev,
						        pdevstat->flag,
						        retry,
						        pdevstat->scnt,
						        pdevstat->ii.sense.data[0],
						        pdevstat->ii.sense.data[1],
						        pdevstat->ii.sense.data[2],
						        pdevstat->ii.sense.data[3],
						        pdevstat->ii.sense.data[4],
						        pdevstat->ii.sense.data[5],
						        pdevstat->ii.sense.data[6],
						        pdevstat->ii.sense.data[7]);

#endif
						if (cio_debug_initialized)
							debug_sprintf_event(cio_debug_msg_id, 2,
									    "SNID - device %04X,"
									    " unit check,"
									    " flag %04X, "
									    " retry %d, cnt %02d,"
									    " sns :"
									    " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
									    ioinfo[irq]->schib.pmcw.dev,
									    pdevstat->flag,
									    retry,
									    pdevstat->scnt,
									    pdevstat->ii.sense.data[0],
									    pdevstat->ii.sense.data[1],
									    pdevstat->ii.sense.data[2],
									    pdevstat->ii.sense.data[3],
									    pdevstat->ii.sense.data[4],
									    pdevstat->ii.sense.data[5],
									    pdevstat->ii.sense.data[6],
									    pdevstat->ii.sense.data[7]);		    
						retry--;
						irq_ret = -EIO;

					} /* endif */
				}
				else if ( pdevstat->flag & DEVSTAT_NOT_OPER )
				{
					/* don't issue warnings during startup unless requested*/
					if (init_IRQ_complete || cio_notoper_msg) {  
						printk( "SNID - Device %04X "
							"on Subchannel %04X "
							"became 'not operational'\n",
							ioinfo[irq]->schib.pmcw.dev,
							irq);
						if (cio_debug_initialized)
							debug_sprintf_event(cio_debug_msg_id, 2,
									    "SNID - Device %04X "
									    "on Subchannel %04X "
									    "became 'not operational'\n",
									    ioinfo[irq]->schib.pmcw.dev,
									    irq);		    
					}

					retry = 0;
					irq_ret = -EIO;

				}
				else
				{
					retry = 0; // success ...
					irq_ret = 0;

				} /* endif */
			}
			else if ( irq_ret != -ENODEV ) // -EIO, or -EBUSY
			{

				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
				{
#ifdef CONFIG_DEBUG_IO
					printk( "SNID - Device %04X "
					        "on Subchannel %04X "
					        "reports pending status, "
					        "retry : %d\n",
					        ioinfo[irq]->schib.pmcw.dev,
					        irq,
					        retry);
#endif
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_msg_id, 2,
								    "SNID - Device %04X "
								    "on Subchannel %04X "
								    "reports pending status, "
								    "retry : %d\n",
								    ioinfo[irq]->schib.pmcw.dev,
								    irq,
								    retry);		    
				} /* endif */


				printk( "SNID - device %04X,"
				        " start_io() reports rc : %d, retrying ...\n",
				        ioinfo[irq]->schib.pmcw.dev,
				        irq_ret);
				if (cio_debug_initialized)
					debug_sprintf_event(cio_debug_msg_id, 2, 
							    "SNID - device %04X,"
							    " start_io() reports rc : %d, retrying ...\n",
							    ioinfo[irq]->schib.pmcw.dev,
							    irq_ret);
				retry--;
				irq_ret = -EIO;
			}
			else	// -ENODEV ...
			{
				retry = 0;
				irq_ret = -ENODEV;

			} /* endif */

		} while ( retry > 0 );


		if ( init_IRQ_complete )
		{
			kfree( snid_ccw );
		}
		else	
		{
			free_bootmem( (unsigned long)snid_ccw, sizeof(ccw1_t) );

		} /* endif */

		s390irq_spin_unlock_irqrestore( irq, flags);

		/*
		 * If we installed the irq action handler we have to
		 *  release it too.
		 */
		if ( inlreq )
			free_irq( irq, pdevstat);

	} /* endif */

   return( irq_ret );
}

/*
 * s390_do_crw_pending
 *
 * Called by the machine check handler to process CRW pending
 *  conditions. It may be a single CRW, or CRWs may be chained.
 *
 * Note : we currently process CRWs for subchannel source only
 */
void s390_do_crw_pending( crwe_t *pcrwe )
{
	int irq;
	int chpid;
	int dev_oper = 0;
	int dev_no   = -1;	
	int lock     = 0;

#ifdef CONFIG_DEBUG_CRW
	printk( "do_crw_pending : starting ...\n");
#endif
	if (cio_debug_initialized) 
		debug_sprintf_event(cio_debug_crw_id, 2, 
				    "do_crw_pending: starting\n");
	while ( pcrwe != NULL )
	{
		int is_owned = 0;

		switch ( pcrwe->crw.rsc ) {	
		case CRW_RSC_SCH :

			irq = pcrwe->crw.rsid;

#ifdef CONFIG_DEBUG_CRW
			printk( KERN_INFO"do_crw_pending : source is "
			        "subchannel %04X\n", irq);
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 2,
						    "source is subchannel %04X\n", irq);
			/*
			 * If the device isn't known yet
			 *   we can't lock it ...
			 */
			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
			{
				s390irq_spin_lock( irq );
				lock = 1;

				dev_oper = ioinfo[irq]->ui.flags.oper;

				if ( ioinfo[irq]->ui.flags.dval )
					dev_no = ioinfo[irq]->devno;

				is_owned = ioinfo[irq]->ui.flags.ready;

			} /* endif */

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : subchannel validation - start ...\n");
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 4,
						    "subchannel validation - start\n");
			s390_validate_subchannel( irq, is_owned );

			if ( irq > highest_subchannel )
				highest_subchannel = irq;

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : subchannel validation - done\n");
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 4,
						    "subchannel validation - done\n");
			/*
			 * After the validate processing
			 *   the ioinfo control block
			 *   should be allocated ...
			 */
			if ( lock )
			{
				s390irq_spin_unlock( irq );

			} /* endif */


			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
			{
#ifdef CONFIG_DEBUG_CRW
				printk( "do_crw_pending : ioinfo at "
#ifdef CONFIG_ARCH_S390X
					"%08lX\n",
					(unsigned long)ioinfo[irq]);
#else /* CONFIG_ARCH_S390X */
					"%08X\n",
				        (unsigned)ioinfo[irq]);
#endif /* CONFIG_ARCH_S390X */
#endif
				if (cio_debug_initialized)
					debug_sprintf_event(cio_debug_crw_id, 4,
							    "ioinfo at "
#ifdef CONFIG_ARCH_S390X
							    "%08lX\n",
							    (unsigned long)ioinfo[irq]);
#else /* CONFIG_ARCH_S390X */
				                            "%08X\n",
							    (unsigned)ioinfo[irq]);
#endif /* CONFIG_ARCH_S390X */
			} /* endif */


			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
			{
				if ( ioinfo[irq]->ui.flags.oper == 0 )
				{
					 not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc;
#ifdef CONFIG_PROC_FS					 
					 /* remove procfs entry */
					 if (cio_proc_devinfo)
						 cio_procfs_device_remove(dev_no);
#endif
					/*
					 * If the device has gone
					 *  call not oper handler        	
					 */       	
					 if (( dev_oper == 1 )
					     && ( nopfunc != NULL))
					{
						
						free_irq( irq,ioinfo[irq]->irq_desc.dev_id );
						nopfunc( irq,DEVSTAT_DEVICE_GONE );			

					} /* endif */
				}
				else
				{
#ifdef CONFIG_DEBUG_CRW
					printk( "do_crw_pending : device "
					        "recognition - start ...\n");
#endif
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_crw_id, 4,
								    "device recognition - start\n");
					s390_device_recognition_irq( irq );

#ifdef CONFIG_DEBUG_CRW
					printk( "do_crw_pending : device "
					        "recognition - done\n");
#endif
					if (cio_debug_initialized)
						debug_sprintf_event(cio_debug_crw_id, 4,
								    "device recognition - done\n");
					/*
					 * the device became operational
					 */
					if ( dev_oper == 0 )
					{
						devreg_t *pdevreg;

						pdevreg = s390_search_devreg( ioinfo[irq] );

						if ( pdevreg != NULL )
						{
							if ( pdevreg->oper_func != NULL )
								pdevreg->oper_func( irq, pdevreg );

						} /* endif */
#ifdef CONFIG_PROC_FS
						/* add new procfs entry */
						if (cio_proc_devinfo) 
							if (highest_subchannel < MAX_CIO_PROCFS_ENTRIES) {
								cio_procfs_device_create(ioinfo[irq]->devno);
							}
#endif
					}
					/*
					 * ... it is and was operational, but
					 *      the devno may have changed
					 */
					else if ((ioinfo[irq]->devno != dev_no) && ( ioinfo[irq]->nopfunc != NULL ))   					
					{
#ifdef CONFIG_PROC_FS
						int devno_old = ioinfo[irq]->devno;
#endif
						ioinfo[irq]->nopfunc( irq,
						                      DEVSTAT_REVALIDATE );				
#ifdef CONFIG_PROC_FS
						/* remove old entry, add new */
						if (cio_proc_devinfo) {
							cio_procfs_device_remove(devno_old);
							cio_procfs_device_create(ioinfo[irq]->devno);
						}
#endif
					} /* endif */

				} /* endif */
#ifdef CONFIG_PROC_FS
				/* get rid of dead procfs entries */
				if (cio_proc_devinfo) 
					cio_procfs_device_purge();
#endif
			} /* endif */

			break;

		case CRW_RSC_MONITOR :

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : source is "
			        "monitoring facility\n");
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 2,
						    "source is monitoring facility\n");
			break;

		case CRW_RSC_CPATH :   	

			chpid = pcrwe->crw.rsid;

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : source is "
			        "channel path %02X\n", chpid);
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 2,
						    "source is channel path %02X\n");
			break;

		case CRW_RSC_CONFIG : 	

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : source is "
			        "configuration-alert facility\n");
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 2,
						    "source is configuration-alert facility\n");
			break;

		case CRW_RSC_CSS :

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : source is "
			        "channel subsystem\n");
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 2,
						    "source is channel subsystem\n");
			break;

		default :

#ifdef CONFIG_DEBUG_CRW
			printk( "do_crw_pending : unknown source\n");
#endif
			if (cio_debug_initialized)
				debug_sprintf_event(cio_debug_crw_id, 2,
						    "unknown source\n");
			break;		

		} /* endswitch */

		pcrwe = pcrwe->crwe_next;

	} /* endwhile */

#ifdef CONFIG_DEBUG_CRW
	printk( "do_crw_pending : done\n");
#endif
	if (cio_debug_initialized)
		debug_sprintf_event(cio_debug_crw_id, 2,
				    "do_crw_pending: done\n");
   return;
}


/* added by Holger Smolinski for reipl support in reipl.S */
extern void do_reipl (int);
void
reipl ( int sch )
{
	int i;
	s390_dev_info_t dev_info;

	for ( i = 0; i <= highest_subchannel; i ++ ) 
	{
		if (    get_dev_info_by_irq( i, &dev_info ) == 0
                    && (dev_info.status & DEVSTAT_DEVICE_OWNED) )
		{
			free_irq ( i, ioinfo[i]->irq_desc.dev_id );
		}
	}

	if (MACHINE_IS_VM)
		cpcmd("IPL", NULL, 0);
	else
		do_reipl( 0x10000 | sch );
}


/*
 * Function: cio_debug_init
 * Initializes three debug logs (under /proc/s390dbf) for common I/O:
 * - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on
 * - cio_trace logs the calling of different functions
 * - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on
 * debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW
 */
int cio_debug_init( void )
{
	int ret = 0;

	cio_debug_msg_id = debug_register("cio_msg",2,4,16*sizeof(long));
	if (cio_debug_msg_id != NULL) {
		debug_register_view(cio_debug_msg_id, &debug_sprintf_view);
#ifdef CONFIG_DEBUG_IO
		debug_set_level(cio_debug_msg_id, 6);
#else /* CONFIG_DEBUG_IO */
		debug_set_level(cio_debug_msg_id, 2);
#endif /* CONFIG_DEBUG_IO */
	} else {
		ret = -1;
	}
	cio_debug_trace_id = debug_register("cio_trace",4,4,8);
	if (cio_debug_trace_id != NULL) {
		debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view);
#ifdef CONFIG_DEBUG_IO
		debug_set_level(cio_debug_trace_id, 6);
#else /* CONFIG_DEBUG_IO */
		debug_set_level(cio_debug_trace_id, 2);
#endif /* CONFIG_DEBUG_IO */
	} else {
		ret = -1;
	}
	cio_debug_crw_id = debug_register("cio_crw",2,4,16*sizeof(long));
	if (cio_debug_crw_id != NULL) {
		debug_register_view(cio_debug_crw_id, &debug_sprintf_view);
#ifdef CONFIG_DEBUG_CRW
		debug_set_level(cio_debug_crw_id, 6);
#else /* CONFIG_DEBUG_CRW */
		debug_set_level(cio_debug_crw_id, 2);
#endif /* CONFIG_DEBUG_CRW */
	} else {
		ret = -1;
	}
	if (ret)
		return ret;
	cio_debug_initialized = 1;
	return 0;
}

__initcall(cio_debug_init);

#ifdef CONFIG_PROC_FS
/* 
 * Display info on subchannels in /proc/subchannels. 
 * Adapted from procfs stuff in dasd.c by Cornelia Huck, 02/28/01.      
 */

typedef struct {
     char *data;
     int len;
} tempinfo_t;

#define MIN(a,b) ((a)<(b)?(a):(b))

static struct proc_dir_entry *chan_subch_entry;

static int chan_subch_open( struct inode *inode, struct file *file)
{
     int rc = 0;
     int size = 1;
     int len = 0;
     int i = 0;
     int j = 0;
     tempinfo_t *info;
     
     info = (tempinfo_t *) vmalloc( sizeof (tempinfo_t));
     if (info == NULL) {
	  printk( KERN_WARNING "No memory available for data\n");
	  return -ENOMEM;
     } else {
	  file->private_data = ( void * ) info;
     }

     size += (highest_subchannel+1) * 128;
     info->data = (char *) vmalloc( size );
     
     if (size && info->data == NULL) {
		printk (KERN_WARNING "No memory available for data\n");
		vfree (info);
		return -ENOMEM;
	}
     
     len += sprintf( info->data+len, 
		     "Device sch.  Dev Type/Model CU  in use  PIM PAM POM LPUM CHPIDs\n");
     len += sprintf( info->data+len, 
		     "--------------------------------------------------------------------------\n");

     for ( i=0; i <= highest_subchannel; i++) {
	  if ( !((ioinfo[i] == NULL) || (ioinfo[i] == INVALID_STORAGE_AREA) || !(ioinfo[i]->ui.flags.oper)) ) {
	       len += sprintf( info->data+len, 
			       "%04X   %04X  ", 
			       ioinfo[i]->schib.pmcw.dev, 
			       i );
	       if ( ioinfo[i]->senseid.dev_type != 0 ) {
		    len += sprintf( info->data+len, 
				    "%04X/%02X   %04X/%02X", 
				    ioinfo[i]->senseid.dev_type,
				    ioinfo[i]->senseid.dev_model, 
				    ioinfo[i]->senseid.cu_type,
				    ioinfo[i]->senseid.cu_model );
	       } else {
		    len += sprintf( info->data+len, 
				    "          %04X/%02X", 
				    ioinfo[i]->senseid.cu_type, 
				    ioinfo[i]->senseid.cu_model );
	       }
	       if (ioinfo[i]->ui.flags.ready) {
		    len += sprintf( info->data+len, "  yes " );
	       } else {
		    len += sprintf( info->data+len, "      " );
	       }
	       len += sprintf( info->data+len,
			       "    %02X  %02X  %02X  %02X   ",
			       ioinfo[i]->schib.pmcw.pim,
			       ioinfo[i]->schib.pmcw.pam,
			       ioinfo[i]->schib.pmcw.pom,
			       ioinfo[i]->schib.pmcw.lpum );
	       for ( j=0; j < 8; j++ ) {
		    len += sprintf( info->data+len,
				    "%02X",
				    ioinfo[i]->schib.pmcw.chpid[j] );
		    if (j==3) {
			 len += sprintf( info->data+len, " " );
		    }
	       }
	       len += sprintf( info->data+len, "\n" );
	  }
     }
     info->len = len;

     return rc;
}

static int chan_subch_close( struct inode *inode, struct file *file)
{
     int rc = 0;
     tempinfo_t *p_info = (tempinfo_t *) file->private_data;

     if (p_info) {
	  if (p_info->data)
	       vfree( p_info->data );
	  vfree( p_info );
     }
     
     return rc;
}

static ssize_t chan_subch_read( struct file *file, char *user_buf, size_t user_len, loff_t * offset)
{
     loff_t len;
     tempinfo_t *p_info = (tempinfo_t *) file->private_data;
     
     if ( *offset>=p_info->len) {
	  return 0;
     } else {
	  len = MIN(user_len, (p_info->len - *offset));
	  if (copy_to_user( user_buf, &(p_info->data[*offset]), len))
	       return -EFAULT; 
	  (* offset) += len;
	  return len;
     }
}

static struct file_operations chan_subch_file_ops =
{
     read:chan_subch_read,
     open:chan_subch_open,
     release:chan_subch_close,
};

static int chan_proc_init( void )
{
     chan_subch_entry = create_proc_entry( "subchannels", S_IFREG|S_IRUGO, &proc_root);
     chan_subch_entry->proc_fops = &chan_subch_file_ops;

     return 1;
}

__initcall(chan_proc_init);

void chan_proc_cleanup( void )
{
     remove_proc_entry( "subchannels", &proc_root);
}

/* 
 * Display device specific information under /proc/deviceinfo/<devno>
 */

static struct proc_dir_entry *cio_procfs_deviceinfo_root = NULL;

/* 
 * cio_procfs_device_list holds all devno-specific procfs directories
 */

typedef struct {
	int devno;
	struct proc_dir_entry *cio_device_entry;
	struct proc_dir_entry *cio_sensedata_entry;
	struct proc_dir_entry *cio_in_use_entry;
	struct proc_dir_entry *cio_chpid_entry;
} cio_procfs_entry_t;

typedef struct _cio_procfs_device{
	struct _cio_procfs_device *next;
	cio_procfs_entry_t *entry;
} cio_procfs_device_t;

cio_procfs_device_t *cio_procfs_device_list = NULL;

/*
 * File operations
 */

static int cio_device_entry_close( struct inode *inode, struct file *file)
{
     int rc = 0;
     tempinfo_t *p_info = (tempinfo_t *) file->private_data;

     if (p_info) {
	  if (p_info->data)
	       vfree( p_info->data );
	  vfree( p_info );
     }
     
     return rc;
}

static ssize_t cio_device_entry_read( struct file *file, char *user_buf, size_t user_len, loff_t * offset)
{
     loff_t len;
     tempinfo_t *p_info = (tempinfo_t *) file->private_data;
     
     if ( *offset>=p_info->len) {
	  return 0;
     } else {
	  len = MIN(user_len, (p_info->len - *offset));
	  if (copy_to_user( user_buf, &(p_info->data[*offset]), len))
	       return -EFAULT; 
	  (* offset) += len;
	  return len;
     }
}


static int cio_sensedata_entry_open( struct inode *inode, struct file *file)
{
	int rc = 0;
	int size = 1;
	int len = 0;
	tempinfo_t *info;
	int irq;
	int devno;
	char * devno_str;

	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
	if (info == NULL) {
		printk( KERN_WARNING "No memory available for data\n");
		rc = -ENOMEM;
	} else {
		file->private_data = (void *) info;
		size += 2 * 32;
		info->data = (char *) vmalloc(size);
		if (size && info->data == NULL) {
			printk(KERN_WARNING "No memory available for data\n");
			vfree(info);
			rc = -ENOMEM;
		} else {
			devno_str = kmalloc(6*sizeof(char), GFP_KERNEL);
			memset(devno_str, 0, 6*sizeof(char));
			memcpy(devno_str,file->f_dentry->d_parent->d_name.name, strlen(file->f_dentry->d_parent->d_name.name)+1);
			devno = simple_strtoul(devno_str, &devno_str, 16);
			irq = get_irq_by_devno(devno);
			if (irq != -1) {
				len += sprintf(info->data+len, "Dev Type/Mod: ");
				if (ioinfo[irq]->senseid.dev_type == 0) {
					len += sprintf(info->data+len, "%04X/%02X\n",
						       ioinfo[irq]->senseid.cu_type,
						       ioinfo[irq]->senseid.cu_model);
				} else {
					len += sprintf(info->data+len, "%04X/%02X\n",
						       ioinfo[irq]->senseid.dev_type,
						       ioinfo[irq]->senseid.dev_model);
					len+= sprintf(info->data+len, "CU Type/Mod:  %04X/%02X\n",
						      ioinfo[irq]->senseid.cu_type,
						      ioinfo[irq]->senseid.cu_model);
				}
			}
			info->len = len;
		}
	}
	
	return rc;
}

static int cio_in_use_entry_open( struct inode *inode, struct file *file)
{
	int rc = 0;
	int size = 1;
	int len = 0;
	tempinfo_t *info;
	int irq;
	int devno;
	char * devno_str;

	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
	if (info == NULL) {
		printk( KERN_WARNING "No memory available for data\n");
		rc = -ENOMEM;
	} else {
		file->private_data = (void *) info;
		size += 8;
		info->data = (char *) vmalloc(size);
		if (size && info->data == NULL) {
			printk(KERN_WARNING "No memory available for data\n");
			vfree(info);
			rc = -ENOMEM;
		} else {
			devno_str = kmalloc(6*sizeof(char), GFP_KERNEL);
			memset(devno_str, 0, 6*sizeof(char));
			memcpy(devno_str,file->f_dentry->d_parent->d_name.name, strlen(file->f_dentry->d_parent->d_name.name)+1);
			devno = simple_strtoul(devno_str, &devno_str, 16);
			irq = get_irq_by_devno(devno);
			if (irq != -1) {
				len += sprintf(info->data+len, "%s\n", ioinfo[irq]->ui.flags.ready?"yes":"no");
			}
			info->len = len;
		}
	}
	
	return rc;
}

static int cio_chpid_entry_open( struct inode *inode, struct file *file)
{
	int rc = 0;
	int size = 1;
	int len = 0;
	tempinfo_t *info;
	int irq;
	int devno;
	int i;
	char * devno_str;

	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
	if (info == NULL) {
		printk( KERN_WARNING "No memory available for data\n");
		rc = -ENOMEM;
	} else {
		file->private_data = (void *) info;
		size += 8*16;
		info->data = (char *) vmalloc(size);
		if (size && info->data == NULL) {
			printk(KERN_WARNING "No memory available for data\n");
			vfree(info);
			rc = -ENOMEM;
		} else {
			devno_str = kmalloc(6*sizeof(char), GFP_KERNEL);
			memset(devno_str, 0, 6*sizeof(char));
			memcpy(devno_str,file->f_dentry->d_parent->d_name.name, strlen(file->f_dentry->d_parent->d_name.name)+1);
			devno = simple_strtoul(devno_str, &devno_str, 16);
			irq = get_irq_by_devno(devno);
			if (irq != -1) {
				for (i=0; i<8; i++) {
					len += sprintf(info->data+len, "CHPID[%d]: ", i);
					len += sprintf(info->data+len, "%02X\n", ioinfo[irq]->schib.pmcw.chpid[i]);
				}
			}
			info->len = len;
		}
	}

	return rc;
}

static struct file_operations cio_sensedata_entry_file_ops =
{
     read:cio_device_entry_read,
     open:cio_sensedata_entry_open,
     release:cio_device_entry_close,
};

static struct file_operations cio_in_use_entry_file_ops =
{
     read:cio_device_entry_read,
     open:cio_in_use_entry_open,
     release:cio_device_entry_close,
};

static struct file_operations cio_chpid_entry_file_ops =
{
     read:cio_device_entry_read,
     open:cio_chpid_entry_open,
     release:cio_device_entry_close,
};

/*
 * Function: cio_procfs_device_create
 * create procfs entry for given device number
 * and insert it into list
 */
int cio_procfs_device_create(int devno)
{
	cio_procfs_entry_t *entry;
	cio_procfs_device_t *tmp;
	cio_procfs_device_t *where;
	char buf[8];
	int i;
	int rc = 0;


	/* create the directory entry */
	entry = (cio_procfs_entry_t *)kmalloc(sizeof(cio_procfs_entry_t), GFP_KERNEL);
	if (entry) {
		entry->devno = devno;
		sprintf(buf, "%x", devno);
		entry->cio_device_entry = proc_mkdir(buf, cio_procfs_deviceinfo_root);
		
		if (entry->cio_device_entry) {
			tmp = (cio_procfs_device_t *)kmalloc(sizeof(cio_procfs_device_t), GFP_KERNEL);
			if (tmp) {
				tmp->entry = entry;
				
				if (cio_procfs_device_list == NULL) {
					cio_procfs_device_list = tmp;
					tmp->next = NULL;
				} else {
					where = cio_procfs_device_list;
					i = where->entry->devno;
					while ((devno>i) && (where->next != NULL)) {
						where = where->next;
						i = where->entry->devno;
					}
					if (where->next == NULL) {
						where->next = tmp;
						tmp->next = NULL;
					} else {
						tmp->next = where->next;
						where->next = tmp;
					}
				}
				/* create the different entries */
				entry->cio_sensedata_entry = create_proc_entry( "sensedata", S_IFREG|S_IRUGO, entry->cio_device_entry);
				entry->cio_sensedata_entry->proc_fops = &cio_sensedata_entry_file_ops;
				entry->cio_in_use_entry = create_proc_entry( "in_use", S_IFREG|S_IRUGO, entry->cio_device_entry);
				entry->cio_in_use_entry->proc_fops = &cio_in_use_entry_file_ops;
				entry->cio_chpid_entry = create_proc_entry( "chpids", S_IFREG|S_IRUGO, entry->cio_device_entry);
				entry->cio_chpid_entry->proc_fops = &cio_chpid_entry_file_ops;
			} else {
				printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
				remove_proc_entry(buf, cio_procfs_deviceinfo_root);
				kfree(entry);
				rc = -ENOMEM;
			}
		} else {
			printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
			kfree(entry);
			rc = -ENOMEM;
		}

	} else {
		printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
		rc = -ENOMEM;
	}
	return rc;
}

/*
 * Function: cio_procfs_device_remove
 * remove procfs entry for given device number
 */
int cio_procfs_device_remove(int devno)
{
	int rc = 0;
	cio_procfs_device_t *tmp;
	cio_procfs_device_t *prev = NULL;

	tmp=cio_procfs_device_list;
	while (tmp) {
		if (tmp->entry->devno == devno)
			break;
		prev = tmp;
		tmp = tmp->next;
	}

	if (tmp) {
		char buf[8];
		
		remove_proc_entry("sensedata", tmp->entry->cio_device_entry);
		remove_proc_entry("in_use", tmp->entry->cio_device_entry);
		remove_proc_entry("chpid", tmp->entry->cio_device_entry);
		sprintf(buf, "%x", devno);
		remove_proc_entry(buf, cio_procfs_deviceinfo_root);
		
		if (tmp == cio_procfs_device_list) {
			cio_procfs_device_list = tmp->next;
		} else {
			prev->next = tmp->next;
		}
		kfree(tmp->entry);
		kfree(tmp);
	} else {
		rc = -ENODEV;
	}

	return rc;
}

/*
 * Function: cio_procfs_purge
 * purge /proc/deviceinfo of entries for gone devices
 */

int cio_procfs_device_purge(void) 
{
	int i;

	for (i=0; i<=highest_subchannel; i++) {
		if (ioinfo[i] != INVALID_STORAGE_AREA) {
			if (!ioinfo[i]->ui.flags.oper) 
				cio_procfs_device_remove(ioinfo[i]->devno);
		}
	}

	return 0;
}

/*
 * Function: cio_procfs_create
 * create /proc/deviceinfo/ and subdirs for the devices
 */
static int cio_procfs_create( void )
{
	int irq;

	if (cio_proc_devinfo) {

		cio_procfs_deviceinfo_root = proc_mkdir( "deviceinfo", &proc_root);
		
		if (highest_subchannel >= MAX_CIO_PROCFS_ENTRIES) {
			printk(KERN_ALERT "Warning: Not enough inodes for creating all entries under /proc/deviceinfo/. "
			       "Not every device will get an entry.\n");
		}
		
		for (irq=0; irq<=highest_subchannel; irq++) {
			if (irq >= MAX_CIO_PROCFS_ENTRIES)
				break;
			if (ioinfo[irq] != INVALID_STORAGE_AREA) {
				if (ioinfo[irq]->ui.flags.oper) 
					if (cio_procfs_device_create(ioinfo[irq]->devno) == -ENOMEM) {
						printk(KERN_CRIT "Out of memory while creating entries in /proc/deviceinfo/, "
						       "not all devices might show up\n");
					break;
					}
			}
		}
	
	}
	
	return 1;
}

__initcall(cio_procfs_create);

/*
 * Entry /proc/cio_ignore to display blacklisted ranges of devices.
 * un-ignore devices by piping to /proc/cio_ignore:
 * free all frees all blacklisted devices, free <range>,<range>,...
 * frees specified ranges of devnos
 * add <range>,<range>,... will add a range of devices to blacklist -
 * but only for devices not already known
 */

static struct proc_dir_entry *cio_ignore_proc_entry;

static int cio_ignore_proc_open(struct inode *inode, struct file *file)
{
	int rc = 0;
	int size = 1;
	int len = 0;
	tempinfo_t *info;
	dev_blacklist_range_t *tmp;
	long flags;

	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
	if (info == NULL) {
		printk( KERN_WARNING "No memory available for data\n");
		rc = -ENOMEM;
	} else {
		file->private_data = (void *) info;
		size += nr_blacklisted_ranges * 32;
		info->data = (char *) vmalloc(size);
		if (size && info->data == NULL) {
			printk( KERN_WARNING "No memory available for data\n");
			vfree (info);
			rc = -ENOMEM;
		} else {
			spin_lock_irqsave( &blacklist_lock, flags ); 
			tmp = dev_blacklist_range_head;
			while (tmp) {
				len += sprintf(info->data+len, "%04x ", tmp->from);
				if (tmp->to != tmp->from) 
					len += sprintf(info->data+len, "- %04x", tmp->to);
				len += sprintf(info->data+len, "\n");
				tmp = tmp->next;
			}
			spin_unlock_irqrestore( &blacklist_lock, flags );
			info->len = len;
		}
	}
	return rc;
}

static int cio_ignore_proc_close(struct inode *inode, struct file *file)
{
	int rc = 0;
	tempinfo_t *p_info = (tempinfo_t *) file->private_data;

     if (p_info) {
	  if (p_info->data)
	       vfree( p_info->data );
	  vfree( p_info );
     }
     
     return rc;
}

static ssize_t cio_ignore_proc_read( struct file *file, char *user_buf, size_t user_len, loff_t * offset)
{
     loff_t len;
     tempinfo_t *p_info = (tempinfo_t *) file->private_data;
     
     if ( *offset>=p_info->len) {
	  return 0;
     } else {
	  len = MIN(user_len, (p_info->len - *offset));
	  if (copy_to_user( user_buf, &(p_info->data[*offset]), len))
	       return -EFAULT; 
	  (* offset) += len;
	  return len;
     }
}

static ssize_t cio_ignore_proc_write (struct file *file, const char *user_buf,
				      size_t user_len, loff_t * offset)
{
	char *buffer = vmalloc (user_len);

	if (buffer == NULL)
		return -ENOMEM;
	if (copy_from_user (buffer, user_buf, user_len)) {
		vfree (buffer);
		return -EFAULT;
	}
	buffer[user_len]='\0';
#ifdef CIO_DEBUG_IO
	printk ( KERN_DEBUG "/proc/cio_ignore: '%s'\n", buffer);
#endif /* CIO_DEBUG_IO */
	if (cio_debug_initialized)
		debug_sprintf_event(cio_debug_msg_id, 2, "/proc/cio_ignore: '%s'\n",buffer);

	blacklist_parse_proc_parameters(buffer);

	return user_len;
}

static struct file_operations cio_ignore_proc_file_ops =
{
	read:cio_ignore_proc_read,
	open:cio_ignore_proc_open,
	write:cio_ignore_proc_write,
	release:cio_ignore_proc_close,
};

static int cio_ignore_proc_init(void)
{
	cio_ignore_proc_entry = create_proc_entry("cio_ignore", S_IFREG|S_IRUGO|S_IWUSR, &proc_root);
	cio_ignore_proc_entry->proc_fops = &cio_ignore_proc_file_ops;

	return 1;
}

__initcall(cio_ignore_proc_init);

/*
 * Entry /proc/irq_count
 * display how many irqs have occured per cpu...
 */

static struct proc_dir_entry *cio_irq_proc_entry;

static int cio_irq_proc_open(struct inode *inode, struct file *file)
{
	int rc = 0;
	int size = 1;
	int len = 0;
	tempinfo_t *info;
	int i;

	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
	if (info == NULL) {
		printk( KERN_WARNING "No memory available for data\n");
		rc = -ENOMEM;
	} else {
		file->private_data = (void *) info;
		size += NR_CPUS * 16;
		info->data = (char *) vmalloc(size);
		if (size && info->data == NULL) {
			printk( KERN_WARNING "No memory available for data\n");
			vfree (info);
			rc = -ENOMEM;
		} else {
			for (i=0; i< NR_CPUS; i++) {
				if (s390_irq_count[i] != 0) 
					len += sprintf(info->data+len, "%lx\n", s390_irq_count[i]);
			}
			info->len = len;
		}
	}
	return rc;
}

static int cio_irq_proc_close(struct inode *inode, struct file *file)
{
	int rc = 0;
	tempinfo_t *p_info = (tempinfo_t *) file->private_data;

	if (p_info) {
		if (p_info->data) 
			vfree(p_info->data);
		vfree(p_info);
	}

	return rc;
}

static ssize_t cio_irq_proc_read( struct file *file, char *user_buf, size_t user_len, loff_t * offset)
{
	loff_t len;
	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
	
	if ( *offset>=p_info->len) {
		return 0;
	} else {
		len = MIN(user_len, (p_info->len - *offset));
		if (copy_to_user( user_buf, &(p_info->data[*offset]), len))
			return -EFAULT; 
		(* offset) += len;
		return len;
	}
}

static struct file_operations cio_irq_proc_file_ops = 
	{
		read:    cio_irq_proc_read,
		open:    cio_irq_proc_open,
		release: cio_irq_proc_close,
	};

static int cio_irq_proc_init(void)
{

	int i;

	if (cio_count_irqs) {
		for (i=0; i<NR_CPUS; i++) 
			s390_irq_count[i]=0;
		cio_irq_proc_entry = create_proc_entry("irq_count", S_IFREG|S_IRUGO, &proc_root);
		cio_irq_proc_entry->proc_fops = &cio_irq_proc_file_ops;
	}

	return 1;
}

__initcall(cio_irq_proc_init);	

/* end of procfs stuff */
#endif

schib_t *s390_get_schib( int irq )
{
	if ( (irq > highest_subchannel) || (irq < 0) )
		return NULL;
	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
		return NULL;
	return &ioinfo[irq]->schib;

}


EXPORT_SYMBOL(halt_IO);
EXPORT_SYMBOL(clear_IO);
EXPORT_SYMBOL(do_IO);
EXPORT_SYMBOL(resume_IO);
EXPORT_SYMBOL(ioinfo);
EXPORT_SYMBOL(get_dev_info_by_irq);
EXPORT_SYMBOL(get_dev_info_by_devno);
EXPORT_SYMBOL(get_irq_by_devno);
EXPORT_SYMBOL(get_devno_by_irq);
EXPORT_SYMBOL(get_irq_first);
EXPORT_SYMBOL(get_irq_next);
EXPORT_SYMBOL(read_conf_data);
EXPORT_SYMBOL(read_dev_chars);
EXPORT_SYMBOL(s390_request_irq_special);
EXPORT_SYMBOL(s390_get_schib);
EXPORT_SYMBOL(s390_register_adapter_interrupt);
EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
