patch-2.2.16 linux/arch/s390/kernel/s390io.c

Next file: linux/arch/s390/kernel/s390mach.c
Previous file: linux/arch/s390/kernel/s390dyn.c
Back to the patch index
Back to the overall index

diff -urN v2.2.15/linux/arch/s390/kernel/s390io.c linux/arch/s390/kernel/s390io.c
@@ -4,7 +4,7 @@
  *
  *  S390 version
  *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
-                               IBM Corporation
+ *                             IBM Corporation
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  */
 
@@ -27,26 +27,34 @@
 #include <asm/smp.h>
 #include <asm/pgtable.h>
 #include <asm/delay.h>
+#include <asm/processor.h>
 #include <asm/lowcore.h>
 #include <asm/s390io.h>
+#include <asm/s390dyn.h>
+#include <asm/s390mach.h>
 
 #undef CONFIG_DEBUG_IO    
+#define CONFIG_DEBUG_CRW
 
 #define REIPL_DEVID_MAGIC 0x87654321
 
-struct irqaction  init_IRQ_action;
+struct s390_irqaction  init_IRQ_action;
 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
 };
-spinlock_t        sync_isc;                 // synchronous irq processing lock
-psw_t             io_sync_wait;             // wait PSW for sync IO, prot. by sync_isc
-psw_t             io_new_psw;               // save I/O new PSW, prot. by sync_isc
-int               cons_dev          = -1;   // identify console device
-int               init_IRQ_complete = 0;
-schib_t           init_schib;
+
+static spinlock_t sync_isc = SPIN_LOCK_UNLOCKED;
+                                          // synchronous irq processing lock
+static psw_t      io_sync_wait;           // wait PSW for sync IO, prot. by sync_isc
+static psw_t      io_new_psw;             // save I/O new PSW, prot. by sync_isc
+static int        cons_dev          = -1; // identify console device
+static int        init_IRQ_complete = 0;
+static schib_t    init_schib;
+static irb_t      init_irb;
+static __u64      irq_IPL_TOD;
 
 /*
  * Dummy controller type for unused interrupts
@@ -63,12 +71,18 @@
 };
 
 static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs);
-static int  s390_setup_irq(unsigned int irq, struct irqaction * new);
+static int  s390_setup_irq(unsigned int irq, struct s390_irqaction * new);
 static void s390_process_subchannels( void);
-static void s390_device_recognition( void);
-static int  s390_validate_subchannel( int irq);
-static int  s390_SenseID( int irq, senseid_t *sid);
+static void s390_device_recognition_all( void);
+static void s390_device_recognition_irq( int irq);
+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  disable_subchannel( unsigned int irq);
+
+int  s390_DevicePathVerification( int irq, __u8 domask );
 
 extern int do_none(unsigned int irq, int cpu, struct pt_regs * regs);
 extern int enable_none(unsigned int irq);
@@ -83,6 +97,7 @@
 // fix me ! must be removed with 2.3.x and follow-up releases
 //
 static void * alloc_bootmem( unsigned long size);
+static int    free_bootmem( unsigned long buffer, unsigned long size);
 static unsigned long memory_start = 0;
 
 void s390_displayhex(char *str,void *ptr,s32 cnt);
@@ -90,13 +105,13 @@
 void s390_displayhex(char *str,void *ptr,s32 cnt)
 {
 	s32	cnt1,cnt2,maxcnt2;
-	u32	*currptr=(u32 *)ptr;
+	u32	*currptr=(__u32 *)ptr;
 
 	printk("\n%s\n",str);
 
 	for(cnt1=0;cnt1<cnt;cnt1+=16)
 	{
-		printk("%08X ",(u32)currptr);
+		printk("%08X ",(__u32)currptr);
 		maxcnt2=cnt-cnt1;
 		if(maxcnt2>16)
 			maxcnt2=16;
@@ -106,19 +121,21 @@
 	}
 }
 
-int s390_request_irq( unsigned int   irq,
-                      void           (*handler)(int, void *, struct pt_regs *),
+
+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;
-	struct irqaction *action;
+	struct s390_irqaction *action;
 
 	if (irq >= __MAX_SUBCHANNELS)
 		return -EINVAL;
 
-	if ( !handler || !dev_id )
+	if ( !io_handler || !dev_id )
 		return -EINVAL;
 
    /*
@@ -128,8 +145,9 @@
     */
    if ( init_IRQ_complete )
    {
-	   action = (struct irqaction *)
-		         kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+		action = (struct s390_irqaction *)
+		            kmalloc( sizeof(struct s390_irqaction),
+		                     GFP_KERNEL);
    }
    else
    {
@@ -143,27 +161,64 @@
 
    } /* endif */
 
-	action->handler = handler;
+	action->handler = io_handler;
 	action->flags   = irqflags;
-	action->mask    = 0;
 	action->name    = devname;
-	action->next    = NULL;
 	action->dev_id  = dev_id;
 
 	retval = s390_setup_irq(irq, action);
 
-	if ( retval && init_IRQ_complete )
+	if ( init_IRQ_complete )
+	{
+		if ( !retval )
+		{
+			s390_DevicePathVerification( irq, 0 );
+		}
+		else
 	{
 		kfree(action);
 
 	} /* endif */
 
+	} /* endif */
+
+	if ( retval == 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 int flags;
+	unsigned long flags;
 	int          ret;
 
 	unsigned int count = 0;
@@ -198,11 +253,59 @@
 
 			do
          {
-				ret = ioinfo[irq]->irq_desc.handler->disable(irq);
+				ret = disable_subchannel( irq);
 
 				count++;
 
-				if ( count == 3 )
+				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);
+							tod_wait( 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);
+							tod_wait( 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 "
@@ -212,6 +315,12 @@
 
 				} /* endif */
 
+						break; /* sigh, let's give up ... */
+
+					} /* endif */
+
+				} /* endif */
+
 			} while ( ret == -EBUSY );
 
 			if ( init_IRQ_complete )
@@ -219,12 +328,12 @@
 
 			ioinfo[irq]->irq_desc.action  = NULL;
 			ioinfo[irq]->ui.flags.ready   = 0;
-
-			ioinfo[irq]->irq_desc.handler->enable  = &enable_none;
-			ioinfo[irq]->irq_desc.handler->disable = &disable_none;
-
+			ioinfo[irq]->irq_desc.handler->enable  = enable_none;
+			ioinfo[irq]->irq_desc.handler->disable = disable_none;
 			ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */
 
+			ioinfo[irq]->nopfunc = NULL;
+
 			s390irq_spin_unlock_irqrestore( irq, flags);
 		}
 		else
@@ -329,6 +438,16 @@
       {
 			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) );
@@ -349,9 +468,7 @@
 	   			 */
 
 					ioinfo[irq]->ui.flags.s_pend = 1;
-
 					s390_process_IRQ( irq );
-
 					ioinfo[irq]->ui.flags.s_pend = 0;
 
 					ret = -EIO;    /* might be overwritten */
@@ -360,6 +477,12 @@
                retry--;
                break;
 
+				case 2:
+					tod_wait(100);	/* allow for recovery */
+					ret = -EBUSY;
+					retry--;
+					break;
+
             case 3:
 					ioinfo[irq]->ui.flags.oper = 0;
                ret = -ENODEV;
@@ -452,7 +575,7 @@
 					s390_process_IRQ( irq );
 					ioinfo[irq]->ui.flags.s_pend = 0;
 
-					ret = -EBUSY;  /* might be overwritten  */
+					ret = -EIO; /* might be overwritten  */
 					               /* ... on re-driving the */
 					               /* ... msch() call       */
                retry--;
@@ -471,7 +594,7 @@
 							  "device %04X received !\n",
 					        irq,
 					        ioinfo[irq]->devstat.devno);
-               ret = -ENODEV; // never reached
+					ret = -EBUSY;
 				   break;
 
    			case 3 :
@@ -502,8 +625,7 @@
 }
 
 
-
-int s390_setup_irq(unsigned int irq, struct irqaction * new)
+int s390_setup_irq( unsigned int irq, struct s390_irqaction * new)
 {
 	unsigned long      flags;
 	int                rc = 0;
@@ -522,9 +644,9 @@
 	{
 		ioinfo[irq]->irq_desc.action           = new;
 		ioinfo[irq]->irq_desc.status           = 0;
-		ioinfo[irq]->irq_desc.handler->enable  = &enable_subchannel;
-		ioinfo[irq]->irq_desc.handler->disable = &disable_subchannel;
-		ioinfo[irq]->irq_desc.handler->handle  = &handle_IRQ_event;
+		ioinfo[irq]->irq_desc.handler->enable  = enable_subchannel;
+		ioinfo[irq]->irq_desc.handler->disable = disable_subchannel;
+		ioinfo[irq]->irq_desc.handler->handle  = handle_IRQ_event;
 
 		ioinfo[irq]->ui.flags.ready            = 1;
 
@@ -559,6 +681,22 @@
    return( ret );
 }
 
+int free_bootmem( unsigned long buffer, unsigned long size)
+{
+   int ret = 0;
+
+	/*
+	 *	We don't have buffer management, thus a free
+	 *  must follow the matching alloc.
+	 */	
+	if ( buffer == (memory_start - size) )
+   	memory_start -= size;
+	else
+		ret = -EINVAL;
+
+   return( ret );
+}
+
 unsigned long s390_init_IRQ( unsigned long memstart)
 {
 	unsigned long flags;     /* PSW flags */
@@ -571,6 +709,8 @@
 	atomic_set(&S390_lowcore.local_bh_count,0);
 	atomic_set(&S390_lowcore.local_irq_count,0);
 
+	asm volatile ("STCK %0" : "=m" (irq_IPL_TOD));
+
 	/*
 	 * As we don't know about the calling environment
 	 *  we assure running disabled. Before leaving the
@@ -597,7 +737,7 @@
 	cr6 = 0x10000000;
 	asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
 
-   s390_device_recognition();
+	s390_device_recognition_all();
 
    init_IRQ_complete = 1;
 
@@ -631,22 +771,21 @@
 	/*
 	 * The flag usage is mutal exclusive ...
 	 */
-	if (    (flag & DOIO_RETURN_CHAN_END)
+	if (    (flag & DOIO_EARLY_NOTIFICATION)
 	     && (flag & DOIO_REPORT_ALL     ) )
 	{
 		return( -EINVAL );
 
 	} /* endif */
 
-	memset( &(ioinfo[irq]->orb), '\0', sizeof( orb_t) );
-
 	/*
 	 * setup ORB
 	 */
+  	ioinfo[irq]->orb.intparm = (__u32)&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);
+	ioinfo[irq]->orb.spnd =  (flag & DOIO_ALLOW_SUSPEND ? TRUE : FALSE);
 	ioinfo[irq]->orb.ssic =  (    (flag & DOIO_ALLOW_SUSPEND )
 	                          && (flag & DOIO_SUPPRESS_INTER) );
 
@@ -656,7 +795,7 @@
 	}
 	else
 	{
-		ioinfo[irq]->orb.lpm = ioinfo[irq]->schib.pmcw.pam;
+		ioinfo[irq]->orb.lpm = ioinfo[irq]->opm;
 
 	} /* endif */
 
@@ -694,6 +833,12 @@
 
 	} /* endif */
 
+	if ( flag & DOIO_DONT_CALL_INTHDLR )
+	{
+		ioinfo[irq]->ui.flags.repnone = 1;
+
+   } /* endif */
+
 	/*
 	 * Issue "Start subchannel" and process condition code
 	 */
@@ -713,6 +858,10 @@
 				'\0', sizeof( irb_t) );
 		} /* endif */
 
+		memset( &ioinfo[irq]->devstat.ii.irb,
+		        '\0',
+		        sizeof( irb_t) );
+
 		/*
 		 * initialize device status information
 		 */
@@ -734,7 +883,7 @@
 		 *  or if we are to return all interrupt info.
 		 * Default is to call IRQ handler at secondary status only
 		 */
-		if ( flag & DOIO_RETURN_CHAN_END )
+		if ( flag & DOIO_EARLY_NOTIFICATION )
 		{
 			ioinfo[irq]->ui.flags.fast = 1;
 		}
@@ -744,15 +893,7 @@
 
 		} /* endif */
 
-		if ( flag & DOIO_VALID_LPM )
-		{
-			ioinfo[irq]->lpm = lpm;         /* specific path */
-		}
-		else
-		{
-			ioinfo[irq]->lpm = 0xff;        /* any path */
-
-		} /* endif */
+		ioinfo[irq]->ulpm = ioinfo[irq]->orb.lpm;
 
 		/*
 		 * If synchronous I/O processing is requested, we have
@@ -764,13 +905,16 @@
 		 */
 		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
 		{
-			int              io_sub;
+			int              io_sub = -1;
 			__u32            io_parm;
          psw_t            io_new_psw;
          int              ccode;
+			uint64_t         time_start;    	
+			uint64_t         time_curr;    	
 
 			int              ready = 0;
          struct _lowcore *lc    = NULL;
+			int              do_retry = 1;
 
          /*
 			 * We shouldn't perform a TPI loop, waiting for an
@@ -812,8 +956,7 @@
 					break;
 			} /* endswitch */
 
-			io_sync_wait.addr =   (unsigned long) &&io_wakeup
-					    | 0x80000000L;
+			io_sync_wait.addr = FIX_PSW(&&io_wakeup);
 
 			/*
 			 * Martin didn't like modifying the new PSW, now we take
@@ -821,27 +964,67 @@
 			 */
 			*(__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;
+
 			do
 			{
+						if ( tpi(&tpi_info) == 1 )
+						{
+							io_sub = tpi_info.irq;
+							break;
+						}
+						else
+						{
+							tod_wait(100); /* usecs */
+							asm volatile ("STCK %0" : "=m" (time_curr));
 
+							if ( ((time_curr >> 32) - time_start ) >= 3 )
+								do_retry = 0;          							
+
+						} /* endif */
+
+					} while ( do_retry );
+				}
+				else
+				{
       		asm volatile ( "lpsw %0" : : "m" (io_sync_wait) );
+
 io_wakeup:
-				io_parm = *(__u32 *)__LC_IO_INT_PARM;
 				io_sub  = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR;
 
+				} /* endif */
+
+ 				if ( do_retry )
 				ready = s390_process_IRQ( io_sub );
 
-			} while ( !((io_sub == irq) && (ready == 1)) );
+				/*
+				 * 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_STATUS_PENDING;
+		ioinfo[irq]->devstat.flag =   DEVSTAT_START_FUNCTION
+		                            | DEVSTAT_STATUS_PENDING;
 
 		/*
 		 * initialize the device driver specific devstat irb area
@@ -866,6 +1049,7 @@
 		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;
 
@@ -880,11 +1064,30 @@
 		 */
 		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]->devstat.flag  |= DEVSTAT_NOT_OPER;
 			ioinfo[irq]->ui.flags.oper  = 0;
+         }
+			else
+			{
+				ret = -EIO;
+
+         } /* endif */
 
-#if CONFIG_DEBUG_IO
+			ioinfo[irq]->devstat.flag  |= DEVSTAT_NOT_OPER;
+
+#ifdef CONFIG_DEBUG_IO
       	{
 	      char buffer[80];
 
@@ -921,7 +1124,7 @@
 		((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->ii.sense.data,
 		((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->rescnt);
 
-         }
+			} /* endif */
       	}
 #endif
 		}
@@ -940,18 +1143,32 @@
 		ret = -EBUSY;
 		break;
 
-	default:            /* device not operational */
+	default:            /* device/path not operational */
 
