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
- Lines: 6191
- Date:
Wed Jun 7 14:26:42 2000
- Orig file:
v2.2.15/linux/arch/s390/kernel/s390io.c
- Orig date:
Wed May 3 17:16:31 2000
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, ®s );
+
+ } /* 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, ®s );
+
+ } /* 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,
+ ®s );
+
+ } /* 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, ®s );
+
+ } /* 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, ®s);
+ 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, ®s);
+ ps->cu_type = 0x3480;
- } /* endif */
+ break;
- } /* endif */
+ case 0x42:
- }
- else
- {
- ioinfo[irq]->ui.flags.w4final = 1;
- action->handler( irq, action->dev_id, ®s);
+ 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, ®s);
+ 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)