-		ret                          = -ENODEV;
-		ioinfo[irq]->ui.flags.oper    = 0;
+		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.action->dev_id,
 			&(ioinfo[irq]->devstat),
 			sizeof( devstat_t) );
 
-#if CONFIG_DEBUG_IO
+#ifdef CONFIG_DEBUG_IO
       	{
 	      char buffer[80];
 
@@ -971,16 +1188,40 @@
 
 	} /* endswitch */
 
-	if (    ( flag & DOIO_WAIT_FOR_INTERRUPT )   	
-	     && ( sync_isc_locked                ) )
+	if ( sync_isc_locked )
 	{
-		disable_cpu_sync_isc( irq );
+		int iret;
+		int retry = 5;
+		int halt  = 0;	
 
-		spin_unlock_irqrestore( &sync_isc, psw_flags);
+		do
+		{
+			iret = disable_cpu_sync_isc( irq );
+			retry--;
+
+			/* try stopping it ... */
+			if ( (iret == -EBUSY) && !halt )
+			{
+				halt_IO( irq, 0x00004711, 0 );
+				halt = 1;
+
+			} /* endif */
+
+			tod_wait( 100);
+
+		} while ( retry && (iret == -EBUSY ) );
 
 		sync_isc_locked             = 0;    // local setting
 		ioinfo[irq]->ui.flags.syncio = 0;    // global setting
 
+		spin_unlock_irqrestore( &sync_isc, psw_flags);
+
+	} /* endif */
+
+	if ( flag & DOIO_DONT_CALL_INTHDLR )
+	{
+		ioinfo[irq]->ui.flags.repnone = 0;
+
 	} /* endif */
 
 	return( ret);
@@ -1005,8 +1246,8 @@
 		return( -ENODEV);
    }
 
-	/* handler registered ?    */
-	if ( !ioinfo[irq]->ui.flags.ready )
+	/* handler registered ? or free_irq() in process already ? */
+	if ( !ioinfo[irq]->ui.flags.ready || ioinfo[irq]->ui.flags.unready )
 	{
 		return( -ENODEV );
 
@@ -1125,7 +1366,7 @@
  */
 int halt_IO( int          irq,
              unsigned long user_intparm,
-             unsigned int flag)  /* possible DOIO_WAIT_FOR_INTERRUPT */
+             unsigned long flag)  /* possible DOIO_WAIT_FOR_INTERRUPT */
 {
 	int            ret;
 	int            ccode;
@@ -1161,7 +1402,8 @@
 	/*
 	 * We don't allow for halt_io with a sync do_IO() requests pending.
 	 */
-	else if ( ioinfo[irq]->ui.flags.syncio )
+	else if (    ioinfo[irq]->ui.flags.syncio
+	          && (flag & DOIO_WAIT_FOR_INTERRUPT))
 	{
 		ret = -EBUSY;
 	}
@@ -1289,8 +1531,7 @@
 						break;
 				} /* endswitch */
 
-				io_sync_wait.addr =   (unsigned long)&&hio_wakeup
-						    | 0x80000000L;
+				io_sync_wait.addr = FIX_PSW(&&hio_wakeup);
 
 				/*
 				 * Martin didn't like modifying the new PSW, now we take
@@ -1385,14 +1626,13 @@
 
 		} /* endswitch */
 
-		if (    ( flag & DOIO_WAIT_FOR_INTERRUPT )
-	   	  && ( sync_isc_locked                ) )
+		if ( sync_isc_locked )
 		{
+			disable_cpu_sync_isc( irq );
+  	
 			sync_isc_locked             = 0;    // local setting
 		   ioinfo[irq]->ui.flags.syncio = 0;    // global setting
   	
-			disable_cpu_sync_isc( irq );
-  	
 			spin_unlock_irqrestore( &sync_isc, psw_flags);
   	
 		} /* endif */
@@ -1402,1382 +1642,2755 @@
 	return( ret );
 }
 
-
 /*
- * do_IRQ() handles all normal I/O device IRQ's (the special
- *          SMP cross-CPU interrupts have their own specific
- *          handlers).
- *
- * Returns: 0 - no ending status received, no further action taken
- *          1 - interrupt handler was called with ending status
+ * 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.
  */
-asmlinkage void do_IRQ( struct pt_regs regs,
-                        unsigned int   irq,
-                        __u32          s390_intparm )
+int clear_IO( int           irq,
+              unsigned long user_intparm,
+              unsigned long flag)  /* possible DOIO_WAIT_FOR_INTERRUPT */
 {
-#ifdef CONFIG_FAST_IRQ
+	int            ret;
 	int			ccode;
-	tpi_info_t 	tpi_info;
-	int			new_irq;
-#endif
-	int			use_irq     = irq;
-//	__u32       use_intparm = s390_intparm;
+	unsigned long  psw_flags;
+
+	int            sync_isc_locked = 0;
+
+	if ( irq > highest_subchannel || irq < 0 )
+	{
+		ret = -ENODEV;
+	}
 
-	//
-	// fix me !!!
-	//
-	// We need to schedule device recognition, the interrupt stays
-	//  pending. We need to dynamically allocate an ioinfo structure.
-	//
 	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 	{
-		return;
+		return( -ENODEV);
 	}
 
 	/*
-	 * 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.
+	 * we only allow for halt_IO if the device has an I/O handler associated
 	 */
- 	if (    *(__u32 *)__LC_SYNC_IO_WORD )
+	else if ( !ioinfo[irq]->ui.flags.ready )
 	{
-		regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT);
-
-      return;
-
-	} /* endif */
-
-	s390irq_spin_lock(use_irq);
-
-#ifdef CONFIG_FAST_IRQ
-	do {
-#endif /*  CONFIG_FAST_IRQ */
-
-		s390_process_IRQ( use_irq );
-
-#ifdef CONFIG_FAST_IRQ
-
+		ret = -ENODEV;
+	}
 		/*
-		 * more interrupts pending ?
+	 * we ignore the halt_io() request if ending_status was received but
+	 *  a SENSE operation is waiting for completion.
 		 */
-		ccode = tpi( &tpi_info );
-
-		if ( ! ccode )
-			break;  	// no, leave ...
-
-		new_irq     = tpi_info.irq;
-//		use_intparm = tpi_info.intparm;
-
+	else if ( ioinfo[irq]->ui.flags.w4sense )
+	{
+		ret = 0;
+	}
 		/*
-		 * if the interrupt is for a different irq we
-		 *  release the current irq lock and obtain
-		 *  a new one ...
+	 * We don't allow for halt_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.
 		 */
-		if ( new_irq != use_irq )
+	else if ( ioinfo[irq]->ui.flags.syncio )
       {
-			s390irq_spin_unlock(use_irq);
-         use_irq = new_irq;
-			s390irq_spin_lock(use_irq);
-
-      } /* endif */
-
-	} while ( 1 );
-
-#endif /*  CONFIG_FAST_IRQ */
-
-	s390irq_spin_unlock(use_irq);
-
-	return;
+		ret = -EBUSY;
 }
-
+	else
+	{
 /*
- * 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
+		 * 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.
  */
-int s390_process_IRQ( unsigned int irq )
+		if ( flag & DOIO_WAIT_FOR_INTERRUPT )
 {
-	int               ccode;      /* condition code from tsch() operation */
-	int               irb_cc;     /* condition code from irb */
-	int               sdevstat;   /* effective struct devstat size to copy */
-	unsigned int      fctl;       /* function control */
-	unsigned int      stctl;      /* status   control */
-	unsigned int      actl;       /* activity control */
-	struct irqaction *action;
-	struct pt_regs    regs;       /* for interface compatibility only */
-
-	int               issense         = 0;
-	int               ending_status   = 0;
-	int               allow4handler   = 1;
-	int               chnchk          = 0;
-#if 0
-	int               cpu             = smp_processor_id();
+			//
+			// check whether we run recursively (sense processing)
+			//
+			if ( !ioinfo[irq]->ui.flags.syncio )
+			{
+				spin_lock_irqsave( &sync_isc, psw_flags);
 
-	kstat.irqs[cpu][irq]++;
-#endif
-	action = ioinfo[irq]->irq_desc.action;
+				ret = enable_cpu_sync_isc( irq);
 
-	/*
-	 * 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 ( action == NULL )
+				if ( ret )
 	{
-		if ( !ioinfo[irq]->ui.flags.d_disable )
+					spin_unlock_irqrestore( &sync_isc,
+					                        psw_flags);
+					return( ret);
+				}
+				else
 		{
-			printk( KERN_CRIT"s390_process_IRQ(%04X) "
-			        "- no interrupt handler registered"
-					  "for device %04X !\n",
-			        irq,
-			        ioinfo[irq]->devstat.devno);
+					sync_isc_locked              = 1; // local
+					ioinfo[irq]->ui.flags.syncio = 1; // global
+
+				} /* endif */  	
 
 		} /* 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.
+		 * Issue "Halt subchannel" and process condition code
 	 */
-	ccode = tsch( irq, &(ioinfo[irq]->devstat.ii.irb) );
+		ccode = csch( irq );
 
-	//
-	// We must only accumulate the status if initiated by do_IO() or halt_IO()
-	//
-	if ( ioinfo[irq]->ui.flags.busy )
+		switch ( ccode ) {
+		case 0:
+
+			ioinfo[irq]->ui.flags.haltio = 1;
+
+			if ( !ioinfo[irq]->ui.flags.doio )
 	{
-		ioinfo[irq]->devstat.dstat |= ioinfo[irq]->devstat.ii.irb.scsw.dstat;
-		ioinfo[irq]->devstat.cstat |= ioinfo[irq]->devstat.ii.irb.scsw.cstat;
+				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.dstat  = ioinfo[irq]->devstat.ii.irb.scsw.dstat;
-		ioinfo[irq]->devstat.cstat  = ioinfo[irq]->devstat.ii.irb.scsw.cstat;
-
-		ioinfo[irq]->devstat.flag   = 0;   // reset status flags
+				ioinfo[irq]->devstat.flag   |= DEVSTAT_CLEAR_FUNCTION;
 
 	} /* endif */
 
-	ioinfo[irq]->devstat.lpum = ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum;
-
-	if ( ioinfo[irq]->ui.flags.busy)
+			/*
+			 * 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 )
 	{
-		ioinfo[irq]->devstat.intparm  = ioinfo[irq]->u_intparm;
+				int              io_sub;
+				__u32            io_parm;
+				psw_t            io_new_psw;
+				int              ccode;
 
-	} /* endif */
+				int              ready = 0;
+				struct _lowcore *lc    = NULL;
 
 	/*
-	 * reset device-busy bit if no longer set in irb
+				 * 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.
 	 */
-	if (   (ioinfo[irq]->devstat.dstat & DEV_STAT_BUSY                   )
-	    && ((ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0))
-	{
-		ioinfo[irq]->devstat.dstat &= ~DEV_STAT_BUSY;
+				memcpy( &io_new_psw,
+				        &lc->io_new_psw,
+				        sizeof(psw_t));
 
-	} /* endif */
+				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(&&cio_wakeup);
 
 	/*
-	 * Save residual count and CCW information in case primary and
-	 *  secondary status are presented with different interrupts.
+				 * Martin didn't like modifying the new PSW, now we take
+				 *  a fast exit in do_IRQ() instead
 	 */
-	if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS )
-	{
-		ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count;
-
-#if CONFIG_DEBUG_IO
-      if ( irq != cons_dev )
-         printk( "s390_process_IRQ( %04X ) : "
-                 "residual count from irb after tsch() %d\n",
-                 irq, ioinfo[irq]->devstat.rescnt );
-#endif
-	} /* endif */
+				*(__u32 *)__LC_SYNC_IO_WORD  = 1;
 
-	if ( ioinfo[irq]->devstat.ii.irb.scsw.cpa != 0 )
+				do
 	{
-		ioinfo[irq]->devstat.cpa = ioinfo[irq]->devstat.ii.irb.scsw.cpa;
 
-	} /* endif */
+	      		asm volatile ( "lpsw %0" : : "m" (io_sync_wait) );
+cio_wakeup:
+				   io_parm = *(__u32 *)__LC_IO_INT_PARM;
+				   io_sub  = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR;
 
-	irb_cc = ioinfo[irq]->devstat.ii.irb.scsw.cc;
+				   ready = s390_process_IRQ( io_sub );
 
-	//
-	// check for any kind of channel or interface control check but don't
-	//  issue the message for the console device
-	//
-	if (    (ioinfo[irq]->devstat.ii.irb.scsw.cstat
-	            & (  SCHN_STAT_CHN_DATA_CHK
-	               | SCHN_STAT_CHN_CTRL_CHK
-	               | SCHN_STAT_INTF_CTRL_CHK )       )
-	     && (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,
-		        ioinfo[irq]->devstat.dstat,
-		        ioinfo[irq]->devstat.cstat);
+				} while ( !((io_sub == irq) && (ready == 1)) );
 
-		chnchk = 1;
+				*(__u32 *)__LC_SYNC_IO_WORD = 0;
 
 	} /* endif */
 
-	issense = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.cons;
-
-	if ( issense )
-	{
-		ioinfo[irq]->devstat.scnt  =
-		             ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.scnt;
-		ioinfo[irq]->devstat.flag |=
-		             DEVSTAT_FLAG_SENSE_AVAIL;
+			ret = 0;
+			break;
                   	
-		sdevstat = sizeof( devstat_t);
-
-#if CONFIG_DEBUG_IO
-      if ( irq != cons_dev )
-         printk( "s390_process_IRQ( %04X ) : "
-                 "concurrent sense bytes avail %d\n",
-                 irq, ioinfo[irq]->devstat.scnt );
-#endif
-	}
-	else
-	{
-		/* don't copy the sense data area ! */
-		sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT;
-
-	} /* endif */
+		case 1 :            /* status pending */
 
-	switch ( irb_cc ) {
-	case 1:      /* status pending */
+			ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING;
 
-		ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING;
+			/*
+			 * initialize the device driver specific devstat irb area
+			 */
+			memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb,
+			        '\0', sizeof( irb_t) );
 
-	case 0:      /* normal i/o interruption */
+			/*
+			 * 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;
 
-		fctl  = ioinfo[irq]->devstat.ii.irb.scsw.fctl;
-		stctl = ioinfo[irq]->devstat.ii.irb.scsw.stctl;
-		actl  = ioinfo[irq]->devstat.ii.irb.scsw.actl;
+			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;
 
-		if ( chnchk && (ioinfo[irq]->senseid.cu_type == 0x3088))
+			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 ( sync_isc_locked )
 		{
-			char buffer[80];
+			disable_cpu_sync_isc( irq );
    	
-			sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
-			         "device %04X after channel check\n",
-			         irq,
-			         ioinfo[irq]->devstat.devno );
+			sync_isc_locked              = 0;    // local setting
+			ioinfo[irq]->ui.flags.syncio = 0;    // global setting
+  	
+			spin_unlock_irqrestore( &sync_isc, psw_flags);
 
-			s390_displayhex( buffer,
-			                 &(ioinfo[irq]->devstat.ii.irb) ,
-			                 sizeof(irb_t));
 		} /* endif */
 			
-		ioinfo[irq]->stctl |= stctl;
+	} /* endif */
+
+	return( ret );
+}
 
-		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) );
 
 		/*
-		 * Check for unsolicited interrupts - for debug purposes only
+ * do_IRQ() handles all normal I/O device IRQ's (the special
+ *          SMP cross-CPU interrupts have their own specific
+ *          handlers).
 		 *
-		 * 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.
+ * Returns: 0 - no ending status received, no further action taken
+ *          1 - interrupt handler was called with ending status
+		 */
+asmlinkage void do_IRQ( struct pt_regs regs,
+                        unsigned int   irq,
+                        __u32          s390_intparm )
+		{
+#ifdef CONFIG_FAST_IRQ
+	int			ccode;
+	tpi_info_t 	tpi_info;
+	int			new_irq;
+#endif
+	int			use_irq     = irq;
+
+	//
+	// fix me !!!
+	//
+	// We need to schedule device recognition, the interrupt stays
+	//  pending. We need to dynamically allocate an ioinfo structure.
+	//
+	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+	{
+		return;	/* this keeps the device boxed ... */
+	}
+
+		/*
+	 * take fast exit if CPU is in sync. I/O state
 		 *
-		 * Note: We must not issue a message to the console, if the
-		 *       unsolicited interrupt applies to the console device
-		 *       itself !
+	 * 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 CONFIG_DEBUG_IO
-		if (     ( irq != cons_dev                 )
-			 && !( stctl & SCSW_STCTL_ALERT_STATUS )
-			 &&  ( ioinfo[irq]->ui.flags.busy == 0  ) )
+ 	if (    *(__u32 *)__LC_SYNC_IO_WORD )
 		{
-	      char buffer[80];
+		regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT);
 
-			printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n"
-				" ... device status : %02X subchannel status : %02X\n",
-				ioinfo[irq]->devstat.devno,
-				irq,
-				ioinfo[irq]->devstat.dstat,
-				ioinfo[irq]->devstat.cstat);
+      return;
 
-   	   sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
-			         "device %04X, ending_status %d\n",
-			         irq,
-			         ioinfo[irq]->devstat.devno,
-		   	      ending_status);
+	} /* endif */
 
-			s390_displayhex( buffer,
-			                 &(ioinfo[irq]->devstat.ii.irb) ,
-			                 sizeof(irb_t));
+	s390irq_spin_lock(use_irq);
 
-		} /* endif */
+#ifdef CONFIG_FAST_IRQ
+	do {
+#endif /*  CONFIG_FAST_IRQ */
+
+		s390_process_IRQ( use_irq );
+
+#ifdef CONFIG_FAST_IRQ
+
+			   /*
+		 * more interrupts pending ?
+			    */
+		ccode = tpi( &tpi_info );
+
+		if ( ! ccode )
+			break;  	// no, leave ...
+
+		new_irq     = tpi_info.irq;
+
+			   /*
+		 * if the interrupt is for a different irq we
+		 *  release the current irq lock and obtain
+		 *  a new one ...
+			    */
+		if ( new_irq != use_irq )
+			   {
+			s390irq_spin_unlock(use_irq);
+         use_irq = new_irq;
+			s390irq_spin_lock(use_irq);
+
+			   } /* endif */
+
+	} while ( 1 );
+
+#endif /*  CONFIG_FAST_IRQ */
+
+	s390irq_spin_unlock(use_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 */
+	struct s390_irqaction *action;
+	struct pt_regs         regs;       /* for interface compatibility only */
+
+	int               issense         = 0;
+	int               ending_status   = 0;
+	int               allow4handler   = 1;
+	int               chnchk          = 0;
+#if 0
+	int               cpu             = smp_processor_id();
+
+	kstat.irqs[cpu][irq]++;
 #endif
-		/*
-		 * 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 ( ioinfo[irq] == INVALID_STORAGE_AREA )
+	{
+		/* we can't properly process the interrupt ... */
+		tsch( irq, &init_irb );
+		return( 1 );
+	}
+	else
+	{
+		action = ioinfo[irq]->irq_desc.action;
+
+		} /* endif */
+
+#ifdef CONFIG_DEBUG_IO
+      /*
+	 * 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]->devstat.ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK )
-			       && ( !issense                                                    ) )
-           || ( ioinfo[irq]->ui.flags.delsense && ending_status                     ) )
+	if ( action == NULL )
 		{
-			int            ret_io;
-			ccw1_t        *s_ccw  = &ioinfo[irq]->senseccw;
-			unsigned long  s_flag = 0;
+		if ( !ioinfo[irq]->ui.flags.d_disable )
+		{
+			printk( KERN_CRIT"s390_process_IRQ(%04X) "
+			        "- no interrupt handler registered "
+					  "for device %04X !\n",
+			        irq,
+			        ioinfo[irq]->devstat.devno);
+
+		} /* endif */
+	} /* 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, &(ioinfo[irq]->devstat.ii.irb) );
+
+	//
+	// We must only accumulate the status if the device is busy already
+	//
+	if ( ioinfo[irq]->ui.flags.busy )
+				{
+		ioinfo[irq]->devstat.dstat |= ioinfo[irq]->devstat.ii.irb.scsw.dstat;
+		ioinfo[irq]->devstat.cstat |= ioinfo[irq]->devstat.ii.irb.scsw.cstat;
+			}
+			else
+			{
+		ioinfo[irq]->devstat.dstat  = ioinfo[irq]->devstat.ii.irb.scsw.dstat;
+		ioinfo[irq]->devstat.cstat  = ioinfo[irq]->devstat.ii.irb.scsw.cstat;
+
+		ioinfo[irq]->devstat.flag   = 0;   // reset status flags
+
+      } /* endif */
+
+	ioinfo[irq]->devstat.lpum = ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum;
+
+	if ( ioinfo[irq]->ui.flags.busy)
+	{
+		ioinfo[irq]->devstat.intparm  = ioinfo[irq]->u_intparm;
+
+	} /* endif */
+
+		/*
+	 * reset device-busy bit if no longer set in irb
+		 */
+	if (   (ioinfo[irq]->devstat.dstat & DEV_STAT_BUSY                   )
+	    && ((ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0))
+		{
+		ioinfo[irq]->devstat.dstat &= ~DEV_STAT_BUSY;
+
+	} /* endif */
+
+			/*
+	 * Save residual count and CCW information in case primary and
+	 *  secondary status are presented with different interrupts.
+			 */
+	if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl
+	           & (   SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_INTER_STATUS ) )
+			{
+		ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count;
+		ioinfo[irq]->devstat.cpa    = ioinfo[irq]->devstat.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, ioinfo[irq]->devstat.rescnt );
+#endif
+
+	} /* endif */
+
+	irb_cc = ioinfo[irq]->devstat.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 (    (ioinfo[irq]->devstat.ii.irb.scsw.cstat
+	            & (  SCHN_STAT_CHN_DATA_CHK
+	               | SCHN_STAT_CHN_CTRL_CHK
+	               | SCHN_STAT_INTF_CTRL_CHK )       )
+	     && (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,
+		        ioinfo[irq]->devstat.dstat,
+		        ioinfo[irq]->devstat.cstat);
+
+		chnchk = 1;
+
+	} /* endif */
+
+	issense = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.cons;
+
+	if ( issense )
+	{
+		ioinfo[irq]->devstat.scnt  =
+		             ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.scnt;
+		ioinfo[irq]->devstat.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, ioinfo[irq]->devstat.scnt );
+#endif
+			}
+			else
+			{
+		/* don't copy the sense data area ! */
+		sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT;
+
+			} /* endif */
+
+	switch ( irb_cc ) {
+	case 1:      /* status pending */
+
+		ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING;
+
+	case 0:      /* normal i/o interruption */
+
+		fctl  = ioinfo[irq]->devstat.ii.irb.scsw.fctl;
+		stctl = ioinfo[irq]->devstat.ii.irb.scsw.stctl;
+		actl  = ioinfo[irq]->devstat.ii.irb.scsw.actl;
+
+		if ( chnchk && (ioinfo[irq]->senseid.cu_type == 0x3088))
+		{
+			char buffer[80];
+   	
+			sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
+			         "device %04X after channel check\n",
+			         irq,
+			         ioinfo[irq]->devstat.devno );
+
+			s390_displayhex( buffer,
+			                 &(ioinfo[irq]->devstat.ii.irb) ,
+			                 sizeof(irb_t));
+		} /* 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 !
+		 */
+#ifdef CONFIG_DEBUG_IO
+		if (     ( irq != cons_dev                 )
+			 && !( stctl & SCSW_STCTL_ALERT_STATUS )
+			 &&  ( ioinfo[irq]->ui.flags.busy == 0  ) )
+		{
+	      char buffer[80];
+
+			printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n"
+				" ... device status : %02X subchannel status : %02X\n",
+				ioinfo[irq]->devstat.devno,
+				irq,
+				ioinfo[irq]->devstat.dstat,
+				ioinfo[irq]->devstat.cstat);
+
+   	   sprintf( buffer, "s390_process_IRQ(%04X) - irb for "
+			         "device %04X, ending_status %d\n",
+			         irq,
+			         ioinfo[irq]->devstat.devno,
+		   	      ending_status);
+
+			s390_displayhex( buffer,
+			                 &(ioinfo[irq]->devstat.ii.irb) ,
+			                 sizeof(irb_t));
+
+		} /* endif */
+
+#endif
+		/*
+		 * take fast exit if no handler is available
+		 */
+		if ( !action )
+			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 (    (    ( ioinfo[irq]->devstat.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( action->dev_id,
+				        &(ioinfo[irq]->devstat),
+				        sizeof( devstat_t) );
+
+				s_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
+				s_ccw->cda      = (char *)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;
+
+				ioinfo[irq]->devstat.cstat     = 0;
+				ioinfo[irq]->devstat.dstat     = 0;
+				ioinfo[irq]->devstat.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
+				ioinfo[irq]->ui.flags.w4sense          = 0;
+				((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
+				((devstat_t *)(action->dev_id))->scnt  = sense_count;
+
+				if ( sense_count >= 0 )
+				{
+					memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data,
+					        &(ioinfo[irq]->sense_data),
+					        sense_count);
+				}
+				else
+				{
+#if 1
+					panic( "s390_process_IRQ(%04x) encountered "
+					       "negative sense count\n",
+					       irq);
+#else
+					printk( KERN_CRIT"s390_process_IRQ(%04x) encountered "
+					        "negative sense count\n",
+					        irq);
+#endif
+				} /* endif */
+			}
+			else
+			{
+				memcpy( action->dev_id, &(ioinfo[irq]->devstat), 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;
+
+				ioinfo[irq]->devstat.flag     |= DEVSTAT_FINAL_STATUS;
+				action->dev_id->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;
+
+				ioinfo[irq]->devstat.flag             |= DEVSTAT_FINAL_STATUS;
+				((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FINAL_STATUS;
+
+				if ( ioinfo[irq]->ui.flags.newreq )
+				{
+					action->handler( irq, ioinfo[irq]->u_intparm );
+				}
+				else
+				{
+					((io_handler_func1_t)action->handler)( irq, action->dev_id, &regs );
+
+				} /* endif */
+
+				//
+				// reset intparm after final status or we will badly present unsolicited
+				//  interrupts with a intparm value possibly no longer valid.
+				//
+				ioinfo[irq]->devstat.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 )
+					{
+						if ( ioinfo[irq]->ui.flags.newreq )
+						{
+							action->handler( irq, ioinfo[irq]->u_intparm );
+						}
+						else
+						{
+							((io_handler_func1_t)action->handler)( irq, action->dev_id, &regs );
+
+						} /* endif */
+
+					} /* 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 ( ioinfo[irq]->devstat.cstat & SCHN_STAT_PCI )
+				{
+					((devstat_t *)(action->dev_id))->flag |= DEVSTAT_PCI;
+					ioinfo[irq]->devstat.cstat &= ~SCHN_STAT_PCI;
+				}
+				else if ( actl & SCSW_ACTL_SUSPENDED )
+				{
+					((devstat_t *)(action->dev_id))->flag |= DEVSTAT_SUSPENDED;
+
+				} /* endif */
+
+				if ( ioinfo[irq]->ui.flags.newreq )
+				{
+					action->handler( irq, ioinfo[irq]->u_intparm );
+				}
+				else
+				{
+					((io_handler_func1_t)action->handler)( irq,
+					                                       action->dev_id,
+					                                       &regs );
+
+				} /* endif */
+
+			} /* 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;
+
+		ioinfo[irq]->devstat.cstat    = 0;
+		ioinfo[irq]->devstat.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 ( !action )
+			return( ending_status );     		
+
+		memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat );
+
+		ioinfo[irq]->devstat.intparm  = 0;
+
+		if ( !ioinfo[irq]->ui.flags.s_pend )
+		{
+			if ( ioinfo[irq]->ui.flags.newreq )
+			{
+				action->handler( irq, ioinfo[irq]->u_intparm );
+			}
+			else
+			{
+				((io_handler_func1_t)action->handler)( irq, action->dev_id, &regs );
+
+			} /* endif */
+
+		} /* 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;
+
+	if ( cons_dev != -1  )
+	{
+		rc = -EBUSY;
+	}
+	else if ( (irq > highest_subchannel) || (irq < 0) )
+	{
+		rc = -ENODEV;
+	}
+	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+	{
+		return( -ENODEV);
+   }
+	else
+	{
+		/*
+		 * 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
+				 */
+				asm volatile ("STCTL 6,6,%0": "=m" (cr6));
+				cr6 |= 0x01000000;
+				asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+
+			} /* endif */
+
+		} /* endif */
+
+	} /* endif */
+
+	return( rc);
+}
+
+int reset_cons_dev( int irq)
+{
+	int     rc = 0;
+	int     ccode;
+	long    cr6 __attribute__ ((aligned (8)));
+
+	if ( cons_dev != -1  )
+	{
+		rc = -EBUSY;
+	}
+	else if ( (irq > highest_subchannel) || (irq < 0) )
+	{
+		rc = -ENODEV;
+	}
+	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+	{
+		return( -ENODEV);
+   }
+	else
+	{
+		/*
+		 * 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
+				 */
+				asm volatile ("STCTL 6,6,%0": "=m" (cr6));
+				cr6 &= 0xFEFFFFFF;
+				asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+
+			} /* endif */
+
+		} /* endif */
+
+	} /* endif */
+
+	return( rc);
+}
+
+int wait_cons_dev( int irq )
+{
+	int              rc = 0;
+	long             save_cr6;
+
+	if ( irq == cons_dev )
+	{
+
+		/*
+		 * 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)
+			 */
+			asm volatile ("STCTL 6,6,%0": "=m" (cr6));
+			save_cr6  = cr6;
+			cr6      &= 0x01FFFFFF;
+			asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+
+			do {
+				tpi_info_t tpi_info;
+				if (tpi(&tpi_info) == 1) {
+					s390_process_IRQ( tpi_info.irq );
+				} else {
+					s390irq_spin_unlock(irq);
+					tod_wait(100);
+					s390irq_spin_lock(irq);
+				}
+				eieio();
+			} while (ioinfo[irq]->ui.flags.busy == 1);
+
+			/*
+			 * restore previous isc value
+			 */
+			asm volatile ("STCTL 6,6,%0": "=m" (cr6));
+			cr6 = save_cr6;
+			asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+
+		} /* endif */
+
+	}
+	else
+	{
+		rc = EINVAL;
+
+	} /* endif */
+
+
+	return(rc);
+}
+
+
+int enable_cpu_sync_isc( int irq )
+{
+	int             ccode;
+	long            cr6 __attribute__ ((aligned (8)));
+
+	int             count = 0;
+	int             rc    = 0;
+
+	if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA )
+	{
+		ccode = stsch( irq, &(ioinfo[irq]->schib) );
+
+		if ( !ccode )
+		{
+			ioinfo[irq]->schib.pmcw.isc = 5;
+
+			do
+			{
+				ccode = msch( irq, &(ioinfo[irq]->schib) );
+
+				if (ccode == 0 )
+				{
+					/*
+					 * enable interrupt subclass in CPU
+					 */
+					asm volatile ("STCTL 6,6,%0": "=m" (cr6));
+					cr6 |= 0x04000000;  // enable sync isc 5
+					cr6 &= 0xEFFFFFFF;  // disable standard isc 3
+					asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+				}
+				else if (ccode == 3)
+				{
+					rc = -ENODEV;  // device not-oper - very unlikely
+
+				}
+				else if (ccode == 2)
+				{
+					rc = -EBUSY;   // device busy - should not happen
+
+				}
+				else if (ccode == 1)
+				{
+					//
+					// process pending status
+					//
+					ioinfo[irq]->ui.flags.s_pend = 1;
+
+					s390_process_IRQ( irq );
+
+					ioinfo[irq]->ui.flags.s_pend = 0;
+
+					count++;
+
+				} /* endif */
+
+			} while ( ccode == 1 && count < 3 );
+
+			if ( count == 3)
+			{
+				rc = -EIO;
+
+			} /* endif */
+		}
+		else
+		{
+			rc = -ENODEV;     // device is not-operational
+
+		} /* endif */
+	}
+	else
+	{
+		rc = -EINVAL;
+
+	} /* endif */
+
+	return( rc);
+}
+
+int disable_cpu_sync_isc( int irq)
+{
+	int     rc    = 0;
+	int     retry = 5;
+	int     ccode;
+	long    cr6 __attribute__ ((aligned (8)));
+
+	if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA )
+	{
+		ccode = stsch( irq, &(ioinfo[irq]->schib) );
+
+		ioinfo[irq]->schib.pmcw.isc = 3;
+
+		do {
+
+			ccode = msch( irq, &(ioinfo[irq]->schib) );
+
+			switch ( ccode ) {
+			case 0:
+				/*
+				 * disable interrupt subclass in CPU
+				 */
+				asm volatile ("STCTL 6,6,%0": "=m" (cr6));
+				cr6 &= 0xFBFFFFFF; // disable sync isc 5
+				cr6 |= 0x10000000; // enable standard isc 3
+				asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+				break;
+			case 1:
+				ioinfo[irq]->ui.flags.s_pend = 1;
+				s390_process_IRQ( irq );
+				ioinfo[irq]->ui.flags.s_pend = 0;
+				retry--;
+				rc = -EIO;
+				break;
+			case 2:
+				rc = -EBUSY;
+				break;
+			default:
+				rc = -ENODEV;
+				break;
+			} /* endswitch */
+
+		} while ( retry && (ccode ==1) );
+	}
+	else
+	{
+		rc = -EINVAL;
+
+	} /* endif */
+
+	return( rc);
+}
+
+//
+// Input :
+//   devno - device number
+//   ps    - pointer to sense ID data area
+//
+// Output : none
+//
+void VM_virtual_device_info( unsigned int  devno,
+                             senseid_t    *ps )
+{
+	diag210_t  diag_data;
+	int        ccode;
+
+	int        error = 0;
+
+	diag_data.vrdcdvno = devno;
+	diag_data.vrdclen  = sizeof( diag210_t);
+	ccode              = diag210( (diag210_t *)virt_to_phys( &diag_data ) );
+	ps->reserved       = 0xff;
+
+	switch (diag_data.vrdcvcla) {
+	case 0x80:
+
+		switch (diag_data.vrdcvtyp) {
+		case 00:
+
+			ps->cu_type   = 0x3215;
+
+			break;
+
+		default:
+
+			error = 1;
+
+			break;
+
+		} /* endswitch */
+
+		break;
+
+	case 0x40:
+
+		switch (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 (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 (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;
 
-         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( ioinfo[irq]->irq_desc.action->dev_id,
-				        &(ioinfo[irq]->devstat),
-				        sizeof( devstat_t) );
+		case 0x43:
 
-			   s_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
-				s_ccw->cda      = (char *)virt_to_phys( ioinfo[irq]->devstat.ii.sense.data);
-			   s_ccw->count    = SENSE_MAX_COUNT;
-			   s_ccw->flags    = CCW_FLAG_SLI;
+			ps->cu_type   = 0x3203;
 
-			   /*
-			    * 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;
+			break;
 
-			   } /* endif */
+		case 0x42:
 
-			   /*
-			    * 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;
+			ps->cu_type   = 0x3211;
 
-				ioinfo[irq]->ui.flags.fast     = 0;
-				ioinfo[irq]->ui.flags.repall   = 0;
-				ioinfo[irq]->ui.flags.w4final  = 0;
-				ioinfo[irq]->ui.flags.delsense = 0;
+			break;
 
-				ioinfo[irq]->devstat.cstat     = 0;
-				ioinfo[irq]->devstat.dstat     = 0;
-				ioinfo[irq]->devstat.rescnt    = SENSE_MAX_COUNT;
+		case 0x41:
 
-				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;
+			ps->cu_type   = 0x1403;
 
-				ioinfo[irq]->ui.flags.delsense = 1;
-				allow4handler                 = 0;
+			break;
 
-			} /* endif */
+		default:
 
-		} /* endif */
+			error = 1;
 
-      /*
-       * we allow for the device action handler if .
-       *  - we received ending status
-		 *  - the action handler requested to see all interrupts
-		 *  - we received a PCI
-		 *  - fast notification was requested (primary status)
-		 *  - unsollicited interrupts
-		 *
-		 */
-		if ( allow4handler )
-		{
-			allow4handler =    ending_status
-			   || ( ioinfo[irq]->ui.flags.repall                                      )
-			   || ( ioinfo[irq]->devstat.ii.irb.scsw.cstat & SCHN_STAT_PCI            )
-				|| ( (ioinfo[irq]->ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) )
-				|| ( ioinfo[irq]->ui.flags.oper == 0                                   );
+			break;
 
-		} /* endif */
+		} /* endswitch */
 
-      /*
-       * 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;
+		break;
 
-#if CONFIG_DEBUG_IO
-      if ( irq != cons_dev )
-         printk( "s390_process_IRQ( %04X ) : "
-                 "BASIC SENSE bytes avail %d\n",
-                 irq, sense_count );
-#endif
-				ioinfo[irq]->ui.flags.w4sense          = 0;
-				((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
-				((devstat_t *)(action->dev_id))->scnt  = sense_count;
+	case 0x08:
 
-				if (sense_count >= 0)
-				{
-					memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data,
-					        &(ioinfo[irq]->devstat.ii.sense.data),
-					        sense_count);
-				}
-            else
-				{
-#if 1
-					panic( "s390_process_IRQ(%04x) encountered "
-					       "negative sense count\n",
-					       irq);
-#else
-					printk( KERN_CRIT"s390_process_IRQ(%04x) encountered "
-					        "negative sense count\n",
-					        irq);
-#endif
-				} /* endif */
-			}
-			else
-			{
-				memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat );
+		switch (diag_data.vrdcvtyp) {
+		case 0x82:
 
-			}  /* endif */
+			ps->cu_type   = 0x3422;
 
-      } /* endif */
+			break;
 
-      /*
-       * 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 )
-			allow4handler = 0;
+		case 0x81:
 
-		/*
-		 * Call device action handler if applicable
-		 */
-		if ( allow4handler )
-		{
+			ps->cu_type   = 0x3490;
 
-			/*
-			 *  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 */
+			break;
 
-				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;
+		case 0x10:
 
-				ioinfo[irq]->devstat.flag             |= DEVSTAT_FINAL_STATUS;
-				((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FINAL_STATUS;
+			ps->cu_type   = 0x3420;
 
-				action->handler( irq, action->dev_id, &regs);
+			break;
 
-				//
-				// reset intparm after final status or we will badly present unsolicited
-				//  interrupts with a intparm value possibly no longer valid.
-				//
-				ioinfo[irq]->devstat.intparm   = 0;
+		case 0x02:
 
-				//
-				// Was there anything queued ? Start the pending channel program
-				//  if there is one.
-				//
-				if ( ioinfo[irq]->ui.flags.doio_q )
-				{
-					int ret;
+			ps->cu_type   = 0x3430;
 
-					ret = s390_start_IO( irq,
-							               ioinfo[irq]->qcpa,
-							               ioinfo[irq]->qintparm,
-							               ioinfo[irq]->qlpm,
-							               ioinfo[irq]->qflag);
+			break;
 
-					ioinfo[irq]->ui.flags.doio_q = 0;
+		case 0x01:
 
-					/*
-					 * 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 )
-					{
-						action->handler( irq, action->dev_id, &regs);
+			ps->cu_type   = 0x3480;
 
-					} /* endif */
+			break;
 
-				} /* endif */
+		case 0x42:
 
-			}
-			else
-			{
-				ioinfo[irq]->ui.flags.w4final = 1;
-				action->handler( irq, action->dev_id, &regs);
+			ps->cu_type   = 0x3424;
 
-			} /* endif */
+			break;
 
-		} /* endif */
+		case 0x44:
 
-		break;
+			ps->cu_type   = 0x9348;
 
-	case 3:      /* device not operational */
+			break;
 
-		ioinfo[irq]->ui.flags.oper    = 0;
+		default:
 
-		ioinfo[irq]->ui.flags.busy    = 0;
-		ioinfo[irq]->ui.flags.doio    = 0;
-		ioinfo[irq]->ui.flags.haltio  = 0;
+			error = 1;
 
-		ioinfo[irq]->devstat.cstat    = 0;
-		ioinfo[irq]->devstat.dstat    = 0;
-		ioinfo[irq]->devstat.flag    |= DEVSTAT_NOT_OPER;
-		ioinfo[irq]->devstat.flag    |= DEVSTAT_FINAL_STATUS;
+		break;
 
-		/*
-		 * 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.
-		 */
+		} /* endswitch */
 
-		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;
+		break;
 
-			} /* endif */
+	case 02: /* special device class ... */
 
-		} /* endif */
+		switch (diag_data.vrdcvtyp) {
+		case 0x20: /* OSA */
 
-		ioinfo[irq]->ui.flags.fast    = 0;
-		ioinfo[irq]->ui.flags.repall  = 0;
-		ioinfo[irq]->ui.flags.w4final = 0;
+			ps->cu_type   = 0x3088;
+			ps->cu_model  = 0x60;
 
-		memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat );
+			break;
 
-		ioinfo[irq]->devstat.intparm  = 0;
+		default:
 
-		if ( !ioinfo[irq]->ui.flags.s_pend )
-		   action->handler( irq, action->dev_id, &regs);
+			error = 1;
+      	break;
 
-		ending_status    = 1;
+		} /* endswitch */
+
+		break;
+
+	default:
+
+		error = 1;
 
 		break;
 
-	} /* endswitch */
+	} /* endswitch */
+
+	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,
+			diag_data.vrdcvcla,
+			diag_data.vrdcvtyp,
+			diag_data.vrdcrccl,
+			diag_data.vrdccrty,
+			diag_data.vrdccrmd );
+
+	} /* endif */
 
-	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.
+ * 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.
  *
- * 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.
+ *  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 set_cons_dev( int irq )
+int read_dev_chars( int irq, void **buffer, int length )
 {
-	int           ccode;
-	unsigned long cr6 __attribute__ ((aligned (8)));
-	int           rc = 0;
+	unsigned int  flags;
+	ccw1_t       *rdc_ccw;
+	devstat_t     devstat;
+	char         *rdc_buf;
+	int           devflag;
+
+	int           ret      = 0;
+	int           emulated = 0;
+	int           retry    = 5;
 
-	if ( cons_dev != -1  )
+	if ( !buffer || !length )
 	{
-		rc = -EBUSY;
-	}
-	else if ( (irq > highest_subchannel) || (irq < 0) )
+		return( -EINVAL );
+
+	} /* endif */
+
+	if ( (irq > highest_subchannel) || (irq < 0 ) )
 	{
-		rc = -ENODEV;
+	return( -ENODEV );
+
 	}
 	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 	{
 		return( -ENODEV);
    }
-	else
-	{
-		/*
-		 * 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
+	if ( ioinfo[irq]->ui.flags.oper == 0 )
 		{
-			ioinfo[irq]->schib.pmcw.isc = 7;
-
-			ccode = msch( irq, &(ioinfo[irq]->schib) );
+		return( -ENODEV );
 
-			if (ccode)
-			{
-				rc = -EIO;
-			}
-			else
-			{
-				cons_dev = irq;
+	} /* endif */
 
 				/*
-				 * enable console I/O-interrupt sublass 7
+	 * 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.  	
 				 */
-				asm volatile ("STCTL 6,6,%0": "=m" (cr6));
-				cr6 |= 0x01000000;
-				asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+	__save_flags(flags);
+	__cli();
 
-			} /* endif */
+	rdc_ccw = &ioinfo[irq]->senseccw;
 
-		} /* endif */
+	if ( !ioinfo[irq]->ui.flags.ready )
+	{
+		ret = request_irq( irq,
+		                   init_IRQ_handler,
+		                   0, "RDC", &devstat );
 
-	} /* endif */
+		if ( !ret )
+		{
+			emulated = 1;
 
-	return( rc);
-}
+		} /* endif */
 
-int reset_cons_dev( int irq)
-{
-	int     rc = 0;
-	int     ccode;
-	long    cr6 __attribute__ ((aligned (8)));
+	} /* endif */
 
-	if ( cons_dev != -1  )
-	{
-		rc = -EBUSY;
-	}
-	else if ( (irq > highest_subchannel) || (irq < 0) )
+	if ( !ret )
 	{
-		rc = -ENODEV;
-	}
-	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+		if ( ! *buffer )
 	{
-		return( -ENODEV);
+			rdc_buf  = kmalloc( length, GFP_KERNEL);
    }
 	else
 	{
-		/*
-		 * reset the indicated console device to operate
-		 *  on default console interrupt sublass 3
-		 */
-		ccode = stsch( irq, &(ioinfo[irq]->schib) );
+			rdc_buf = *buffer;
 
-		if (ccode)
+		} /* endif */
+
+		if ( !rdc_buf )
 		{
-			rc                        = -ENODEV;
-			ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
+			ret = -ENOMEM;
 		}
 		else
 		{
+			do
+			{
+				rdc_ccw->cmd_code = CCW_CMD_RDC;
+				rdc_ccw->cda      = (char *)virt_to_phys( rdc_buf );
+				rdc_ccw->count    = length;
+				rdc_ccw->flags    = CCW_FLAG_SLI;
+
+				memset( (devstat_t *)(ioinfo[irq]->irq_desc.action->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 = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag;
 
-			ioinfo[irq]->schib.pmcw.isc = 3;
+			} while (    ( retry                                     )
+			          && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) );
 
-			ccode = msch( irq, &(ioinfo[irq]->schib) );
+		} /* endif */
 
-			if (ccode)
-			{
-				rc = -EIO;
-			}
-			else
+		if ( !retry )
 			{
-				cons_dev = -1;
+			ret = -EBUSY;
+
+		} /* endif */
+
+		__restore_flags(flags);
 
 				/*
-				 * disable special console I/O-interrupt sublass 7
+		 * on success we update the user input parms
 				 */
-				asm volatile ("STCTL 6,6,%0": "=m" (cr6));
-				cr6 &= 0xFEFFFFFF;
-				asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+		if ( !ret )
+		{
+			*buffer = rdc_buf;
 
 			} /* endif */
 
+		if ( emulated )
+		{
+			free_irq( irq, &devstat);
+
 		} /* endif */
 
 	} /* endif */
 
-	return( rc);
+	return( ret );
 }
 
-int wait_cons_dev( int irq )
-{
-	int              rc = 0;
-	long             save_cr6;
-
-	if ( irq == cons_dev )
-	{
-
 		/*
-		 * before entering the spinlock we may already have
-		 *  processed the interrupt on a different CPU ...
+ *  Read Configuration data
 		 */
-		if ( ioinfo[irq]->ui.flags.busy == 1 )
+int read_conf_data( int irq, void **buffer, int *length, __u8 lpm )
 		{
-			long cr6 __attribute__ ((aligned (8)));
+	unsigned long flags;
+	int           ciw_cnt;
 
-			/*
-			 * disable all, but isc 7 (console device)
-			 */
-			asm volatile ("STCTL 6,6,%0": "=m" (cr6));
-			save_cr6  = cr6;
-			cr6      &= 0x01FFFFFF;
-			asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+	int           found  = 0; // RCD CIW found
+	int           ret    = 0; // return code
 
-			do {
-				tpi_info_t tpi_info;
-				if (tpi(&tpi_info) == 1) {
-					s390_process_IRQ( tpi_info.irq );
-				} else {
-					s390irq_spin_unlock(irq);
-					tod_wait(100);
-					s390irq_spin_lock(irq);
+	if ( (irq > highest_subchannel) || (irq < 0 ) )
+	{
+		return( -ENODEV );
 				}
-				eieio();
-			} while (ioinfo[irq]->ui.flags.busy == 1);
+	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+	{
+		return( -ENODEV);
+	}
+	else 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 */
 
 			/*
-			 * restore previous isc value
+	 * scan for RCD command in extended SenseID data
 			 */
-			asm volatile ("STCTL 6,6,%0": "=m" (cr6));
-			cr6 = save_cr6;
-			asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
 
-		} /* endif */
-
-	}
-	else
+	for ( ciw_cnt = 0; (found == 0) && (ciw_cnt < 62); ciw_cnt++ )
 	{
-		rc = EINVAL;
+		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 */
 
-	return(rc);
-}
-
+   } /* endfor */
 
-int enable_cpu_sync_isc( int irq )
+	if ( found )
 {
-	int             ccode;
-	long            cr6 __attribute__ ((aligned (8)));
-
-	int             count = 0;
-	int             rc    = 0;
+		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 */
 
-	if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA )
-	{
-		ccode = stsch( irq, &(ioinfo[irq]->schib) );
+		__save_flags(flags);
+		__cli();
 
-		if ( !ccode )
+		if ( !ioinfo[irq]->ui.flags.ready )
 		{
-			ioinfo[irq]->schib.pmcw.isc = 5;
+			pdevstat = &devstat;
+			ret      = request_irq( irq,
+			                        init_IRQ_handler,
+			                        0, "RCD", pdevstat );
 
-			do
+			if ( !ret )
 			{
-				ccode = msch( irq, &(ioinfo[irq]->schib) );
+				emulated = 1;
 
-				if (ccode == 0 )
-				{
-					/*
-					 * enable interrupt subclass in CPU
-					 */
-					asm volatile ("STCTL 6,6,%0": "=m" (cr6));
-					cr6 |= 0x04000000;  // enable sync isc 5
-					cr6 &= 0xEFFFFFFF;  // disable standard isc 3
-					asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+			} /* endif */
 				}
-				else if (ccode == 3)
+		else
 				{
-					rc = -ENODEV;  // device not-oper - very unlikely
+			pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
 
-				}
-				else if (ccode == 2)
-				{
-					rc = -EBUSY;   // device busy - should not happen
+		} /* endif */
 
+		if ( !ret )
+		{
+			if ( init_IRQ_complete )
+			{
+				rcd_buf = kmalloc( ioinfo[irq]->senseid.ciw[ciw_cnt].count,
+				                   GFP_KERNEL);
 				}
-				else if (ccode == 1)
+			else
 				{
-					//
-					// process pending status
-					//
-			      ioinfo[irq]->ui.flags.s_pend = 1;
-
-					s390_process_IRQ( irq );
+				rcd_buf = alloc_bootmem( ioinfo[irq]->senseid.ciw[ciw_cnt].count);
 
-			      ioinfo[irq]->ui.flags.s_pend = 0;
+   		} /* endif */
 
-					count++;
+			if ( rcd_buf == NULL )
+			{
+				ret = -ENOMEM;	
 
 				} /* endif */
 
-			} while ( ccode == 1 && count < 3 );
+			if ( !ret )
+			{
+				memset( rcd_buf,
+				        '\0',
+				        ioinfo[irq]->senseid.ciw[ciw_cnt].count);
 
-			if ( count == 3)
+				do
 			{
-				rc = -EIO;
+					rcd_ccw->cmd_code = ioinfo[irq]->senseid.ciw[ciw_cnt].cmd;
+					rcd_ccw->cda      = (char *)virt_to_phys( rcd_buf );
+					rcd_ccw->count    = ioinfo[irq]->senseid.ciw[ciw_cnt].count;
+					rcd_ccw->flags    = CCW_FLAG_SLI;
 
-			} /* endif */
+					memset( pdevstat, '\0', sizeof( devstat_t));
+
+					if ( lpm )
+					{
+						ioflags = DOIO_WAIT_FOR_INTERRUPT
+						          | DOIO_VALID_LPM    					
+						          | DOIO_DONT_CALL_INTHDLR;
 		}
 		else
 		{
-			rc = -ENODEV;     // device is not-operational
+						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
 	{
-		rc = -EINVAL;
+               	   retry--;    // try again ...
 
 	} /* endif */
 
-	return( rc);
-}
+   	            break;
 
-int disable_cpu_sync_isc( int irq)
-{
-	int     rc = 0;
-	int     ccode;
-	long    cr6 __attribute__ ((aligned (8)));
+	            default :   // -EBUSY, -ENODEV, ???
+   	            retry = 0;
 
-	if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA )
-	{
-		ccode = stsch( irq, &(ioinfo[irq]->schib) );
+      	      } /* endswitch */
 
-		ioinfo[irq]->schib.pmcw.isc = 3;
+				} while ( retry );
 
-		ccode = msch( irq, &(ioinfo[irq]->schib) );
+			} /* endif */
 
-		if (ccode)
+			__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
 		{
-			rc = -EIO;
+			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);
 
-			/*
-			 * enable interrupt subclass in CPU
-			 */
-			asm volatile ("STCTL 6,6,%0": "=m" (cr6));
-			cr6 &= 0xFBFFFFFF;             // disable sync isc 5
-			cr6 |= 0x10000000;             // enable standard isc 3
-			asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory");
+   			} /* endif */
+
+			} /* endif */
+
+			*buffer = NULL;
+			*length = 0;
 
 		} /* endif */
 
+		if ( emulated )
+			free_irq( irq, pdevstat);
 	}
 	else
 	{
-		rc = -EINVAL;
+		ret = -EOPNOTSUPP;
 
 	} /* endif */
 
-	return( rc);
+	return( ret );
+
 }
 
-//
-// Input :
-//   devno - device number
-//   ps    - pointer to sense ID data area
-//
-// Output : none
-//
-void VM_virtual_device_info( unsigned int  devno,
-                             senseid_t    *ps )
+int get_dev_info( int irq, dev_info_t * pdi)
 {
-	diag210_t  diag_data;
-	int        ccode;
-
-	int        error = 0;
-
-	diag_data.vrdcdvno = devno;
-	diag_data.vrdclen  = sizeof( diag210_t);
-	ccode              = diag210( (diag210_t *)virt_to_phys( &diag_data ) );
-	ps->reserved       = 0xff;
-
-	switch (diag_data.vrdcvcla) {
-	case 0x80:
+	return( get_dev_info_by_irq( irq, pdi));
+}
 
-		switch (diag_data.vrdcvtyp) {
-		case 00:
 
-			ps->cu_type   = 0x3215;
+static int __inline__ get_next_available_irq( ioinfo_t *pi)
+{
+	int ret_val;
 
+	while ( TRUE )
+	{
+		if ( pi->ui.flags.oper )
+		{
+			ret_val = pi->irq;
 			break;
+		}
+		else
+		{
+			pi = pi->next;
 
-		default:
-
-			error = 1;
-
+			//
+			// leave at end of list unconditionally
+			//
+			if ( pi == NULL )
+			{
+				ret_val = -ENODEV;
 			break;
+			}
 
-		} /* endswitch */
+		} /* endif */
 
-		break;
+	} /* endwhile */
 
-	case 0x40:
+	return ret_val;
+}
 
-		switch (diag_data.vrdcvtyp) {
-		case 0xC0:
 
-			ps->cu_type   = 0x5080;
+int get_irq_first( void )
+{
+   int ret_irq;
 
-			break;
+	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 );
 
-		case 0x80:
+		}
+		else
+		{
+			ret_irq = -ENODEV;
 
-			ps->cu_type   = 0x2250;
+		} /* endif */
+	}
+	else
+	{
+		ret_irq = -ENODEV;
 
-			break;
+	} /* endif */
 
-		case 0x04:
+	return ret_irq;
+}
 
-			ps->cu_type   = 0x3277;
+int get_irq_next( int irq )
+{
+	int ret_irq;	
 
-			break;
+	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 );
 
-		case 0x01:
+			} /* endif */
+		}
+		else
+		{
+			ret_irq = -ENODEV;     	
 
-			ps->cu_type   = 0x3278;
+		} /* endif */
+	}
+	else
+	{
+		ret_irq = -EINVAL;
 
-			break;
+	} /* endif */
 
-		default:
+	return ret_irq;
+}
 
-			error = 1;
+int get_dev_info_by_irq( int irq, dev_info_t *pdi)
+{
 
-			break;
+	if ( irq > highest_subchannel || irq < 0 )
+	{
+		return -ENODEV;
+	}
+	else if ( pdi == NULL )
+	{
+		return -EINVAL;
+	}
+	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+	{
+		return( -ENODEV);
+	}
+	else
+	{
+		pdi->devno = ioinfo[irq]->schib.pmcw.dev;
+		pdi->irq   = irq;
 
-		} /* endswitch */
+		if ( ioinfo[irq]->ui.flags.oper )
+		{
+			pdi->status = 0;
+			memcpy( &(pdi->sid_data),
+			        &ioinfo[irq]->senseid,
+			        sizeof( senseid_t));
+		}
+		else
+		{
+			pdi->status = DEVSTAT_NOT_OPER;
+			memcpy( &(pdi->sid_data),
+			        '\0',
+			        sizeof( senseid_t));
+			pdi->sid_data.cu_type = 0xFFFF;
 
-		break;
+		} /* endif */
 
-	case 0x20:
+		if ( ioinfo[irq]->ui.flags.ready )
+			pdi->status |= DEVSTAT_DEVICE_OWNED;
 
-		switch (diag_data.vrdcvtyp) {
-		case 0x84:
+		return 0;
 
-			ps->cu_type   = 0x3505;
+	} /* endif */
 
-			break;
+}
 
-		case 0x82:
 
-			ps->cu_type   = 0x2540;
+int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi)
+{
+	int i;
+	int rc = -ENODEV;
 
-			break;
+	if ( devno > 0x0000ffff )
+	{
+		return -ENODEV;
+	}
+	else if ( pdi == NULL )
+	{
+		return -EINVAL;
+	}
+	else
+	{
 
-		case 0x81:
+		for ( i=0; i <= highest_subchannel; i++ )
+		{
 
-			ps->cu_type   = 0x2501;
+			if (    ioinfo[i] != INVALID_STORAGE_AREA
+			     && ioinfo[i]->schib.pmcw.dev == devno )
+			{
+				if ( ioinfo[i]->ui.flags.oper )
+				{
+					pdi->status = 0;
+					pdi->irq    = i;
+					pdi->devno  = devno;
 
-			break;
+					memcpy( &(pdi->sid_data),
+					        &ioinfo[i]->senseid,
+					        sizeof( senseid_t));
+				}
+				else
+				{
+					pdi->status = DEVSTAT_NOT_OPER;
+					pdi->irq    = i;
+					pdi->devno  = devno;
 
-		default:
+					memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t));
+					pdi->sid_data.cu_type = 0xFFFF;
 
-			error = 1;
+				} /* endif */
 
+				if ( ioinfo[i]->ui.flags.ready )
+					pdi->status |= DEVSTAT_DEVICE_OWNED;
+
+				rc = 0; /* found */
 			break;
 
-		} /* endswitch */
+			} /* endif */
+
+		} /* endfor */
 
-		break;
+		return( rc);
 
-	case 0x10:
+	} /* endif */
 
-		switch (diag_data.vrdcvtyp) {
-		case 0x84:
+}
 
-			ps->cu_type   = 0x3525;
+int get_irq_by_devno( unsigned int devno )
+{
+	int i;
+	int rc = -1;
 
-			break;
+	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;
 
-		case 0x82:
+			} /* endif */
 
-			ps->cu_type   = 0x2540;
+		} /* endfor */
 
-			break;
+	} /* endif */
 
-		case 0x4F:
-		case 0x4E:
-		case 0x48:
+	return( rc);
+}
 
-			ps->cu_type   = 0x3820;
+unsigned int get_devno_by_irq( int irq )
+{
 
-			break;
+	if (    ( irq > highest_subchannel            )
+	     || ( irq < 0                             )
+	     || ( ioinfo[irq] == INVALID_STORAGE_AREA ) )
+	{
+		return -1;
 
-		case 0x4D:
-		case 0x49:
-		case 0x45:
+	} /* endif */
 
-			ps->cu_type   = 0x3800;
+	/*
+	 * 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;
+}
 
-			break;
+/*
+ * 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;
+	unsigned long psw_flags;
 
-		case 0x4B:
+	/*
+	 * 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,
+		                       0,
+		                       "INIT",
+		                       &devstat);
 
-			ps->cu_type   = 0x4248;
+		if ( !irq_ret )
+		{
+			/*
+			 * avoid sync processing (STSCH/MSCH) for every
+			 *  single I/O during boot (IPL) processing.
+			 */
+			spin_lock_irqsave( &sync_isc, psw_flags);
 
-			break;
+			ret = enable_cpu_sync_isc( irq);
 
-		case 0x4A:
+			if ( ret )
+			{
+				spin_unlock_irqrestore( &sync_isc, psw_flags);
+			}
+			else
+			{
+				ioinfo[irq]->ui.flags.syncio = 1; // global
 
-			ps->cu_type   = 0x4245;
+				memset( &ioinfo[irq]->senseid, '\0', sizeof( senseid_t));
 
-			break;
+				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;
 
-		case 0x47:
+					ret = read_conf_data( irq, (void **)&prcd, &lrcd, 0 );
 
-			ps->cu_type   = 0x3262;
+					if ( !ret )	// on success only ...
+					{
+#ifdef CONFIG_DEBUG_IO
+						char buffer[80];
 
-			break;
+						sprintf( buffer,
+						         "RCD for device(%04X)/"
+						         "subchannel(%04X) returns :\n",
+						         ioinfo[irq]->schib.pmcw.dev,
+						         irq );
+
+						s390_displayhex( buffer, prcd, lrcd );
+#endif      				
+						if ( init_IRQ_complete )
+						{
+							kfree( prcd );
+						}
+						else
+						{
+							free_bootmem( (unsigned long)prcd, lrcd );
 
-		case 0x43:
+ 			  			} /* endif */
 
-			ps->cu_type   = 0x3203;
+	   			} /* endif */
 
-			break;
+     	      } /* endif */
+#endif
+				s390_DevicePathVerification( irq, 0 );
 
-		case 0x42:
+				disable_cpu_sync_isc( irq );
 
-			ps->cu_type   = 0x3211;
+				ioinfo[irq]->ui.flags.syncio = 0; // global
 
-			break;
+				spin_unlock_irqrestore( &sync_isc, psw_flags);
 
-		case 0x41:
+			} /* endif */  	
 
-			ps->cu_type   = 0x1403;
+			free_irq( irq, &devstat );
 
-			break;
+		} /* endif */
 
-		default:
+	} /* endif */
 
-			error = 1;
+}
 
-			break;
+/*
+ * 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 ... */
 
-		} /* endswitch */
+	do
+	{
+		s390_device_recognition_irq( irq );
 
-		break;
+		irq ++;
 
-	case 0x08:
+	} while ( irq <= highest_subchannel );
 
-		switch (diag_data.vrdcvtyp) {
-		case 0x82:
+}
 
-			ps->cu_type   = 0x3422;
 
-			break;
+/*
+ * 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 ... */
 
-		case 0x81:
+	do
+	{
+		ret = s390_validate_subchannel( irq, 0);
 
-			ps->cu_type   = 0x3490;
+		irq++;
 
-			break;
+  	} while ( (ret != -ENXIO) && (irq < __MAX_SUBCHANNELS) );
 
-		case 0x10:
+	highest_subchannel = --irq;
 
-			ps->cu_type   = 0x3420;
+	printk( "\nHighest subchannel number detected: %u\n",
+	        highest_subchannel);
+}
 
-			break;
+/*
+ * 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 )
+{
 
-		case 0x02:
+	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;
 
-			ps->cu_type   = 0x3430;
+	/*
+	 * 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 = &init_schib;
 
-			break;
+	} /* endif */
 
-		case 0x01:
+	/*
+	 * 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;
 
-			ps->cu_type   = 0x3480;
+	} /* endif */
 
-			break;
+	ccode = stsch( irq, p_schib);
 
-		case 0x42:
+	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);
 
-			ps->cu_type   = 0x3424;
+			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
+				ioinfo[irq]->ui.flags.oper = 0;
 
-			break;
+		} /* endif */
 
-		case 0x44:
+		if ( p_schib->pmcw.dnv )
+		{
+			if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+			{	
 
-			ps->cu_type   = 0x9348;
+				if ( !init_IRQ_complete )
+				{
+					ioinfo[irq] =
+					   (ioinfo_t *)alloc_bootmem( sizeof(ioinfo_t));
+				}
+				else
+				{
+					ioinfo[irq] =
+					   (ioinfo_t *)kmalloc( sizeof(ioinfo_t),
+				                           GFP_KERNEL );
 
-			break;
+				} /* endif */
 
-		default:
+				memset( ioinfo[irq], '\0', sizeof( ioinfo_t));
+				memcpy( &ioinfo[irq]->schib,
+			           &init_schib,
+			           sizeof( schib_t));
+				ioinfo[irq]->irq_desc.status  = IRQ_DISABLED;
+				ioinfo[irq]->irq_desc.handler = &no_irq_type;
 
-			error = 1;
+				/*
+				 * 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;
 
+					do
+					{
+						if ( irq < pi->next->irq )
+						{
+							ioinfo[irq]->next = pi->next;
+							ioinfo[irq]->prev = pi;
+							pi->next->prev    = ioinfo[irq];
+							pi->next          = ioinfo[irq];
 			break;
 
-		} /* endswitch */
-
-		break;
+						} /* endif */
 
-	default:
+						pi = pi->next;
 
-		error = 1;
+					} while ( 1 );
 
-		break;
+				} /* endif */
 
-	} /* endswitch */
+			} /* 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,
-			diag_data.vrdcvcla,
-			diag_data.vrdcvtyp,
-			diag_data.vrdcrccl,
-			diag_data.vrdccrty,
-			diag_data.vrdccrmd );
+			// initialize some values ...	
+			ioinfo[irq]->ui.flags.pgid_supp = 1;
 
-	} /* endif */
+			ioinfo[irq]->opm =   ioinfo[irq]->schib.pmcw.pim
+			                   & ioinfo[irq]->schib.pmcw.pam
+			                   & ioinfo[irq]->schib.pmcw.pom;
 
-}
+			printk( "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);
 
 /*
- * 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.
+			 * initialize ioinfo structure
+			 */
+			ioinfo[irq]->irq             = irq;
+			ioinfo[irq]->nopfunc         = NULL;
+			ioinfo[irq]->ui.flags.busy   = 0;
+			ioinfo[irq]->ui.flags.ready  = 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 ...
  */
-int read_dev_chars( int irq, void **buffer, int length )
+			if ( ioinfo[irq]->opm )
 {
-  unsigned int  flags;
-  ccw1_t       *rdc_ccw;
-  devstat_t     devstat;
-  char         *rdc_buf;
-  int           devflag;
-
-  int           ret      = 0;
-  int           emulated = 0;
-  int           retry    = 5;
-
-  if ( !buffer || !length )
+				/*
+				 * 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 ) )
   {
-    return( -EINVAL );
+					ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */
 
   } /* endif */
 
-  if ( (irq > highest_subchannel) || (irq < 0 ) )
-  {
-    return( -ENODEV );
+				retry = 5;
 
-	}
-	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+				do
 	{
-		return( -ENODEV);
-   }
+					ccode2 = msch_err( irq, &ioinfo[irq]->schib);
 
-	if ( ioinfo[irq]->ui.flags.oper == 0 )
-  {
-    return( -ENODEV );
+					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;
 
-  } /* endif */
+					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
 	/*
-	 * 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.  	
+						 * we mark it not-oper as we can't
+						 *  properly operate it !
 	 */
-  __save_flags(flags);
-  __cli();
+						ioinfo[irq]->ui.flags.oper = 0;
+						tod_wait( 100);	/* allow for recovery */
+						retry--;
+						ret = -EBUSY;
+						break;
 
-	rdc_ccw = &ioinfo[irq]->senseccw;
+					case 3:  // not operational
+						ioinfo[irq]->ui.flags.oper = 0;
+						retry                      = 0;
+						ret = -ENODEV;
+						break;
 
-	if ( !ioinfo[irq]->ui.flags.ready )
-  {
-		ret = request_irq( irq,
-		                   init_IRQ_handler,
-		                   0, "RDC", &devstat );
+					default:
+#define PGMCHK_OPERAND_EXC      0x15
 
-    if ( !ret )
+						if ( (ccode2 & PGMCHK_OPERAND_EXC) == PGMCHK_OPERAND_EXC )
     {
-      emulated = 1;
+							/*
+							 * re-issue the modify subchannel without trying to
+							 *  enable the concurrent sense facility
+							 */
+							ioinfo[irq]->schib.pmcw.csense = 0;
 
-    } /* endif */
-
-  } /* endif */
+							ccode2 = msch_err( irq, &ioinfo[irq]->schib);
 
-  if ( !ret )
-  {
-    if ( ! *buffer )
+							if ( ccode2 != 0 )
     {
-      rdc_buf  = kmalloc( length, GFP_KERNEL);
+								printk( " ... msch() (2) failed with CC = %X\n",
+								        ccode2 );
+								ioinfo[irq]->ui.flags.oper = 0;
+								ret                        = -EIO;
     }
     else
     {
-      rdc_buf = *buffer;
+								ioinfo[irq]->ui.flags.oper   = 1;
+								ioinfo[irq]->ui.flags.consns = 0;
+								ret                          = 0;
 
     } /* endif */
-
-    if ( !rdc_buf )
-    {
-       ret = -ENOMEM;
     }
     else
     {
-      do
-      {
-         rdc_ccw->cmd_code = CCW_CMD_RDC;
-         rdc_ccw->cda      = (char *)virt_to_phys( rdc_buf );
-         rdc_ccw->count    = length;
-         rdc_ccw->flags    = CCW_FLAG_SLI;
-
-         ret = s390_start_IO( irq,
-                              rdc_ccw,
-		                        0x00524443, // RDC
-		                        0,          // n/a
-		                        DOIO_WAIT_FOR_INTERRUPT );
-         retry--;
-				devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag;
-
-      } while (    ( retry                                     )
-	             && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) );
+							printk( " ... msch() (1) failed with CC = %X\n",
+							        ccode2);
+							ioinfo[irq]->ui.flags.oper = 0;
+							ret                        = -EIO;
 
     } /* endif */
 
-    if ( !retry )
-    {
-      ret = -EBUSY;
+						retry  = 0;
+						break;
 
-    } /* endif */
+					} /* endswitch */
 
-    __restore_flags(flags);
+				} while ( ccode2 && retry );
 
-    /*
-     * on success we update the user input parms
-     */
-    if ( !ret )
+				if ( (ccode2 != 0) && (ccode2 != 3) && (!retry) )
     {
-      *buffer = rdc_buf;
+					printk( " ... 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;    	
 
-    if ( emulated )
+			} /* endif */
+		}
+		else
     {
-      free_irq( irq, &devstat);
+			ret = -ENODEV;
 
     } /* endif */
+	}
+	else
+	{
+		
+		ret = -ENXIO;
 
   } /* endif */
 
@@ -2785,19 +4398,38 @@
 }
 
 /*
- *  Read Configuration data
+ * 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 read_conf_data( int irq, void **buffer, int *length )
+int s390_SenseID( int irq, senseid_t *sid, __u8 lpm )
 {
-   int          found   = 0;
-   int          ciw_cnt = 0;
-   unsigned int flags;
+	ccw1_t     sense_ccw[2];  /* 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 */
 
-   int          ret = 0;
+	senseid_t *psid     = sid;/* start with the external buffer */	
+	int        sbuffer  = 0; /* switch SID data buffer */
 
    if ( (irq > highest_subchannel) || (irq < 0 ) )
    {
       return( -ENODEV );
+
    }
 	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 	{
@@ -2811,686 +4443,720 @@
 
    } /* endif */
 
-   /*
-    * scan for RCD command in extended SenseID data
-    */
-   for ( ; (found == 0) && (ciw_cnt < 62); ciw_cnt++ )
-   {
-      if ( ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD )
-      {
-         found = 1;
-      	break;
-      } /* endif */
-
-   } /* endfor */
-
-   if ( found )
-   {
-		ccw1_t    *rcd_ccw = &ioinfo[irq]->senseccw;
-       devstat_t  devstat;
-       char      *rcd_buf;
-       int        devflag;
-
-       int        emulated = 0;
-       int        retry    = 5;
-
-   	 __save_flags(flags);
-	    __cli();
-
 		if ( !ioinfo[irq]->ui.flags.ready )
        {
-			ret = request_irq( irq,
-			                   init_IRQ_handler,
-			                   0, "RCD", &devstat );
 
-          if ( !ret )
-          {
-             emulated = 1;
-
-          } /* endif */
-
-       } /* endif */
-
-       if ( !ret )
-       {
-			rcd_buf = kmalloc( ioinfo[irq]->senseid.ciw[ciw_cnt].count,
-			                   GFP_KERNEL);
-
-          do
-          {
-				rcd_ccw->cmd_code = ioinfo[irq]->senseid.ciw[ciw_cnt].cmd;
-	   	    rcd_ccw->cda      = (char *)virt_to_phys( rcd_buf );
-				rcd_ccw->count    = ioinfo[irq]->senseid.ciw[ciw_cnt].count;
-		       rcd_ccw->flags    = CCW_FLAG_SLI;
-
-             ret = s390_start_IO( irq,
-                                  rcd_ccw,
-                                  0x00524344,  // == RCD
-                                  0,           // n/a
-                                  DOIO_WAIT_FOR_INTERRUPT );
-
-             retry--;
-
-				devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag;
-
-          } while (    ( retry                                     )
-                    && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) );
-
-          if ( !retry )
-             ret = -EBUSY;
-
-          __restore_flags(flags);
-
-       } /* endif */
+		pdevstat = &devstat;
 
        /*
-        * on success we update the user input parms
+		 * 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.
         */
-       if ( !ret )
-       {
-			*length = ioinfo[irq]->senseid.ciw[ciw_cnt].count;
-          *buffer = rcd_buf;
-
-       } /* endif */
+		irq_ret = request_irq( irq, init_IRQ_handler, 0, "SID", &devstat);
 
-       if ( emulated )
-          free_irq( irq, &devstat);
+		if ( irq_ret == 0 )
+			inlreq = 1;
    }
    else
    {
-      ret = -EINVAL;
+		inlreq   = 0;
+		irq_ret  = 0;
+		pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
 
    } /* endif */
 
-   return( ret );
-
-}
-
-int get_dev_info( int irq, dev_info_t *pdi)
+	if ( irq_ret == 0 )
 {
-   return( get_dev_info_by_irq( irq, pdi) );
-}
+      int i;
 
+		s390irq_spin_lock( irq);
 
-static int __inline__ get_next_available_irq( ioinfo_t *pi)
+		// more than one path installed ?
+		if ( ioinfo[irq]->schib.pmcw.pim != 0x80 )
 {
-	int ret_val;
-
-	while ( TRUE )
-	{
-		if ( pi->ui.flags.oper )
-		{
-			ret_val = pi->irq;
-			break;
+			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      = (char *)virt_to_phys( psid );
+			sense_ccw[1].count    = sizeof( senseid_t);
+			sense_ccw[1].flags    = CCW_FLAG_SLI;
 		}
 		else
 		{
-			pi = pi->next;
-
-			//
-			// leave at end of list unconditionally
-			//
-			if ( pi == NULL )
-			{
-				ret_val = -ENODEV;
-				break;
-			}
+			sense_ccw[0].cmd_code = CCW_CMD_SENSE_ID;
+			sense_ccw[0].cda      = (char *)virt_to_phys( psid );
+			sense_ccw[0].count    = sizeof( senseid_t);
+			sense_ccw[0].flags    = CCW_FLAG_SLI;
 
 		} /* endif */
 
-	} /* endwhile */
-
-	return ret_val;
-}
+		for ( i = 0 ; (i < 8) ; i++ )
+		{
+			pathmask = 0x80 >> i;						
 
+			domask = ioinfo[irq]->opm & pathmask;
 
-int get_irq_first( void )
-{
-   int ret_irq;
+			if ( lpm )
+				domask &= lpm;
 
-	if ( ioinfo_head )
-	{
-		if ( ioinfo_head->ui.flags.oper )
-		{
-			ret_irq = ioinfo_head->irq;
-		}
-		else if ( ioinfo_head->next )
+			if ( domask )
 		{
-			ret_irq = get_next_available_irq( ioinfo_head->next );
+				psid->cu_type    = 0xFFFF;  /* initialize fields ... */
+				psid->cu_model   = 0;
+				psid->dev_type   = 0;
+				psid->dev_model  = 0;
 
-		}
-		else
-		{
-			ret_irq = -ENODEV;
+				retry            = 5;  /* retry count    */
+				io_retry         = 1;  /* enable retries */
    	
-		} /* endif */
-	}
-	else
+				/*
+				 * We now issue a SenseID request. In case of BUSY,
+				 *  STATUS PENDING or non-CMD_REJECT error conditions
+				 *  we run simple retries.
+				 */
+				do
 	{
-		ret_irq = -ENODEV;
-
-	} /* endif */
+					memset( pdevstat, '\0', sizeof( devstat_t) );
 
-	return ret_irq;
-}
+					irq_ret = s390_start_IO( irq,
+					                         sense_ccw,
+					                         0x00E2C9C4,  // == SID
+			   		                      domask,
+					                         DOIO_WAIT_FOR_INTERRUPT
+					                          | DOIO_TIMEOUT
+					                          | DOIO_VALID_LPM
+					                          | DOIO_DONT_CALL_INTHDLR );
 
-int get_irq_next( int irq )
-{
-	int ret_irq;	
+					//
+					// The OSA_E FE card possibly causes -ETIMEDOUT
+					//  conditions, as the SenseID may stay start
+					//  pending. This will cause start_IO() to finally
+					//  halt the operation we should retry. If the halt
+					//  fails this may cause -EBUSY we simply retry
+					//  and eventually clean up with free_irq().   	     		
+					//
 
-	if ( ioinfo[irq] != INVALID_STORAGE_AREA )
+					if ( psid->cu_type  == 0xFFFF )
 	{
-		if ( ioinfo[irq]->next )
+						if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 		{
-			if ( ioinfo[irq]->next->ui.flags.oper )
+#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
+						} /* endif */
+
+						if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
 			{
-				ret_irq = ioinfo[irq]->next->irq;
-			}
-			else
+							/*
+							 * 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) )
 			{
-				ret_irq = get_next_available_irq( ioinfo[irq]->next );
-
-			} /* endif */
+#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
+								io_retry = 1;
 		}
+#ifdef CONFIG_DEBUG_IO
 		else
 		{
-			ret_irq = -ENODEV;     	
+								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 */
+#endif
 	}
-	else
+						else if (    ( pdevstat->flag & DEVSTAT_NOT_OPER )
+					             || ( irq_ret        == -ENODEV         ) )
 	{
-		ret_irq = -EINVAL;
+#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
 
-	} /* endif */
+							io_retry          = 0;
+							ioinfo[irq]->opm &= ~domask;
 
-	return ret_irq;
 }
-
-int get_dev_info_by_irq( int irq, dev_info_t *pdi)
-{
-
-	if ( irq > highest_subchannel || irq < 0 )
-	{
-		return -ENODEV;
-	}
-	else if ( pdi == NULL )
-	{
-      return -EINVAL;
+#ifdef CONFIG_DEBUG_IO
+						else if (     (pdevstat->flag !=
+						                    (   DEVSTAT_START_FUNCTION
+						                      | DEVSTAT_FINAL_STATUS    ) )
+						           && !(pdevstat->flag &
+						                    DEVSTAT_STATUS_PENDING        ) )
+						{
+							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 */
+#endif
 	}
-	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+					else   // we got it ...
 	{
-		return( -ENODEV);
-	}
-	else
+						if ( !sbuffer )	// switch buffers
 	{
-		pdi->devno = ioinfo[irq]->schib.pmcw.dev;
-      pdi->irq   = irq;
+							/*
+							 * we report back the
+							 *  first hit only
+							 */
+							psid = &isid;
 
-		if ( ioinfo[irq]->ui.flags.oper )
+							if ( ioinfo[irq]->schib.pmcw.pim != 0x80 )
       {
-		   pdi->status = 0;
-			memcpy( &(pdi->sid_data),
-			        &ioinfo[irq]->senseid,
-			        sizeof( senseid_t));
+								sense_ccw[1].cda = (char *)virt_to_phys( psid );
       }
       else
       {
-		   pdi->status = DEVSTAT_NOT_OPER;
-			memcpy( &(pdi->sid_data),
-			        '\0',
-			        sizeof( senseid_t));
-         pdi->sid_data.cu_type = 0xFFFF;
+								sense_ccw[0].cda = (char *)virt_to_phys( psid );
 
       } /* endif */
 
-		if ( ioinfo[irq]->ui.flags.ready )
-			pdi->status |= DEVSTAT_DEVICE_OWNED;
+							/*
+							 * if just the very first
+							 *  was requested to be
+							 *  sensed disable further
+							 *  scans.
+							 */	
+							if ( !lpm )
+								lpm = domask;
 
-		return 0;
+							sbuffer = 1;
 
 	} /* endif */
 
-}
-
-
-int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi)
+					   if ( pdevstat->rescnt < (sizeof( senseid_t) - 8) )
 {
-   int i;
-   int rc = -ENODEV;
+							ioinfo[irq]->ui.flags.esid = 1;
 
-	if ( devno > 0x0000ffff )
-	{
-		return -ENODEV;
-	}
-	else if ( pdi == NULL )
-	{
-      return -EINVAL;
-	}
-	else
-	{
+						} /* endif */
 
-      for ( i=0; i <= highest_subchannel; i++ )
-      {
+						io_retry = 0;
 
-			if (    ioinfo[i] != INVALID_STORAGE_AREA
-			     && ioinfo[i]->schib.pmcw.dev == devno )
-         {
-				if ( ioinfo[i]->ui.flags.oper )
-            {
-		         pdi->status = 0;
-               pdi->irq    = i;
-		         pdi->devno  = devno;
+					} /* endif */
 
-	   	      memcpy( &(pdi->sid_data),
-					        &ioinfo[i]->senseid,
-                       sizeof( senseid_t));
-            }
-            else
+					if ( io_retry )
             {
-		         pdi->status = DEVSTAT_NOT_OPER;
-               pdi->irq    = i;
-               pdi->devno  = devno;
+						retry--;
 	
-	   	      memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t));
-               pdi->sid_data.cu_type = 0xFFFF;
+						if ( retry == 0 )
+						{
+							io_retry = 0;
 
             } /* endif */
 
-				if ( ioinfo[i]->ui.flags.ready )
-					pdi->status |= DEVSTAT_DEVICE_OWNED;
+					} /* endif */
 
-            rc = 0; /* found */
-            break;
+				} while ( (io_retry) );
 
-         } /* endif */
+ 			} /* endif - domask */
 
       } /* endfor */
 
-		return( rc);
+		s390irq_spin_unlock( irq);
 
-	} /* endif */
+		/*
+		 * 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 */
 
-int get_irq_by_devno( unsigned int devno )
+		if ( sid->cu_type == 0xFFFF )
 {
-   int i;
-   int rc = -1;
+			/*
+			 * 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
+			ioinfo[irq]->ui.flags.oper = 0;
 
-	if ( devno <= 0x0000ffff )
+		} /* endif */
+
+		/*
+		 * Issue device info message if unit was operational .
+		 */
+		if ( ioinfo[irq]->ui.flags.oper )
 	{
-      for ( i=0; i <= highest_subchannel; i++ )
+			if ( sid->dev_type != 0 )
       {
-			if (    (ioinfo[i] != INVALID_STORAGE_AREA )
-			     && (ioinfo[i]->schib.pmcw.dev == devno)
-			     && (ioinfo[i]->schib.pmcw.dnv == 1    ) )
+				printk( "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
          {
-            rc = i;
-            break;
+				printk( "SenseID : device %04X reports:"
+				        " Dev Type/Mod = %04X/%02X\n",
+				        ioinfo[irq]->schib.pmcw.dev,
+				        sid->cu_type,
+				        sid->cu_model);
 
          } /* 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;
+		if ( ioinfo[irq]->ui.flags.oper )
+			irq_ret = 0;
+		else
+			irq_ret = -ENODEV;
 
 	} /* 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;
+   return( irq_ret );
 }
 
-/*
- * s390_device_recognition
- *
- * Used for system wide device recognition. Issues the device
- *  independant SenseID command to obtain info the device type.
- *
- */
-void s390_device_recognition( void)
+static int __inline__ s390_SetMultiPath( int irq )
 {
+	int cc;
 
-	int irq = 0; /* let's start with subchannel 0 ... */
+	cc = stsch( irq, &ioinfo[irq]->schib );
 
-	do
-	{
-      /*
-       * 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     ) )
+	if ( !cc )
       {
-			s390_SenseID( irq, &ioinfo[irq]->senseid );
+		ioinfo[irq]->schib.pmcw.mp = 1;     /* multipath mode */
 
-      } /* endif */
-
-		irq ++;
+		cc = msch( irq, &ioinfo[irq]->schib );
 
-	} while ( irq <= highest_subchannel );
+	} /* endif */
 
+	return( cc);
 }
 
-
 /*
- * s390_search_devices
+ * Device Path Verification
  *
- * Determines all subchannels available to the system.
+ * 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.
  *
  */
-void s390_process_subchannels( void)
+int s390_DevicePathVerification( int irq, __u8 usermask )
 {
-   int   isValid;
-	int   irq = 0;   /* Evaluate all subchannels starting with 0 ... */
+#if 1
+	int  ccode;
+	__u8 pathmask;
+	__u8 domask;
 
-	do
-	{
-		isValid = s390_validate_subchannel( irq);
+	int ret = 0;
 
-      irq++;
+	if ( ioinfo[irq]->ui.flags.pgid_supp == 0 )
+	{
+		return( 0);	// just exit ...
 
-  	} while ( isValid && irq < __MAX_SUBCHANNELS );
+	} /* endif */
 
-	highest_subchannel = --irq;
+	ccode = stsch( irq, &(ioinfo[irq]->schib) );
 
-	printk( "\nHighest subchannel number detected: %u\n",
-	        highest_subchannel);
+	if ( ccode )
+	{
+		ret = -ENODEV;
 }
-
+	else if ( ioinfo[irq]->schib.pmcw.pim == 0x80 )
+	{
 /*
- * s390_validate_subchannel()
- *
- * Process the subchannel for the requested irq. Returns 1 for valid
- *  subchannels, otherwise 0.
+		 * no error, just not required for single path only devices
  */
-int s390_validate_subchannel( int irq )
+		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;
 
-   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;
-
-	/*
-	 * 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 ) )
+		if ( usermask )
 	{
-		p_schib = &ioinfo[irq]->schib;
+			dev_path = usermask;
 	}
 	else
 	{
-		p_schib = &init_schib;
+			dev_path = ioinfo[irq]->opm;
 
 	} /* endif */
 
-	ccode = stsch( irq, p_schib);
-
-   if ( ccode == 0)
-   {
    	/*
-	    * ... just being curious we check for non I/O subchannels
+		 * let's build a path group ID if we don't have one yet
 		 */
-		if ( p_schib->pmcw.st )
+		if ( ioinfo[irq]->ui.flags.pgid == 0)
 		{
-			printk( "Subchannel %04X reports "
-			        "non-I/O subchannel type %04X\n",
-			        irq,
-			        p_schib->pmcw.st);
+			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;
 
-			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
-				ioinfo[irq]->ui.flags.oper = 0;
+			ioinfo[irq]->ui.flags.pgid  = 1;
 
 		} /* endif */
 
-		if ( p_schib->pmcw.dnv )
-		{
-			if ( ioinfo[irq] == INVALID_STORAGE_AREA )
-			{	
+		memcpy( &pgid, &ioinfo[irq]->pgid, sizeof(pgid_t));
 
-				if ( !init_IRQ_complete )
+		for ( i = 0; i < 8 && !ret ; i++)
 		{
-					ioinfo[irq] =
-					   (ioinfo_t *)alloc_bootmem( sizeof(ioinfo_t));
-				}
-				else
+			pathmask = 0x80 >> i;						
+
+			domask = dev_path & pathmask;
+
+			if ( domask )
 				{
-					ioinfo[irq] =
-					   (ioinfo_t *)kmalloc( sizeof(ioinfo_t),
-				                           GFP_KERNEL );
+				ret = s390_SetPGID( irq, domask, &pgid );
 
-				} /* endif */
+				/*
+				 * 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;
 
-				memset( ioinfo[irq], '\0', sizeof( ioinfo_t));
-				memcpy( &ioinfo[irq]->schib,
-			           &init_schib,
-			           sizeof( schib_t));
-				ioinfo[irq]->irq_desc.status  = IRQ_DISABLED;
-				ioinfo[irq]->irq_desc.handler = &no_irq_type;
+					ret   = s390_SensePGID( irq, domask, &pgid);
+					first = 0;
 			
+					if ( ret == 0 )
+					{
 			/*
-				 * We have to insert the new ioinfo element
-				 *  into the linked list, either at its head,
-				 *  its tail or insert it.
+						 * Check whether we retrieved
+						 *  a reasonable PGID ...
 			 */
-				if ( ioinfo_head == NULL )  /* first element */
+						if ( pgid.inf.ps.state1 == SNID_STATE1_GROUPED )
 				{
-					ioinfo_head = ioinfo[irq];
-					ioinfo_tail = ioinfo[irq];
+							memcpy( &(ioinfo[irq]->pgid),
+							        &pgid,
+							        sizeof(pgid_t) );
 		}
-				else if (irq < ioinfo_head->irq) /* new head */
+						else // ungrouped or garbage ...
 				{
-					ioinfo[irq]->next = ioinfo_head;
-					ioinfo_head->prev = ioinfo[irq];
-					ioinfo_head       = ioinfo[irq];
+							ret = -EOPNOTSUPP;
+
+						} /* endif */
 				}
-				else if (irq > ioinfo_tail->irq) /* new tail */
+					else
 				{
-					ioinfo_tail->next = ioinfo[irq];
-					ioinfo[irq]->prev = ioinfo_tail;
-					ioinfo_tail       = ioinfo[irq];
+						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
+
+					} /* endif */
 				}
-				else /* insert element */
+				else if ( ret )
 				{
-					ioinfo_t *pi = ioinfo_head;
 
-					do              
-					{
-						if ( irq < pi->next->irq )
-		{
-							ioinfo[irq]->next = pi->next;
-							ioinfo[irq]->prev = pi;
-							pi->next->prev    = ioinfo[irq];
-							pi->next          = ioinfo[irq];
-							break;
+#ifdef CONFIG_DEBUG_IO
+						printk( "PathVerification(%04X) "
+						        "- Device %04X doesn't "
+						        " support path grouping\n",
+						        irq,
+						        ioinfo[irq]->schib.pmcw.dev);
 						
-						} /* endif */
+#endif
 
-						pi = pi->next;
+					ioinfo[irq]->ui.flags.pgid_supp = 0;
 
-					} while ( 1 );
+				} /* endif */
       	
 				} /* endif */
 
+		} /* endfor */
+
 			} /* endif */
 
-			printk( "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);
+	return ret;
+#else
+	return 0;
+#endif
+}
 
 			/*
-			 * We now have to initially ...
-			 *  ... set "interruption sublass"
-			 *  ... enable "concurrent sense"
-			 *  ... enable "multipath mode" if more than
-			 *        one CHPID is available
+ * s390_SetPGID
+ *
+ * Set Path Group ID
 			 *
-			 * 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     = 0; /* force disable it */
-			ioinfo[irq]->schib.pmcw.intparm =
-			                     ioinfo[irq]->schib.pmcw.dev;
+int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid )
+			{
+	ccw1_t     spid_ccw[2]; /* ccw area for SPID 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() */
+	int        mpath   = 1; /* try multi-path first */
+
+	if ( (irq > highest_subchannel) || (irq < 0 ) )
+	{
+		return( -ENODEV );
 
-			if (    ( ioinfo[irq]->schib.pmcw.pim != 0    )
-			     && ( ioinfo[irq]->schib.pmcw.pim != 0x80 ) )
+	}
+	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 			{
-				ioinfo[irq]->schib.pmcw.mp     = 1;     /* multipath mode */
+		return( -ENODEV);
 
-			} /* endif */
+	} /* endif */
 
-			/*
-			 * initialize ioinfo structure
-		    */
-			ioinfo[irq]->irq             = irq;
-			ioinfo[irq]->ui.flags.busy   = 0;
-			ioinfo[irq]->ui.flags.ready  = 0;
-			ioinfo[irq]->ui.flags.oper   = 1;
-			ioinfo[irq]->devstat.intparm = 0;
-			ioinfo[irq]->devstat.devno   = ioinfo[irq]->schib.pmcw.dev;
+	if ( ioinfo[irq]->ui.flags.oper == 0 )
+	{
+		return( -ENODEV );
 
-         retry = 5;
+	} /* endif */
 
-         do
-			{
-				ccode2 = msch_err( irq, &ioinfo[irq]->schib);
+	if ( !ioinfo[irq]->ui.flags.ready )
+	{
+		/*
+		 * 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,
+		                       0,
+		                       "SPID",
+		                       pdevstat);
 
-            switch (ccode2) {
-            case 0:  // successful completion
-			      //
-				   // concurrent sense facility available ...
-				   //
-					ioinfo[irq]->ui.flags.consns = 1;
-               break;
+		if ( irq_ret == 0 )
+			inlreq = 1;
+   }
+	else
+	{
+		pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
 
-            case 1:  // status pending
-               //
-               // How can we have a pending status as device is
-               //  disabled for interrupts ? Anyway, clear it ...
-               //
-					tsch( irq, &(ioinfo[irq]->devstat.ii.irb) );
-               retry--;
-               break;
+	} /* endif */
 
-            case 2:  // busy
-               retry--;
-               break;
+	if ( irq_ret == 0 )
+				   {
+		s390irq_spin_lock( irq);
 
-            case 3:  // not operational
-					ioinfo[irq]->ui.flags.oper = 0;
-               retry                     = 0;
-               break;
+		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      = (char *)virt_to_phys( pgid );
+		spid_ccw[1].count    = sizeof( pgid_t);
+		spid_ccw[1].flags    = CCW_FLAG_SLI;
 
-            default:
-#define PGMCHK_OPERAND_EXC      0x15
+		pgid->inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH;
 
-				   if ( (ccode2 & PGMCHK_OPERAND_EXC) == PGMCHK_OPERAND_EXC )
-				   {
 						/*
-						 * re-issue the modify subchannel without trying to
-						 *  enable the concurrent sense facility
+		 * We now issue a SenseID request. In case of BUSY
+		 *  or STATUS PENDING conditions we retry 5 times.
 						 */
-						ioinfo[irq]->schib.pmcw.csense = 0;
+		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 );
 
-						ccode2 = msch_err( irq, &ioinfo[irq]->schib);
+			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
+				} /* endif */
 
-						if ( ccode2 != 0 )
+				if ( pdevstat->flag == (   DEVSTAT_START_FUNCTION
+				                         | DEVSTAT_FINAL_STATUS   ) )
 						{
-						   printk( " ... modify subchannel (2) failed with CC = %X\n",
-						           ccode2 );
-							ioinfo[irq]->ui.flags.oper = 0;
+					retry = 0;	// successfully set ...
+				}
+				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--;
 						}
 						else
 						{
-							ioinfo[irq]->ui.flags.consns = 0;
+							irq_ret = -EOPNOTSUPP;
+							retry   = 0;			
 
 						} /* endif */
 					}
+#ifdef CONFIG_DEBUG_IO
 					else
 					{
-						printk( " ... modify subchannel (1) failed with CC = %X\n",
-						        ccode2);
-						ioinfo[irq]->ui.flags.oper = 0;
+						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 */
+#endif
+				}
+				else if ( pdevstat->flag & DEVSTAT_NOT_OPER )
+				{
+					printk( "SPID - Device %04X "
+					        "on Subchannel %04X "
+					        "became 'not operational'\n",
+					        ioinfo[irq]->schib.pmcw.dev,
+					        irq);
 
 					retry  = 0;
-					break;
-
-				} /* endswitch */
 
-			} while ( ccode2 && retry );
-
-         if ( (ccode2 < 3) && (!retry) )
+				} /* endif */
+			}
+			else if ( irq_ret != -ENODEV )
          {
-			   printk( " ... msch() retry count for "
-                    "subchannel %04X exceeded, CC = %d\n",
-                    irq,
-                    ccode2);
+				retry--;
+			}
+			else
+			{
+				retry = 0;
 
          } /* endif */
 
-      } /* endif */
+		} while ( retry > 0 );
 
-   } /* endif */
+		s390irq_spin_unlock( irq);
 
    /*
-	 * indicate whether the subchannel is valid
+		 * If we installed the irq action handler we have to
+		 *  release it too.
     */
-   if ( ccode == 3)
-      return(0);
-   else
-      return(1);
+		if ( inlreq )
+			free_irq( irq, pdevstat);
+
+	} /* endif */
+
+   return( irq_ret );
 }
 
+
 /*
- * s390_SenseID
+ * s390_SensePGID
  *
- * Try to obtain the 'control unit'/'device type' information
- *  associated with the subchannel.
+ * Sense Path Group ID
  *
- * 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 )
+int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid )
 {
-	ccw1_t     sense_ccw;   /* ccw area for SenseID command */
+	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 */
@@ -3504,6 +5170,7 @@
 	else if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 	{
 		return( -ENODEV);
+
 	} /* endif */
 
 	if ( ioinfo[irq]->ui.flags.oper == 0 )
@@ -3520,26 +5187,30 @@
        *  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, 0, "SID", &devstat);
+		irq_ret = request_irq( irq,
+		                       init_IRQ_handler,
+		                       0,
+		                       "SNID",
+		                       pdevstat);
 
 		if ( irq_ret == 0 )
 			inlreq = 1;
 
+   }
+	else
+	{
+		pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
+
 	} /* endif */
 
 	if ( irq_ret == 0 )
    {
 		s390irq_spin_lock( irq);
 
-		sense_ccw.cmd_code = CCW_CMD_SENSE_ID;
-		sense_ccw.cda      = (char *)virt_to_phys( sid );
-		sense_ccw.count    = sizeof( senseid_t);
-		sense_ccw.flags    = CCW_FLAG_SLI;
-
-		ioinfo[irq]->senseid.cu_type    = 0xFFFF;  /* initialize fields ... */
-		ioinfo[irq]->senseid.cu_model   = 0;
-		ioinfo[irq]->senseid.dev_type   = 0;
-		ioinfo[irq]->senseid.dev_model  = 0;
+		snid_ccw.cmd_code = CCW_CMD_SENSE_PGID;
+		snid_ccw.cda      = (char *)virt_to_phys( pgid );
+		snid_ccw.count    = sizeof( pgid_t);
+		snid_ccw.flags    = CCW_FLAG_SLI;
 
 		/*
 		 * We now issue a SenseID request. In case of BUSY
@@ -3547,78 +5218,100 @@
 		 */
 		do
 		{
-			memset( &devstat, '\0', sizeof( devstat_t) );
+			memset( pdevstat, '\0', sizeof( devstat_t) );
 
 			irq_ret = s390_start_IO( irq,
-			                         &sense_ccw,
-			                         0x00E2C9C4,  // == SID
-			                         0,           // n/a
-			                         DOIO_WAIT_FOR_INTERRUPT );
-
-			if ( sid->cu_type == 0xFFFF )
-			{
-				if ( devstat.flag & DEVSTAT_STATUS_PENDING )
-				{
-#if CONFIG_DEBUG_IO
-					printk( "Device %04X on Subchannel %04X "
-					        "reports pending status, retry : %d\n",
-					        ioinfo[irq]->schib.pmcw.dev,
-					        irq,
-					        retry);
-#endif
-				} /* endif */
+			                         &snid_ccw,
+			                         0xE2D5C9C4,  // == SNID
+			                         lpm,         // n/a
+			                         DOIO_WAIT_FOR_INTERRUPT
+			                          | DOIO_VALID_LPM
+			                          | DOIO_DONT_CALL_INTHDLR );
 
-				if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL )
+			if ( irq_ret == 0 )
+				{
+				if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
 				{
 					/*
-					 * if the device doesn't support the SenseID
-					 *  command further retries wouldn't help ...
+					 * If the device doesn't support the
+					 *  Sense Path Group ID command
+					 *  further retries wouldn't help ...
 					 */
-					if ( devstat.ii.sense.data[0] == SNS0_CMD_REJECT )
+					if ( pdevstat->ii.sense.data[0] & SNS0_CMD_REJECT )
 					{
 						retry = 0;
+						irq_ret = -EOPNOTSUPP;
 					}
-#if CONFIG_DEBUG_IO
 					else
 					{
-						printk( "Device %04X,"
-						        " UC/SenseID,"
+#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,
-						        devstat.scnt,
-						        devstat.ii.sense.data[0],
-						        devstat.ii.sense.data[1],
-						        devstat.ii.sense.data[2],
-						        devstat.ii.sense.data[3],
-						        devstat.ii.sense.data[4],
-						        devstat.ii.sense.data[5],
-						        devstat.ii.sense.data[6],
-						        devstat.ii.sense.data[7]);
+						        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 */
 #endif
+						retry--;
+
+					} /* endif */
 				}
-				else if ( devstat.flag & DEVSTAT_NOT_OPER )
+				else if ( pdevstat->flag & DEVSTAT_NOT_OPER )
 				{
-					printk( "Device %04X on Subchannel %04X "
+					printk( "SNID - Device %04X "
+					        "on Subchannel %04X "
 					        "became 'not operational'\n",
 					        ioinfo[irq]->schib.pmcw.dev,
 					        irq);
 
 					retry = 0;
 
-				} /* endif */
 			}
-			else   // we got it ...
+				else
 			{
-				retry = 0;
+					retry = 0; // success ...
 
 			} /* endif */
+			}
+			else if ( irq_ret != -ENODEV ) // -EIO, or -EBUSY
+			{
+#ifdef CONFIG_DEBUG_IO
+				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
+				{
+					printk( "SNID - Device %04X "
+					        "on Subchannel %04X "
+					        "reports pending status, "
+					        "retry : %d\n",
+					        ioinfo[irq]->schib.pmcw.dev,
+					        irq,
+					        retry);
+				} /* endif */
+#endif
 
+				printk( "SNID - device %04X,"
+				        " start_io() reports rc : %d, retrying ...\n",
+				        ioinfo[irq]->schib.pmcw.dev,
+				        irq_ret);
 			retry--;
+			}
+			else	// -ENODEV ...
+			{
+				retry = 0;
+
+			} /* endif */
 
 		} while ( retry > 0 );
 
@@ -3629,75 +5322,206 @@
 		 *  release it too.
 		 */
 		if ( inlreq )
-			free_irq( irq, &devstat);
+			free_irq( irq, pdevstat);
+
+	} /* endif */
+
+   return( irq_ret );
+}
 
 		/*
-		 * if running under VM check there ... perhaps we should do
-		 *  only if we suffered a command reject, but it doesn't harm
+ * 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
 		 */
-		if (    ( sid->cu_type == 0xFFFF )
-		     && ( MACHINE_IS_VM              ) )
+void s390_do_crw_pending( crwe_t *pcrwe )
 		{
-			VM_virtual_device_info( ioinfo[irq]->schib.pmcw.dev,
-			                        sid );
-		} /* endif */
+	int irq;
+	int dev_oper = 0;
+	int dev_no   = -1;	
+	int lock     = 0;
 
-		if ( sid->cu_type == 0xFFFF )
+#ifdef CONFIG_DEBUG_CRW
+	printk( "do_crw_pending : starting ...\n");
+#endif
+
+	while ( pcrwe != NULL )
 		{
+		switch ( pcrwe->crw.rsc ) {	
+		case CRW_RSC_SCH :
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : source is "
+			        "subchannel\n");
+#endif
+			irq = pcrwe->crw.rsid;
+
 			/*
-			 * 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".
+			 * If the device isn't known yet
+			 *   we can't lock it ...
 			 */
-			printk( "Unknown device %04X on subchannel %04X\n",
-			        ioinfo[irq]->schib.pmcw.dev,
-			        irq);
-			ioinfo[irq]->ui.flags.oper = 0;
+	      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;
 
 		} /* endif */
 
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : subchannel validation - start ...\n");
+#endif
+			s390_validate_subchannel( irq, 0 );
+
+			if ( irq > highest_subchannel )
+				highest_subchannel = irq;
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : subchannel validation - done\n");
+#endif
 		/*
-		 * Issue device info message if unit was operational .
+			 * After the validate processing
+			 *   the ioinfo control block
+			 *   should be allocated ...
 		 */
-		if ( ioinfo[irq]->ui.flags.oper )
+			if ( lock )
 		{
-			if ( sid->dev_type != 0 )
+				s390irq_spin_unlock( irq );
+
+			} /* endif */
+
+#ifdef CONFIG_DEBUG_CRW
+	      if ( ioinfo[irq] != INVALID_STORAGE_AREA )
 			{
-				printk( "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);
+				printk( "do_crw_pending : ioinfo at %08X\n",
+				        (unsigned)ioinfo[irq]);
+
+			} /* endif */
+#endif
+
+	      if ( ioinfo[irq] != INVALID_STORAGE_AREA )
+			{
+				if ( ioinfo[irq]->ui.flags.oper == 0 )
+				{
+					/*
+					 * If the device has gone
+					 *  call not oper handler        	
+					 */       	
+					if (    (             dev_oper == 1    )
+					     && ( ioinfo[irq]->nopfunc != NULL ) )
+					{
+                  free_irq( irq,
+						          ioinfo[irq]->irq_desc.action->dev_id );
+						ioinfo[irq]->nopfunc( irq,
+						                      DEVSTAT_DEVICE_GONE );				
+
+					} /* endif */
 			}
 			else
 			{
-				printk( "Device %04X reports:"
-				        " Dev Type/Mod = %04X/%02X\n",
-				        ioinfo[irq]->schib.pmcw.dev,
-				        sid->cu_type,
-				        sid->cu_model);
+#ifdef CONFIG_DEBUG_CRW
+					printk( "do_crw_pending : device "
+					        "recognition - start ...\n");
+#endif
+					s390_device_recognition_irq( irq );
+
+#ifdef CONFIG_DEBUG_CRW
+					printk( "do_crw_pending : device "
+					        "recognition - done\n");
+#endif
+		
+					/*
+					 * 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 */
+					}
+					/*
+					 * ... it is and was operational, but
+					 *      the devno may have changed
+					 */
+					else if ( ioinfo[irq]->devno != dev_no )   					
+					{
+						ioinfo[irq]->nopfunc( irq,
+						                      DEVSTAT_REVALIDATE );				
 
 		} /* endif */
 
-		if ( ioinfo[irq]->ui.flags.oper )
-			irq_ret = 0;
-		else
-			irq_ret = -ENODEV;
+				} /* endif */
 
 	} /* endif */
 
-   return( irq_ret );
-}
+			break;
 
-void do_crw_pending(void)
-{
+		case CRW_RSC_MONITOR :
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : source is "
+			        "monitoring facility\n");
+#endif
+			break;
+
+		case CRW_RSC_CPATH :   	
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : source is "
+			        "channel path\n");
+#endif
+			break;
+
+		case CRW_RSC_CONFIG : 	
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : source is "
+			        "configuration-alert facility\n");
+#endif
+			break;
+
+		case CRW_RSC_CSS :
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : source is "
+			        "channel path\n");
+#endif
+			break;
+
+		default :
+
+#ifdef CONFIG_DEBUG_CRW
+			printk( "do_crw_pending : unknown source\n");
+#endif
+			break;		
+
+		} /* endswitch */
+
+		pcrwe = pcrwe->crwe_next;
+
+	} /* endwhile */
+
+#ifdef CONFIG_DEBUG_CRW
+	printk( "do_crw_pending : done\n");
+#endif
+
+   return;
 }
+
 
 /* added by Holger Smolinski for reipl support in reipl.S */
 void 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)