/*
* drivers/s390/s390io.c
* S/390 common I/O routines
* $Revision: 1.258 $
*
* S390 version
* Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
* ChangeLog: 01/07/2001 Blacklist cleanup (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
* 01/04/2001 Holger Smolinski (smolinsk@de.ibm.com)
* Fixed lost interrupts and do_adapter_IO
* xx/xx/xxxx nnn multiple changes not reflected
* 03/12/2001 Ingo Adlung blacklist= - changed to cio_ignore=
* 03/14/2001 Ingo Adlung disable interrupts before start_IO
* in Path Group processing
* decrease retry2 on busy while
* disabling sync_isc; reset isc_cnt
* on io error during sync_isc enablement
* 05/09/2001 Cornelia Huck added exploitation of debug feature
* 05/16/2001 Cornelia Huck added /proc/deviceinfo/<devno>/
* 05/22/2001 Cornelia Huck added /proc/cio_ignore
* un-ignore blacklisted devices by piping
* to /proc/cio_ignore
* xx/xx/xxxx some bugfixes & cleanups
* 08/02/2001 Cornelia Huck not already known devices can be blacklisted
* by piping to /proc/cio_ignore
* 09/xx/2001 couple more fixes
* 10/15/2001 Cornelia Huck xsch - internal only for now
* 10/29/2001 Cornelia Huck Blacklisting reworked again
* 10/29/2001 Cornelia Huck improved utilization of debug feature
* 10/29/2001 Cornelia Huck more work on cancel_IO - use the flag
* DOIO_CANCEL_ON_TIMEOUT in do_IO to get
* io cancelled
* 11/15/2001 Cornelia Huck proper behaviour with procfs off
* 12/10/2001 Cornelia Huck added private_data + functions to
* ioinfo_t
* 11-12/2001 Cornelia Huck various cleanups
* 01/09/2002 Cornelia Huck PGID fixes
* process css machine checks
* 01/10/2002 Cornelia Huck added /proc/chpids
* 04/10/2002 Cornelia Huck fixed reaction on css machine checks
* 04/23/2002 Cornelia Huck fixed console isc (un)setting
* 06/06/2002 Cornelia Huck added detection of locked devices
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/threads.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/ctype.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/smp.h>
#include <asm/pgtable.h>
#include <asm/delay.h>
#include <asm/processor.h>
#include <asm/lowcore.h>
#include <asm/idals.h>
#include <asm/uaccess.h>
#include <asm/cpcmd.h>
#include <asm/s390io.h>
#include <asm/s390dyn.h>
#include <asm/s390mach.h>
#include <asm/debug.h>
#include <asm/queue.h>
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#define SANITY_CHECK(irq) do { \
if (irq > highest_subchannel || irq < 0) \
return (-ENODEV); \
if (ioinfo[irq] == INVALID_STORAGE_AREA) \
return (-ENODEV); \
if (ioinfo[irq]->st) \
return -ENODEV; \
} while(0)
#define CIO_TRACE_EVENT(imp, txt) do { \
if (cio_debug_initialized) \
debug_text_event(cio_debug_trace_id, \
imp, \
txt); \
}while (0)
#define CIO_MSG_EVENT(imp, args...) do { \
if (cio_debug_initialized) \
debug_sprintf_event(cio_debug_msg_id, \
imp, \
##args); \
} while (0)
#define CIO_CRW_EVENT(imp, args...) do { \
if (cio_debug_initialized) \
debug_sprintf_event(cio_debug_crw_id, \
imp, \
##args); \
} while (0)
#define CIO_HEX_EVENT(imp, args...) do { \
if (cio_debug_initialized) \
debug_event(cio_debug_trace_id, imp, ##args); \
} while (0)
#undef CONFIG_DEBUG_IO
#define CONFIG_DEBUG_CRW
#define CONFIG_DEBUG_CHSC
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
};
#ifdef CONFIG_CHSC
__u64 chpids[4] = {0,0,0,0};
__u64 chpids_logical[4] = {-1,-1,-1,-1};
__u64 chpids_known[4] = {0,0,0,0};
#endif /* CONFIG_CHSC */
static atomic_t sync_isc = ATOMIC_INIT (-1);
static int sync_isc_cnt = 0; /* synchronous irq processing lock */
static spinlock_t adapter_lock = SPIN_LOCK_UNLOCKED; /* adapter interrupt lock */
static int cons_dev = -1; /* identify console device */
static int init_IRQ_complete = 0;
static int cio_show_msg = 0;
static schib_t *p_init_schib = NULL;
static irb_t *p_init_irb = NULL;
static __u64 irq_IPL_TOD;
static adapter_int_handler_t adapter_handler = NULL;
static pgid_t * global_pgid;
/* for use of debug feature */
debug_info_t *cio_debug_msg_id = NULL;
debug_info_t *cio_debug_trace_id = NULL;
debug_info_t *cio_debug_crw_id = NULL;
int cio_debug_initialized = 0;
#ifdef CONFIG_CHSC
int cio_chsc_desc_avail = 0;
int cio_chsc_err_msg = 0;
#endif
static void init_IRQ_handler (int irq, void *dev_id, struct pt_regs *regs);
static void s390_process_subchannels (void);
static void s390_device_recognition_all (void);
static void s390_device_recognition_irq (int irq);
#ifdef CONFIG_PROC_FS
static void s390_redo_validation (void);
#endif
static int s390_validate_subchannel (int irq, int enable);
static int s390_SenseID (int irq, senseid_t * sid, __u8 lpm);
static int s390_SetPGID (int irq, __u8 lpm);
static int s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid);
static int s390_process_IRQ (unsigned int irq);
static int enable_subchannel (unsigned int irq);
static int disable_subchannel (unsigned int irq);
int cancel_IO (int irq);
int s390_start_IO (int irq, ccw1_t * cpa, unsigned long user_intparm,
__u8 lpm, unsigned long flag);
#ifdef CONFIG_PROC_FS
static int chan_proc_init (void);
#endif
static inline void do_adapter_IO (__u32 intparm);
static void s390_schedule_path_verification(unsigned long irq);
int s390_DevicePathVerification (int irq, __u8 domask);
int s390_register_adapter_interrupt (adapter_int_handler_t handler);
int s390_unregister_adapter_interrupt (adapter_int_handler_t handler);
extern int do_none (unsigned int irq, int cpu, struct pt_regs *regs);
extern int enable_none (unsigned int irq);
extern int disable_none (unsigned int irq);
asmlinkage void do_IRQ (struct pt_regs regs);
#ifdef CONFIG_CHSC
static chsc_area_t *chsc_area_ssd = NULL;
static chsc_area_t *chsc_area_sei = NULL;
static spinlock_t chsc_lock_ssd = SPIN_LOCK_UNLOCKED;
static spinlock_t chsc_lock_sei = SPIN_LOCK_UNLOCKED;
static int chsc_get_sch_descriptions( void );
int s390_vary_chpid( __u8 chpid, int on );
#endif
#ifdef CONFIG_PROC_FS
#define MAX_CIO_PROCFS_ENTRIES 0x300
/* magic number; we want to have some room to spare */
int cio_procfs_device_create (int devno);
int cio_procfs_device_remove (int devno);
int cio_procfs_device_purge (void);
#endif
int cio_notoper_msg = 1;
#ifdef CONFIG_PROC_FS
int cio_proc_devinfo = 0; /* switch off the /proc/deviceinfo/ stuff by default
until problems are dealt with */
#endif
unsigned long s390_irq_count[NR_CPUS]; /* trace how many irqs have occured per cpu... */
int cio_count_irqs = 1; /* toggle use here... */
int cio_sid_with_pgid = 0; /* if we need a PGID for SenseID, switch this on */
/*
* "Blacklisting" of certain devices:
* Device numbers given in the commandline as cio_ignore=... won't be known to Linux
* These can be single devices or ranges of devices
*
* 10/23/01 reworked to get rid of lists
*/
static u32 bl_dev[2048];
static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
static int highest_ignored = 0;
static int nr_ignored = 0;
/*
* Function: blacklist_range_add
* Blacklist the devices from-to
*/
static inline void
blacklist_range_add (int from, int to, int locked)
{
unsigned long flags;
int i;
if ((to && (from > to))
|| (to<0) || (to > 0xffff)
|| (from<0) || (from > 0xffff))
return;
if (!locked)
spin_lock_irqsave (&blacklist_lock, flags);
if (!to)
to = from;
for (i = from; i <= to; i++) {
if (!test_and_set_bit (i, &bl_dev))
nr_ignored++;
}
if (to >= highest_ignored)
highest_ignored = to;
if (!locked)
spin_unlock_irqrestore (&blacklist_lock, flags);
}
/*
* Function: blacklist_range_remove
* Removes a range from the blacklist chain
*/
static inline void
blacklist_range_remove (int from, int to)
{
long flags;
int i;
if ((to && (from > to))
|| (to<0) || (to > 0xffff)
|| (from<0) || (from > 0xffff))
return;
spin_lock_irqsave (&blacklist_lock, flags);
for (i = from; i <= to; i++) {
if (test_and_clear_bit (i, &bl_dev))
nr_ignored--;
}
if (to == highest_ignored)
for (highest_ignored = from; (highest_ignored > 0)
&& (!test_bit (highest_ignored, &bl_dev));
highest_ignored--) ;
spin_unlock_irqrestore (&blacklist_lock, flags);
}
/* Parsing the commandline for blacklist parameters */
/*
* Variable to hold the blacklisted devices given by the parameter line
* cio_ignore=...
*/
char *blacklist[256] = { NULL, };
/*
* Get the cio_ignore=... items from the parameter line
*/
static void
blacklist_split_parm_string (char *str)
{
char *tmp = str;
int count = 0;
do {
char *end;
int len;
end = strchr (tmp, ',');
if (end == NULL) {
len = strlen (tmp) + 1;
} else {
len = (long) end - (long) tmp + 1;
*end = '\0';
end++;
}
blacklist[count] = alloc_bootmem (len * sizeof (char));
if (blacklist == NULL) {
printk (KERN_WARNING
"can't store cio_ignore= parameter no %d\n",
count + 1);
break;
}
memset (blacklist[count], 0, len * sizeof (char));
memcpy (blacklist[count], tmp, len * sizeof (char));
count++;
tmp = end;
} while (tmp != NULL && *tmp != '\0');
}
/*
* The blacklist parameters as one concatenated string
*/
static char blacklist_parm_string[1024] __initdata = { 0, };
/*
* function: blacklist_strtoul
* Strip leading '0x' and interpret the values as Hex
*/
static inline int
blacklist_strtoul (char *str, char **stra)
{
if (*str == '0') {
str++; /* strip leading zero */
if (*str == 'x')
str++; /* strip leading x */
}
return simple_strtoul (str, stra, 16); /* interpret anything as hex */
}
/*
* Function: blacklist_parse
* Parse the parameters given to cio_ignore=...
* Add the blacklisted devices to the blacklist chain
*/
static inline void
blacklist_parse (char **str)
{
char *temp;
int from, to;
while (*str) {
temp = *str;
from = 0;
to = 0;
from = blacklist_strtoul (temp, &temp);
if (*temp == '-') {
temp++;
to = blacklist_strtoul (temp, &temp);
}
blacklist_range_add (from, to, 0);
#ifdef CONFIG_DEBUG_IO
printk (KERN_INFO "Blacklisted range from %X to %X\n", from,
to);
#endif
str++;
}
}
/*
* Initialisation of blacklist
*/
void __init
blacklist_init (void)
{
#ifdef CONFIG_DEBUG_IO
printk (KERN_DEBUG "Reading blacklist...\n");
#endif
CIO_MSG_EVENT(6, "Reading blacklist\n");
blacklist_split_parm_string (blacklist_parm_string);
blacklist_parse (blacklist);
}
/*
* Get all the blacklist parameters from parameter line
*/
void __init
blacklist_setup (char *str, int *ints)
{
int len = strlen (blacklist_parm_string);
if (len != 0) {
strcat (blacklist_parm_string, ",");
}
strcat (blacklist_parm_string, str);
}
int __init
blacklist_call_setup (char *str)
{
int dummy;
#ifdef CONFIG_DEBUG_IO
printk (KERN_DEBUG "Reading blacklist parameters...\n");
#endif
CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
blacklist_setup (str, &dummy);
/* Blacklist ranges must be ready when device recognition starts */
blacklist_init ();
return 1;
}
__setup ("cio_ignore=", blacklist_call_setup);
/* Checking if devices are blacklisted */
/*
* Function: is_blacklisted
* Returns 1 if the given devicenumber can be found in the blacklist, otherwise 0.
*/
static inline int
is_blacklisted (int devno)
{
long flags;
int retval = 0;
spin_lock_irqsave (&blacklist_lock, flags);
if (test_bit (devno, &bl_dev))
retval = 1;
spin_unlock_irqrestore (&blacklist_lock, flags);
return retval;
}
/*
* Function: blacklist_free_all_ranges
* set all blacklisted devices free...
*/
void
blacklist_free_all_ranges (void)
{
unsigned long flags;
int i;
spin_lock_irqsave (&blacklist_lock, flags);
for (i = 0; i <= highest_ignored; i++)
clear_bit (i, &bl_dev);
highest_ignored = 0;
nr_ignored = 0;
spin_unlock_irqrestore (&blacklist_lock, flags);
}
#ifdef CONFIG_PROC_FS
/*
* Function: blacklist_parse_proc_parameters
* parse the stuff which is piped to /proc/cio_ignore
*/
void
blacklist_parse_proc_parameters (char *buf)
{
int i;
int from = 0;
int to = 0;
long flags;
int err = 0;
if (strstr (buf, "free ")) {
for (i = 0; i < 5; i++) {
buf++;
}
if (strstr (buf, "all")) {
blacklist_free_all_ranges ();
s390_redo_validation ();
} else {
while (*buf != 0 && *buf != '\n') {
if (!isxdigit(*buf)) {
printk(KERN_WARNING "%s: error parsing "
"\"%s\"\n", __FUNCTION__, buf);
return;
}
from = blacklist_strtoul (buf, &buf);
to = (*buf == '-') ?
blacklist_strtoul (buf+1, &buf) : from;
blacklist_range_remove (from, to);
if (*buf == ',')
buf++;
}
s390_redo_validation();
}
} else if (strstr (buf, "add ")) {
for (i = 0; i < 4; i++) {
buf++;
}
while (*buf != 0 && *buf != '\n') {
if (!isxdigit(*buf)) {
printk(KERN_WARNING "%s: error parsing "
"\"%s\"\n", __FUNCTION__, buf);
return;
}
from = blacklist_strtoul (buf, &buf);
to = (*buf == '-') ?
blacklist_strtoul (buf+1, &buf) : from;
spin_lock_irqsave (&blacklist_lock, flags);
/*
* Don't allow for already known devices to be
* blacklisted
* The criterion is a bit dumb, devices which once were
* there but are already gone are also caught...
*/
err = 0;
for (i = 0; i <= highest_subchannel; i++) {
if (ioinfo[i] != INVALID_STORAGE_AREA) {
if (!ioinfo[i]->st)
if ((ioinfo[i]->schib.pmcw.dev >= from)
&& (ioinfo[i]->schib.pmcw.dev <=
to)) {
printk (KERN_WARNING
"cio_ignore: Won't blacklist "
"already known devices, "
"skipping range %x to %x\n",
from, to);
err = 1;
break;
}
}
}
if (!err)
blacklist_range_add (from, to, 1);
spin_unlock_irqrestore (&blacklist_lock, flags);
if (*buf == ',')
buf++;
}
} else {
printk (KERN_WARNING
"cio_ignore: Parse error; "
"try using 'free all|<devno-range>,<devno-range>,...'\n");
printk (KERN_WARNING
"or 'add <devno-range>,<devno-range>,...'\n");
}
}
#endif
/* End of blacklist handling */
void s390_displayhex (char *str, void *ptr, s32 cnt);
void
s390_displayhex (char *str, void *ptr, s32 cnt)
{
s32 cnt1, cnt2, maxcnt2;
u32 *currptr = (__u32 *) ptr;
printk ("\n%s\n", str);
for (cnt1 = 0; cnt1 < cnt; cnt1 += 16) {
printk ("%08lX ", (unsigned long) currptr);
maxcnt2 = cnt - cnt1;
if (maxcnt2 > 16)
maxcnt2 = 16;
for (cnt2 = 0; cnt2 < maxcnt2; cnt2 += 4)
printk ("%08X ", *currptr++);
printk ("\n");
}
}
static int __init
cio_setup (char *parm)
{
if (!strcmp (parm, "yes")) {
cio_show_msg = 1;
} else if (!strcmp (parm, "no")) {
cio_show_msg = 0;
} else {
printk (KERN_ERR "cio_setup : invalid cio_msg parameter '%s'",
parm);
}
return 1;
}
__setup ("cio_msg=", cio_setup);
static int __init
cio_notoper_setup (char *parm)
{
if (!strcmp (parm, "yes")) {
cio_notoper_msg = 1;
} else if (!strcmp (parm, "no")) {
cio_notoper_msg = 0;
} else {
printk (KERN_ERR
"cio_notoper_setup: "
"invalid cio_notoper_msg parameter '%s'", parm);
}
return 1;
}
__setup ("cio_notoper_msg=", cio_notoper_setup);
#ifdef CONFIG_PROC_FS
static int __init
cio_proc_devinfo_setup (char *parm)
{
if (!strcmp (parm, "yes")) {
cio_proc_devinfo = 1;
} else if (!strcmp (parm, "no")) {
cio_proc_devinfo = 0;
} else {
printk (KERN_ERR
"cio_proc_devinfo_setup: invalid parameter '%s'\n",
parm);
}
return 1;
}
__setup ("cio_proc_devinfo=", cio_proc_devinfo_setup);
#endif
static int __init
cio_pgid_setup (char *parm)
{
if (!strcmp (parm, "yes")) {
cio_sid_with_pgid = 1;
} else if (!strcmp (parm, "no")) {
cio_sid_with_pgid = 0;
} else {
printk (KERN_ERR
"cio_pgid_setup : invalid cio_msg parameter '%s'",
parm);
}
return 1;
}
__setup ("cio_sid_with_pgid=", cio_pgid_setup);
/*
* register for adapter interrupts
*
* With HiperSockets the zSeries architecture provides for
* means of adapter interrups, pseudo I/O interrupts that are
* not tied to an I/O subchannel, but to an adapter. However,
* it doesn't disclose the info how to enable/disable them, but
* to recognize them only. Perhaps we should consider them
* being shared interrupts, and thus build a linked list
* of adapter handlers ... to be evaluated ...
*/
int
s390_register_adapter_interrupt (adapter_int_handler_t handler)
{
int ret = 0;
char dbf_txt[15];
CIO_TRACE_EVENT (4, "rgaint");
spin_lock (&adapter_lock);
if (handler == NULL)
ret = -EINVAL;
else if (adapter_handler)
ret = -EBUSY;
else
adapter_handler = handler;
spin_unlock (&adapter_lock);
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (4, dbf_txt);
return (ret);
}
int
s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
{
int ret = 0;
char dbf_txt[15];
CIO_TRACE_EVENT (4, "urgaint");
spin_lock (&adapter_lock);
if (handler == NULL)
ret = -EINVAL;
else if (handler != adapter_handler)
ret = -EINVAL;
else
adapter_handler = NULL;
spin_unlock (&adapter_lock);
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (4, dbf_txt);
return (ret);
}
static inline void
do_adapter_IO (__u32 intparm)
{
CIO_TRACE_EVENT (4, "doaio");
spin_lock (&adapter_lock);
if (adapter_handler)
(*adapter_handler) (intparm);
spin_unlock (&adapter_lock);
return;
}
void s390_free_irq (unsigned int irq, void *dev_id);
/*
* Note : internal use of irqflags SA_PROBE for NOT path grouping
*
*/
int
s390_request_irq_special (int irq,
io_handler_func_t io_handler,
not_oper_handler_func_t not_oper_handler,
unsigned long irqflags,
const char *devname, void *dev_id)
{
int retval = 0;
unsigned long flags;
char dbf_txt[15];
int retry;
if (irq >= __MAX_SUBCHANNELS)
return -EINVAL;
if (!io_handler || !dev_id)
return -EINVAL;
if (ioinfo[irq] == INVALID_STORAGE_AREA)
return -ENODEV;
if (ioinfo[irq]->st)
return -ENODEV;
sprintf (dbf_txt, "reqsp%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* The following block of code has to be executed atomically
*/
s390irq_spin_lock_irqsave (irq, flags);
if (ioinfo[irq]->ui.flags.unfriendly &&
!(irqflags & SA_FORCE)) {
retval = -EUSERS;
} else if (!ioinfo[irq]->ui.flags.ready) {
retry = 5;
ioinfo[irq]->irq_desc.handler = io_handler;
ioinfo[irq]->irq_desc.name = devname;
ioinfo[irq]->irq_desc.dev_id = dev_id;
ioinfo[irq]->ui.flags.ready = 1;
do {
retval = enable_subchannel (irq);
if (retval) {
ioinfo[irq]->ui.flags.ready = 0;
break;
}
stsch (irq, &ioinfo[irq]->schib);
if (ioinfo[irq]->schib.pmcw.ena)
retry = 0;
else
retry--;
} while (retry);
} else {
/*
* interrupt already owned, and shared interrupts
* aren't supported on S/390.
*/
retval = -EBUSY;
}
s390irq_spin_unlock_irqrestore (irq, flags);
if (retval == 0) {
if (irqflags & SA_DOPATHGROUP) {
ioinfo[irq]->ui.flags.pgid_supp = 1;
ioinfo[irq]->ui.flags.notacccap = 1;
}
if ((irqflags & SA_DOPATHGROUP) &&
(!ioinfo[irq]->ui.flags.pgid ||
irqflags & SA_PROBE)) {
pgid_t pgid;
int i, mask;
/*
* Do an initial SensePGID to find out if device
* is locked by someone else.
*/
memcpy(&pgid, global_pgid, sizeof(pgid_t));
retval = -EAGAIN;
for (i=0; i<8 && retval==-EAGAIN; i++) {
mask = (0x80 >> i) & ioinfo[irq]->opm;
if (!mask)
continue;
retval = s390_SensePGID(irq, mask, &pgid);
if (retval == -EOPNOTSUPP)
/* Doesn't prevent us from proceeding */
retval = 0;
}
}
if (!(irqflags & SA_PROBE) &&
(irqflags & SA_DOPATHGROUP) &&
(!ioinfo[irq]->ui.flags.unfriendly))
s390_DevicePathVerification (irq, 0);
if (ioinfo[irq]->ui.flags.unfriendly &&
!(irqflags & SA_FORCE)) {
/*
* We found out during path verification that the
* device is locked by someone else and we have to
* let the device driver know.
*/
retval = -EUSERS;
free_irq(irq, dev_id);
} else {
ioinfo[irq]->ui.flags.newreq = 1;
ioinfo[irq]->nopfunc = not_oper_handler;
}
}
if (cio_debug_initialized)
debug_int_event (cio_debug_trace_id, 4, retval);
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;
}
return (ret);
}
void
s390_free_irq (unsigned int irq, void *dev_id)
{
unsigned long flags;
int ret;
char dbf_txt[15];
if (irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA)
return;
if (ioinfo[irq]->st)
return;
sprintf (dbf_txt, "free%x", irq);
CIO_TRACE_EVENT (2, dbf_txt);
s390irq_spin_lock_irqsave (irq, flags);
#ifdef CONFIG_KERNEL_DEBUG
if (irq != cons_dev)
printk (KERN_DEBUG "Trying to free IRQ%d\n", irq);
#endif
CIO_MSG_EVENT(2, "Trying to free IRQ %d\n", irq);
/*
* disable the device and reset all IRQ info if
* the IRQ is actually owned by the handler ...
*/
if (ioinfo[irq]->ui.flags.ready) {
if (dev_id == ioinfo[irq]->irq_desc.dev_id) {
/* start deregister */
ioinfo[irq]->ui.flags.unready = 1;
ret = disable_subchannel (irq);
if (ret == -EBUSY) {
/*
* kill it !
* We try to terminate the I/O by halt_IO first,
* then clear_IO.
* Because the device may be gone (machine
* check handling), we can't use sync I/O.
*/
halt_IO (irq, 0xC8C1D3E3, 0);
s390irq_spin_unlock_irqrestore (irq, flags);
udelay (200000); /* 200 ms */
s390irq_spin_lock_irqsave (irq, flags);
ret = disable_subchannel (irq);
if (ret == -EBUSY) {
clear_IO (irq, 0x40C3D3D9, 0);
s390irq_spin_unlock_irqrestore (irq,
flags);
udelay (1000000); /* 1000 ms */
s390irq_spin_lock_irqsave (irq, flags);
/* give it a very last try ... */
disable_subchannel (irq);
if (ioinfo[irq]->ui.flags.busy) {
printk (KERN_CRIT
"free_irq(%04X) "
"- device %04X busy, retry "
"count exceeded\n", irq,
ioinfo[irq]->devstat.
devno);
CIO_MSG_EVENT( 0,
"free_irq(%04X) - "
"device %04X busy, "
"retry count exceeded\n",
irq,
ioinfo[irq]->
devstat.devno);
}
}
}
ioinfo[irq]->ui.flags.ready = 0;
ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */
ioinfo[irq]->nopfunc = NULL;
s390irq_spin_unlock_irqrestore (irq, flags);
} else {
s390irq_spin_unlock_irqrestore (irq, flags);
printk (KERN_ERR "free_irq(%04X) : error, "
"dev_id does not match !\n", irq);
CIO_MSG_EVENT( 0,
"free_irq(%04X) : error, "
"dev_id does not match !\n",
irq);
}
} else {
s390irq_spin_unlock_irqrestore (irq, flags);
printk (KERN_ERR "free_irq(%04X) : error, "
"no action block ... !\n", irq);
CIO_MSG_EVENT(0,
"free_irq(%04X) : error, "
"no action block ... !\n", irq);
}
}
/*
* Enable IRQ by modifying the subchannel
*/
static int
enable_subchannel (unsigned int irq)
{
int ret = 0;
int ccode;
int retry = 5;
char dbf_txt[15];
SANITY_CHECK (irq);
sprintf (dbf_txt, "ensch%x", irq);
CIO_TRACE_EVENT (2, dbf_txt);
/*
* If a previous disable request is pending we reset it. However, this
* status implies that the device may (still) be not-operational.
*/
if (ioinfo[irq]->ui.flags.d_disable) {
ioinfo[irq]->ui.flags.d_disable = 0;
ret = 0;
} else {
ccode = stsch (irq, &(ioinfo[irq]->schib));
if (ccode) {
ret = -ENODEV;
} else {
ioinfo[irq]->schib.pmcw.ena = 1;
if (irq == cons_dev) {
ioinfo[irq]->schib.pmcw.isc = 7;
} else {
ioinfo[irq]->schib.pmcw.isc = 3;
}
do {
ccode = msch (irq, &(ioinfo[irq]->schib));
switch (ccode) {
case 0: /* ok */
ret = 0;
retry = 0;
break;
case 1: /* status pending */
ioinfo[irq]->ui.flags.s_pend = 1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend = 0;
ret = -EIO;
/*
* might be overwritten on re-driving
* the msch()
*/
retry--;
break;
case 2: /* busy */
udelay (100); /* allow for recovery */
ret = -EBUSY;
retry--;
break;
case 3: /* not oper */
ioinfo[irq]->ui.flags.oper = 0;
retry = 0;
ret = -ENODEV;
break;
}
} while (retry);
}
}
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
return (ret);
}
/*
* Disable IRQ by modifying the subchannel
*/
static int
disable_subchannel (unsigned int irq)
{
int cc; /* condition code */
int ret = 0; /* function return value */
int retry = 5;
char dbf_txt[15];
SANITY_CHECK (irq);
sprintf (dbf_txt, "dissch%x", irq);
CIO_TRACE_EVENT (2, dbf_txt);
if (ioinfo[irq]->ui.flags.busy) {
/*
* the disable function must not be called while there are
* requests pending for completion !
*/
ret = -EBUSY;
} else {
/*
* If device isn't operational we have to perform delayed
* disabling when the next interrupt occurs - unless the
* irq is re-requested prior to the interrupt to occur.
*/
cc = stsch (irq, &(ioinfo[irq]->schib));
if (cc == 3) {
ioinfo[irq]->ui.flags.oper = 0;
ioinfo[irq]->ui.flags.d_disable = 1;
ret = 0;
} else { /* cc == 0 */
ioinfo[irq]->schib.pmcw.ena = 0;
do {
cc = msch (irq, &(ioinfo[irq]->schib));
switch (cc) {
case 0: /* ok */
retry = 0;
ret = 0;
break;
case 1: /* status pending */
ioinfo[irq]->ui.flags.s_pend = 1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend = 0;
ret = -EIO;
/*
* might be overwritten on re-driving
* the msch() call
*/
retry--;
break;
case 2: /* busy; this should not happen! */
printk (KERN_CRIT
"disable_subchannel(%04X) "
"- unexpected busy condition for "
"device %04X received !\n", irq,
ioinfo[irq]->devstat.devno);
CIO_MSG_EVENT(0,
"disable_subchannel(%04X) "
"- unexpected busy condition "
"for device %04X received !\n",
irq,
ioinfo[irq]->devstat.
devno);
retry = 0;
ret = -EBUSY;
break;
case 3: /* not oper */
/*
* should hardly occur ?!
*/
ioinfo[irq]->ui.flags.oper = 0;
ioinfo[irq]->ui.flags.d_disable = 1;
retry = 0;
ret = 0;
/*
* if the device has gone, we don't need
* to disable it anymore !
*/
break;
}
} while (retry);
}
}
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
return (ret);
}
void
s390_init_IRQ (void)
{
unsigned long flags; /* PSW flags */
long cr6 __attribute__ ((aligned (8)));
cpuid_t cpuid;
asm volatile ("STCK %0":"=m" (irq_IPL_TOD));
p_init_schib = alloc_bootmem_low (sizeof (schib_t));
p_init_irb = alloc_bootmem_low (sizeof (irb_t));
/*
* As we don't know about the calling environment
* we assure running disabled. Before leaving the
* function we resestablish the old environment.
*
* Note : as we don't need a system wide lock, therefore
* we shouldn't use cli(), but __cli() as this
* affects the current CPU only.
*/
__save_flags (flags);
__cli ();
/*
* disable all interrupts
*/
cr6 = 0;
__ctl_load (cr6, 6, 6);
s390_process_subchannels ();
if (cio_count_irqs) {
int i;
for (i = 0; i < NR_CPUS; i++)
s390_irq_count[i] = 0;
}
/*
* Let's build our path group ID here.
*/
global_pgid = (pgid_t *)alloc_bootmem(sizeof(pgid_t));
cpuid = *(cpuid_t*) __LC_CPUID;
if (MACHINE_NEW_STIDP)
global_pgid->cpu_addr = 0x8000;
else {
#ifdef CONFIG_SMP
global_pgid->cpu_addr = hard_smp_processor_id();
#else
global_pgid->cpu_addr = 0;
#endif
}
global_pgid->cpu_id = cpuid.ident;
global_pgid->cpu_model = ((cpuid_t *) __LC_CPUID)->machine;
global_pgid->tod_high = *(__u32 *) & irq_IPL_TOD;
/*
* enable default I/O-interrupt sublass 3
*/
cr6 = 0x10000000;
__ctl_load (cr6, 6, 6);
s390_device_recognition_all ();
init_IRQ_complete = 1;
__restore_flags (flags);
return;
}
/*
* dummy handler, used during init_IRQ() processing for compatibility only
*/
void
init_IRQ_handler (int irq, void *dev_id, struct pt_regs *regs)
{
/* this is a dummy handler only ... */
}
int
s390_start_IO (int irq, /* IRQ */
ccw1_t * cpa, /* logical channel prog addr */
unsigned long user_intparm, /* interruption parameter */
__u8 lpm, /* logical path mask */
unsigned long flag)
{ /* flags */
int ccode;
int ret = 0;
char dbf_txt[15];
SANITY_CHECK (irq);
/*
* The flag usage is mutal exclusive ...
*/
if ((flag & DOIO_EARLY_NOTIFICATION)
&& (flag & DOIO_REPORT_ALL)) {
return (-EINVAL);
}
sprintf (dbf_txt, "stIO%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* setup ORB
*/
ioinfo[irq]->orb.intparm = (__u32) (long) &ioinfo[irq]->u_intparm;
ioinfo[irq]->orb.fmt = 1;
ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH);
ioinfo[irq]->orb.spnd = (flag & DOIO_ALLOW_SUSPEND ? TRUE : FALSE);
ioinfo[irq]->orb.ssic = ((flag & DOIO_ALLOW_SUSPEND)
&& (flag & DOIO_SUPPRESS_INTER));
if (flag & DOIO_VALID_LPM) {
ioinfo[irq]->orb.lpm = lpm;
} else {
ioinfo[irq]->orb.lpm = ioinfo[irq]->opm;
}
#ifdef CONFIG_ARCH_S390X
/*
* for 64 bit we always support 64 bit IDAWs with 4k page size only
*/
ioinfo[irq]->orb.c64 = 1;
ioinfo[irq]->orb.i2k = 0;
#endif
ioinfo[irq]->orb.cpa = (__u32) virt_to_phys (cpa);
/*
* If sync processing was requested we lock the sync ISC, modify the
* device to present interrupts for this ISC only and switch the
* CPU to handle this ISC + the console ISC exclusively.
*/
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
ret = enable_cpu_sync_isc (irq);
if (ret) {
return (ret);
}
}
if (flag & DOIO_DONT_CALL_INTHDLR) {
ioinfo[irq]->ui.flags.repnone = 1;
}
/*
* Issue "Start subchannel" and process condition code
*/
if (flag & DOIO_USE_DIAG98) {
ioinfo[irq]->orb.key = get_storage_key() >> 4;
ioinfo[irq]->orb.cpa =
(__u32) pfix_get_addr((void *)ioinfo[irq]->orb.cpa);
ccode = diag98 (irq, &(ioinfo[irq]->orb));
} else {
ccode = ssch (irq, &(ioinfo[irq]->orb));
}
sprintf (dbf_txt, "ccode:%d", ccode);
CIO_TRACE_EVENT (4, dbf_txt);
switch (ccode) {
case 0:
if (!ioinfo[irq]->ui.flags.w4sense) {
/*
* init the device driver specific devstat irb area
*
* Note : donīt clear saved irb info in case of sense !
*/
memset (&((devstat_t *) ioinfo[irq]->irq_desc.dev_id)->
ii.irb, '\0', sizeof (irb_t));
}
memset (&ioinfo[irq]->devstat.ii.irb, '\0', sizeof (irb_t));
/*
* initialize device status information
*/
ioinfo[irq]->ui.flags.busy = 1;
ioinfo[irq]->ui.flags.doio = 1;
ioinfo[irq]->u_intparm = user_intparm;
ioinfo[irq]->devstat.cstat = 0;
ioinfo[irq]->devstat.dstat = 0;
ioinfo[irq]->devstat.lpum = 0;
ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION;
ioinfo[irq]->devstat.scnt = 0;
ioinfo[irq]->ui.flags.fast = 0;
ioinfo[irq]->ui.flags.repall = 0;
/*
* Check for either early (FAST) notification requests
* or if we are to return all interrupt info.
* Default is to call IRQ handler at secondary status only
*/
if (flag & DOIO_EARLY_NOTIFICATION) {
ioinfo[irq]->ui.flags.fast = 1;
} else if (flag & DOIO_REPORT_ALL) {
ioinfo[irq]->ui.flags.repall = 1;
}
/*
* 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) {
unsigned long psw_mask;
int ccode;
uint64_t time_start;
uint64_t time_curr;
int ready = 0;
int io_sub = -1;
int do_retry = 1;
/*
* We shouldn't perform a TPI loop, waiting for an
* interrupt to occur, but should load a WAIT PSW
* instead. Otherwise we may keep the channel subsystem
* busy, not able to present the interrupt. When our
* sync. interrupt arrived we reset the I/O old PSW to
* its original value.
*/
ccode = iac ();
switch (ccode) {
case 0: /* primary-space */
psw_mask = _IO_PSW_MASK
| _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT;
break;
case 1: /* secondary-space */
psw_mask = _IO_PSW_MASK
| _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT;
break;
case 2: /* access-register */
psw_mask = _IO_PSW_MASK
| _PSW_ACC_REG_MODE | _PSW_IO_WAIT;
break;
case 3: /* home-space */
psw_mask = _IO_PSW_MASK
| _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT;
break;
default:
panic ("start_IO() : unexpected "
"address-space-control %d\n", ccode);
break;
}
/*
* Martin didn't like modifying the new PSW, now we take
* a fast exit in do_IRQ() instead
*/
*(__u32 *) __LC_SYNC_IO_WORD = 1;
asm volatile ("STCK %0":"=m" (time_start));
time_start = time_start >> 32;
do {
if (flag & DOIO_TIMEOUT) {
tpi_info_t tpi_info = { 0, };
do {
if (tpi (&tpi_info) == 1) {
io_sub = tpi_info.irq;
break;
} else {
udelay (100); /* usecs */
asm volatile
("STCK %0":"=m"
(time_curr));
if (((time_curr >> 32) -
time_start) >= 3)
do_retry = 0;
}
} while (do_retry);
} else {
__load_psw_mask (psw_mask);
io_sub =
(__u32) *
(__u16 *) __LC_SUBCHANNEL_NR;
}
if (do_retry)
ready = s390_process_IRQ (io_sub);
/*
* surrender when retry count's exceeded ...
*/
} while (!((io_sub == irq)
&& (ready == 1))
&& do_retry);
*(__u32 *) __LC_SYNC_IO_WORD = 0;
if (!do_retry)
ret = -ETIMEDOUT;
}
break;
case 1: /* status pending */
/*
* Don't do an inline processing of pending interrupt conditions
* while doing async. I/O. The interrupt will pop up when we are
* enabled again and the I/O can be retried.
*/
if (!ioinfo[irq]->ui.flags.syncio) {
ret = -EBUSY;
break;
}
ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION
| DEVSTAT_STATUS_PENDING;
/*
* initialize the device driver specific devstat irb area
*/
memset (&((devstat_t *) ioinfo[irq]->irq_desc.dev_id)->ii.irb,
'\0', sizeof (irb_t));
/*
* Let the common interrupt handler process the pending status.
* However, we must avoid calling the user action handler, as
* it won't be prepared to handle a pending status during
* do_IO() processing inline. This also implies that process_IRQ
* must terminate synchronously - especially if device sensing
* is required.
*/
ioinfo[irq]->ui.flags.s_pend = 1;
ioinfo[irq]->ui.flags.busy = 1;
ioinfo[irq]->ui.flags.doio = 1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend = 0;
ioinfo[irq]->ui.flags.busy = 0;
ioinfo[irq]->ui.flags.doio = 0;
ioinfo[irq]->ui.flags.repall = 0;
ioinfo[irq]->ui.flags.w4final = 0;
ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS;
/*
* In multipath mode a condition code 3 implies the last path
* has gone, except we have previously restricted the I/O to
* a particular path. A condition code 1 (0 won't occur)
* results in return code EIO as well as 3 with another path
* than the one used (i.e. path available mask is non-zero).
*/
if (ioinfo[irq]->devstat.ii.irb.scsw.cc == 3) {
if (ioinfo[irq]->opm == 0) {
ret = -ENODEV;
ioinfo[irq]->ui.flags.oper = 0;
} else {
ret = -EIO;
}
ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
#ifdef CONFIG_DEBUG_IO
{
char buffer[80];
stsch (irq, &(ioinfo[irq]->schib));
sprintf (buffer,
"s390_start_IO(%04X) - irb for "
"device %04X, after status pending\n",
irq, ioinfo[irq]->devstat.devno);
s390_displayhex (buffer,
&(ioinfo[irq]->devstat.ii.irb),
sizeof (irb_t));
sprintf (buffer,
"s390_start_IO(%04X) - schib for "
"device %04X, after status pending\n",
irq, ioinfo[irq]->devstat.devno);
s390_displayhex (buffer,
&(ioinfo[irq]->schib),
sizeof (schib_t));
if (ioinfo[irq]->devstat.
flag & DEVSTAT_FLAG_SENSE_AVAIL) {
sprintf (buffer,
"s390_start_IO(%04X) "
"- sense data for device %04X,"
" after status pending\n",
irq,
ioinfo[irq]->devstat.devno);
s390_displayhex (buffer,
ioinfo[irq]->irq_desc.
dev_id->ii.sense.data,
ioinfo[irq]->irq_desc.
dev_id->rescnt);
}
}
#endif
if (cio_debug_initialized) {
stsch (irq, &(ioinfo[irq]->schib));
sprintf(dbf_txt, "sp%x", irq);
CIO_TRACE_EVENT(2, dbf_txt);
CIO_TRACE_EVENT(2, "irb:");
CIO_HEX_EVENT(2, &(ioinfo[irq]->devstat.ii.irb),
sizeof (irb_t));
CIO_TRACE_EVENT(2, "schib:");
CIO_HEX_EVENT(2, &(ioinfo[irq]->schib),
sizeof (schib_t));
if (ioinfo[irq]->devstat.
flag & DEVSTAT_FLAG_SENSE_AVAIL) {
CIO_TRACE_EVENT(2, "sense:");
CIO_HEX_EVENT(2, ioinfo[irq]->irq_desc.
dev_id->ii.sense.data,
ioinfo[irq]->irq_desc.
dev_id->rescnt);
}
}
} else {
ret = -EIO;
ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER;
ioinfo[irq]->ui.flags.oper = 1;
}
break;
case 2: /* busy */
ret = -EBUSY;
break;
default: /* device/path not operational */
if (flag & DOIO_VALID_LPM) {
ioinfo[irq]->opm &= ~lpm;
} else {
ioinfo[irq]->opm = 0;
}
if (ioinfo[irq]->opm == 0) {
ioinfo[irq]->ui.flags.oper = 0;
ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
}
ret = -ENODEV;
memcpy (ioinfo[irq]->irq_desc.dev_id,
&(ioinfo[irq]->devstat), sizeof (devstat_t));
#ifdef CONFIG_DEBUG_IO
stsch (irq, &(ioinfo[irq]->schib));
sprintf (buffer, "s390_start_IO(%04X) - schib for "
"device %04X, after 'not oper' status\n",
irq, ioinfo[irq]->devstat.devno);
s390_displayhex (buffer,
&(ioinfo[irq]->schib), sizeof (schib_t));
#endif
if (cio_debug_initialized) {
stsch (irq, &(ioinfo[irq]->schib));
sprintf(dbf_txt, "no%x", irq);
CIO_TRACE_EVENT(2, dbf_txt);
CIO_HEX_EVENT(2, &(ioinfo[irq]->schib),
sizeof (schib_t));
}
break;
}
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
disable_cpu_sync_isc (irq);
}
if (flag & DOIO_DONT_CALL_INTHDLR) {
ioinfo[irq]->ui.flags.repnone = 0;
}
return (ret);
}
int
do_IO (int irq, /* IRQ */
ccw1_t * cpa, /* channel program address */
unsigned long user_intparm, /* interruption parameter */
__u8 lpm, /* logical path mask */
unsigned long flag)
{ /* flags : see above */
int ret = 0;
char dbf_txt[15];
SANITY_CHECK (irq);
/* handler registered ? or free_irq() in process already ? */
if (!ioinfo[irq]->ui.flags.ready || ioinfo[irq]->ui.flags.unready) {
return (-ENODEV);
}
sprintf (dbf_txt, "doIO%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
if (ioinfo[irq]->ui.flags.noio)
return -EBUSY;
/*
* Note: We ignore the device operational status - if not operational,
* the SSCH will lead to an -ENODEV condition ...
*/
if (!ioinfo[irq]->ui.flags.busy) { /* last I/O completed ? */
ret = s390_start_IO (irq, cpa, user_intparm, lpm, flag);
} else {
ret = -EBUSY;
}
return (ret);
}
/*
* resume suspended I/O operation
*/
int
resume_IO (int irq)
{
int ret = 0;
char dbf_txt[15];
SANITY_CHECK (irq);
sprintf (dbf_txt, "resIO%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* We allow for 'resume' requests only for active I/O operations
*/
if (ioinfo[irq]->ui.flags.busy) {
int ccode;
ccode = rsch (irq);
sprintf (dbf_txt, "ccode:%d", ccode);
CIO_TRACE_EVENT (4, dbf_txt);
switch (ccode) {
case 0:
break;
case 1:
ret = -EBUSY;
break;
case 2:
ret = -EINVAL;
break;
case 3:
/*
* useless to wait for request completion
* as device is no longer operational !
*/
ioinfo[irq]->ui.flags.oper = 0;
ioinfo[irq]->ui.flags.busy = 0;
ret = -ENODEV;
break;
}
} else {
ret = -ENOTCONN;
}
return (ret);
}
/*
* Note: The "intparm" parameter is not used by the halt_IO() function
* itself, as no ORB is built for the HSCH instruction. However,
* it allows the device interrupt handler to associate the upcoming
* interrupt with the halt_IO() request.
*/
int
halt_IO (int irq, unsigned long user_intparm, unsigned long flag)
{ /* possible DOIO_WAIT_FOR_INTERRUPT */
int ret;
int ccode;
char dbf_txt[15];
SANITY_CHECK (irq);
if (ioinfo[irq]->ui.flags.noio)
return -EBUSY;
/*
* we only allow for halt_IO if the device has an I/O handler associated
*/
if (!ioinfo[irq]->ui.flags.ready) {
return -ENODEV;
}
/*
* we ignore the halt_io() request if ending_status was received but
* a SENSE operation is waiting for completion.
*/
if (ioinfo[irq]->ui.flags.w4sense) {
return 0;
}
sprintf (dbf_txt, "haltIO%x", irq);
CIO_TRACE_EVENT (2, dbf_txt);
/*
* If sync processing was requested we lock the sync ISC,
* modify the device to present interrupts for this ISC only
* and switch the CPU to handle this ISC + the console ISC
* exclusively.
*/
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
ret = enable_cpu_sync_isc (irq);
if (ret)
return (ret);
}
/*
* Issue "Halt subchannel" and process condition code
*/
ccode = hsch (irq);
sprintf (dbf_txt, "ccode:%d", ccode);
CIO_TRACE_EVENT (2, dbf_txt);
switch (ccode) {
case 0:
ioinfo[irq]->ui.flags.haltio = 1;
if (!ioinfo[irq]->ui.flags.doio) {
ioinfo[irq]->ui.flags.busy = 1;
ioinfo[irq]->u_intparm = user_intparm;
ioinfo[irq]->devstat.cstat = 0;
ioinfo[irq]->devstat.dstat = 0;
ioinfo[irq]->devstat.lpum = 0;
ioinfo[irq]->devstat.flag = DEVSTAT_HALT_FUNCTION;
ioinfo[irq]->devstat.scnt = 0;
} else {
ioinfo[irq]->devstat.flag |= DEVSTAT_HALT_FUNCTION;
}
/*
* If synchronous I/O processing is requested, we have
* to wait for the corresponding interrupt to occur by
* polling the interrupt condition. However, as multiple
* interrupts may be outstanding, we must not just wait
* for the first interrupt, but must poll until ours
* pops up.
*/
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
int io_sub;
__u32 io_parm;
unsigned long psw_mask;
int ccode;
int ready = 0;
/*
* 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.
*/
ccode = iac ();
switch (ccode) {
case 0: /* primary-space */
psw_mask = _IO_PSW_MASK
| _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT;
break;
case 1: /* secondary-space */
psw_mask = _IO_PSW_MASK
| _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT;
break;
case 2: /* access-register */
psw_mask = _IO_PSW_MASK
| _PSW_ACC_REG_MODE | _PSW_IO_WAIT;
break;
case 3: /* home-space */
psw_mask = _IO_PSW_MASK
| _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT;
break;
default:
panic ("halt_IO() : unexpected "
"address-space-control %d\n", ccode);
break;
}
/*
* Martin didn't like modifying the new PSW, now we take
* a fast exit in do_IRQ() instead
*/
*(__u32 *) __LC_SYNC_IO_WORD = 1;
do {
__load_psw_mask (psw_mask);
io_parm = *(__u32 *) __LC_IO_INT_PARM;
io_sub = (__u32) * (__u16 *) __LC_SUBCHANNEL_NR;
ready = s390_process_IRQ (io_sub);
} while (!((io_sub == irq) && (ready == 1)));
*(__u32 *) __LC_SYNC_IO_WORD = 0;
}
ret = 0;
break;
case 1: /* status pending */
/*
* Don't do an inline processing of pending interrupt conditions
* while doing async. I/O. The interrupt will pop up when we are
* enabled again and the I/O can be retried.
*/
if (!ioinfo[irq]->ui.flags.syncio) {
ret = -EBUSY;
break;
}
ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING;
/*
* initialize the device driver specific devstat irb area
*/
memset (&ioinfo[irq]->irq_desc.dev_id->ii.irb,
'\0', sizeof (irb_t));
/*
* Let the common interrupt handler process the pending
* status. However, we must avoid calling the user
* action handler, as it won't be prepared to handle
* a pending status during do_IO() processing inline.
* This also implies that s390_process_IRQ must
* terminate synchronously - especially if device
* sensing is required.
*/
ioinfo[irq]->ui.flags.s_pend = 1;
ioinfo[irq]->ui.flags.busy = 1;
ioinfo[irq]->ui.flags.doio = 1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend = 0;
ioinfo[irq]->ui.flags.busy = 0;
ioinfo[irq]->ui.flags.doio = 0;
ioinfo[irq]->ui.flags.repall = 0;
ioinfo[irq]->ui.flags.w4final = 0;
ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS;
/*
* In multipath mode a condition code 3 implies the last
* path has gone, except we have previously restricted
* the I/O to a particular path. A condition code 1
* (0 won't occur) results in return code EIO as well
* as 3 with another path than the one used (i.e. path
* available mask is non-zero).
*/
if (ioinfo[irq]->devstat.ii.irb.scsw.cc == 3) {
ret = -ENODEV;
ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
ioinfo[irq]->ui.flags.oper = 0;
} else {
ret = -EIO;
ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER;
ioinfo[irq]->ui.flags.oper = 1;
}
break;
case 2: /* busy */
ret = -EBUSY;
break;
default: /* device not operational */
ret = -ENODEV;
break;
}
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
disable_cpu_sync_isc (irq);
}
return (ret);
}
/*
* Note: The "intparm" parameter is not used by the clear_IO() function
* itself, as no ORB is built for the CSCH instruction. However,
* it allows the device interrupt handler to associate the upcoming
* interrupt with the clear_IO() request.
*/
int
clear_IO (int irq, unsigned long user_intparm, unsigned long flag)
{ /* possible DOIO_WAIT_FOR_INTERRUPT */
int ret = 0;
int ccode;
char dbf_txt[15];
SANITY_CHECK (irq);
if (ioinfo[irq] == INVALID_STORAGE_AREA)
return (-ENODEV);
if (ioinfo[irq]->ui.flags.noio)
return -EBUSY;
/*
* we only allow for clear_IO if the device has an I/O handler associated
*/
if (!ioinfo[irq]->ui.flags.ready)
return -ENODEV;
/*
* we ignore the clear_io() request if ending_status was received but
* a SENSE operation is waiting for completion.
*/
if (ioinfo[irq]->ui.flags.w4sense)
return 0;
sprintf (dbf_txt, "clearIO%x", irq);
CIO_TRACE_EVENT (2, dbf_txt);
/*
* If sync processing was requested we lock the sync ISC,
* modify the device to present interrupts for this ISC only
* and switch the CPU to handle this ISC + the console ISC
* exclusively.
*/
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
ret = enable_cpu_sync_isc (irq);
if (ret)
return (ret);
}
/*
* Issue "Clear subchannel" and process condition code
*/
ccode = csch (irq);
sprintf (dbf_txt, "ccode:%d", ccode);
CIO_TRACE_EVENT (2, dbf_txt);
switch (ccode) {
case 0:
ioinfo[irq]->ui.flags.haltio = 1;
if (!ioinfo[irq]->ui.flags.doio) {
ioinfo[irq]->ui.flags.busy = 1;
ioinfo[irq]->u_intparm = user_intparm;
ioinfo[irq]->devstat.cstat = 0;
ioinfo[irq]->devstat.dstat = 0;
ioinfo[irq]->devstat.lpum = 0;
ioinfo[irq]->devstat.flag = DEVSTAT_CLEAR_FUNCTION;
ioinfo[irq]->devstat.scnt = 0;
} else {
ioinfo[irq]->devstat.flag |= DEVSTAT_CLEAR_FUNCTION;
}
/*
* If synchronous I/O processing is requested, we have
* to wait for the corresponding interrupt to occur by
* polling the interrupt condition. However, as multiple
* interrupts may be outstanding, we must not just wait
* for the first interrupt, but must poll until ours
* pops up.
*/
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
int io_sub;
__u32 io_parm;
unsigned long psw_mask;
int ccode;
int ready = 0;
/*
* 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.
*/
ccode = iac ();
switch (ccode) {
case 0: /* primary-space */
psw_mask = _IO_PSW_MASK
| _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT;
break;
case 1: /* secondary-space */
psw_mask = _IO_PSW_MASK
| _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT;
break;
case 2: /* access-register */
psw_mask = _IO_PSW_MASK
| _PSW_ACC_REG_MODE | _PSW_IO_WAIT;
break;
case 3: /* home-space */
psw_mask = _IO_PSW_MASK
| _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT;
break;
default:
panic ("clear_IO() : unexpected "
"address-space-control %d\n", ccode);
break;
}
/*
* Martin didn't like modifying the new PSW, now we take
* a fast exit in do_IRQ() instead
*/
*(__u32 *) __LC_SYNC_IO_WORD = 1;
do {
__load_psw_mask (psw_mask);
io_parm = *(__u32 *) __LC_IO_INT_PARM;
io_sub = (__u32) * (__u16 *) __LC_SUBCHANNEL_NR;
ready = s390_process_IRQ (io_sub);
} while (!((io_sub == irq) && (ready == 1)));
*(__u32 *) __LC_SYNC_IO_WORD = 0;
}
ret = 0;
break;
case 1: /* no status pending for csh */
BUG ();
break;
case 2: /* no busy for csh */
BUG ();
break;
default: /* device not operational */
ret = -ENODEV;
break;
}
if (flag & DOIO_WAIT_FOR_INTERRUPT) {
disable_cpu_sync_isc (irq);
}
return (ret);
}
/*
* Function: cancel_IO
* Issues a "Cancel Subchannel" on the specified subchannel
* Note: We don't need any fancy intparms and flags here
* since xsch is executed synchronously.
* Only for common I/O internal use as for now.
*/
int
cancel_IO (int irq)
{
int ccode;
char dbf_txt[15];
int ret = 0;
SANITY_CHECK (irq);
sprintf (dbf_txt, "cancelIO%x", irq);
CIO_TRACE_EVENT (2, dbf_txt);
ccode = xsch (irq);
sprintf (dbf_txt, "ccode:%d", ccode);
CIO_TRACE_EVENT (2, dbf_txt);
switch (ccode) {
case 0: /* success */
ret = 0;
break;
case 1: /* status pending */
ret = -EBUSY;
break;
case 2: /* not applicable */
ret = -EINVAL;
break;
default: /* not oper */
ret = -ENODEV;
}
return ret;
}
/*
* do_IRQ() handles all normal I/O device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*
*/
asmlinkage void
do_IRQ (struct pt_regs regs)
{
/*
* Get interrupt info from lowcore
*/
volatile tpi_info_t *tpi_info = (tpi_info_t *) (__LC_SUBCHANNEL_ID);
int cpu = smp_processor_id ();
/*
* take fast exit if CPU is in sync. I/O state
*
* Note: we have to turn off the WAIT bit and re-disable
* interrupts prior to return as this was the initial
* entry condition to synchronous I/O.
*/
if (*(__u32 *) __LC_SYNC_IO_WORD) {
regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT);
return;
}
/* endif */
#ifdef CONFIG_FAST_IRQ
do {
#endif /* CONFIG_FAST_IRQ */
/*
* Non I/O-subchannel thin interrupts are processed differently
*/
if (tpi_info->adapter_IO == 1 &&
tpi_info->int_type == IO_INTERRUPT_TYPE) {
irq_enter (cpu, -1);
do_adapter_IO (tpi_info->intparm);
irq_exit (cpu, -1);
} else {
unsigned int irq = tpi_info->irq;
/*
* fix me !!!
*
* instead of boxing the device, we need to schedule device
* recognition, the interrupt stays pending. We need to
* dynamically allocate an ioinfo structure, etc..
*/
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
return; /* this keeps the device boxed ... */
}
if (ioinfo[irq]->st) {
/* How can that be? */
printk(KERN_WARNING "Received interrupt on "
"non-IO subchannel %x!\n", irq);
return;
}
irq_enter (cpu, irq);
s390irq_spin_lock (irq);
s390_process_IRQ (irq);
s390irq_spin_unlock (irq);
irq_exit (cpu, irq);
}
#ifdef CONFIG_FAST_IRQ
/*
* Are more interrupts pending?
* If so, the tpi instruction will update the lowcore
* to hold the info for the next interrupt.
*/
} while (tpi (NULL) != 0);
#endif /* CONFIG_FAST_IRQ */
return;
}
/*
* s390_process_IRQ() handles status pending situations and interrupts
*
* Called by : do_IRQ() - for "real" interrupts
* s390_start_IO, halt_IO()
* - status pending cond. after SSCH, or HSCH
* disable_subchannel() - status pending conditions (after MSCH)
*
* Returns: 0 - no ending status received, no further action taken
* 1 - interrupt handler was called with ending status
*/
int
s390_process_IRQ (unsigned int irq)
{
int ccode; /* cond code from tsch() operation */
int irb_cc; /* cond code from irb */
int sdevstat; /* struct devstat size to copy */
unsigned int fctl; /* function control */
unsigned int stctl; /* status control */
unsigned int actl; /* activity control */
int issense = 0;
int ending_status = 0;
int allow4handler = 1;
int chnchk = 0;
devstat_t *dp;
devstat_t *udp;
char dbf_txt[15];
char buffer[256];
if (cio_count_irqs) {
int cpu = smp_processor_id ();
s390_irq_count[cpu]++;
}
CIO_TRACE_EVENT (3, "procIRQ");
sprintf (dbf_txt, "%x", irq);
CIO_TRACE_EVENT (3, dbf_txt);
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
/* we can't properly process the interrupt ... */
#ifdef CONFIG_DEBUG_IO
printk (KERN_CRIT "s390_process_IRQ(%04X) - got interrupt "
"for non-initialized subchannel!\n", irq);
#endif /* CONFIG_DEBUG_IO */
CIO_MSG_EVENT (0,
"s390_process_IRQ(%04X) - got interrupt "
"for non-initialized subchannel!\n",
irq);
tsch (irq, p_init_irb);
return (1);
}
if (ioinfo[irq]->st) {
/* can't be */
BUG();
return 1;
}
dp = &ioinfo[irq]->devstat;
udp = ioinfo[irq]->irq_desc.dev_id;
/*
* It might be possible that a device was not-oper. at the time
* of free_irq() processing. This means the handler is no longer
* available when the device possibly becomes ready again. In
* this case we perform delayed disable_subchannel() processing.
*/
if (!ioinfo[irq]->ui.flags.ready) {
if (!ioinfo[irq]->ui.flags.d_disable) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_CRIT "s390_process_IRQ(%04X) "
"- no interrupt handler registered "
"for device %04X !\n",
irq, ioinfo[irq]->devstat.devno);
#endif /* CONFIG_DEBUG_IO */
CIO_MSG_EVENT(0,
"s390_process_IRQ(%04X) "
"- no interrupt handler "
"registered for device "
"%04X !\n",
irq,
ioinfo[irq]->devstat.devno);
}
}
/*
* retrieve the i/o interrupt information (irb),
* update the device specific status information
* and possibly call the interrupt handler.
*
* Note 1: At this time we don't process the resulting
* condition code (ccode) from tsch(), although
* we probably should.
*
* Note 2: Here we will have to check for channel
* check conditions and call a channel check
* handler.
*
* Note 3: If a start function was issued, the interruption
* parameter relates to it. If a halt function was
* issued for an idle device, the intparm must not
* be taken from lowcore, but from the devstat area.
*/
ccode = tsch (irq, &(dp->ii.irb));
sprintf (dbf_txt, "ccode:%d", ccode);
CIO_TRACE_EVENT (3, dbf_txt);
if (ccode == 1) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_INFO "s390_process_IRQ(%04X) - no status "
"pending...\n", irq);
#endif /* CONFIG_DEBUG_IO */
CIO_MSG_EVENT(2,
"s390_process_IRQ(%04X) - no status pending\n",
irq);
} else if (ccode == 3) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_WARNING "s390_process_IRQ(%04X) - subchannel "
"is not operational!\n",
irq);
#endif /* CONFIG_DEBUG_IO */
CIO_MSG_EVENT(0,
"s390_process_IRQ(%04X) - subchannel "
"is not operational!\n",
irq);
}
/*
* We must only accumulate the status if the device is busy already
*/
if (ioinfo[irq]->ui.flags.busy) {
dp->dstat |= dp->ii.irb.scsw.dstat;
dp->cstat |= dp->ii.irb.scsw.cstat;
dp->intparm = ioinfo[irq]->u_intparm;
} else {
dp->dstat = dp->ii.irb.scsw.dstat;
dp->cstat = dp->ii.irb.scsw.cstat;
dp->flag = 0; /* reset status flags */
dp->intparm = 0;
}
dp->lpum = dp->ii.irb.esw.esw1.lpum;
/*
* reset device-busy bit if no longer set in irb
*/
if ((dp->dstat & DEV_STAT_BUSY)
&& ((dp->ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) {
dp->dstat &= ~DEV_STAT_BUSY;
}
/*
* Save residual count and CCW information in case primary and
* secondary status are presented with different interrupts.
*/
if (dp->ii.irb.scsw.stctl
& (SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_INTER_STATUS)) {
/*
* If the subchannel status shows status pending
* and we received a check condition, the count
* information is not meaningful.
*/
if (!((dp->ii.irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)
&& (dp->ii.irb.scsw.cstat
& (SCHN_STAT_CHN_DATA_CHK
| SCHN_STAT_CHN_CTRL_CHK
| SCHN_STAT_INTF_CTRL_CHK
| SCHN_STAT_PROG_CHECK
| SCHN_STAT_PROT_CHECK
| SCHN_STAT_CHAIN_CHECK)))) {
dp->rescnt = dp->ii.irb.scsw.count;
} else {
dp->rescnt = SENSE_MAX_COUNT;
}
dp->cpa = dp->ii.irb.scsw.cpa;
}
irb_cc = dp->ii.irb.scsw.cc;
/*
* check for any kind of channel or interface control check but don't
* issue the message for the console device
*/
if ((dp->ii.irb.scsw.cstat
& (SCHN_STAT_CHN_DATA_CHK
| SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK))) {
if (irq != cons_dev)
printk (KERN_WARNING
"Channel-Check or Interface-Control-Check "
"received\n"
" ... device %04X on subchannel %04X, dev_stat "
": %02X sch_stat : %02X\n",
ioinfo[irq]->devstat.devno, irq, dp->dstat,
dp->cstat);
CIO_MSG_EVENT(0,
"Channel-Check or "
"Interface-Control-Check received\n");
CIO_MSG_EVENT(0,
"... device %04X on subchannel %04X,"
" dev_stat: %02X sch_stat: %02X\n",
ioinfo[irq]->devstat.devno, irq,
dp->dstat, dp->cstat);
chnchk = 1;
}
if (dp->ii.irb.scsw.ectl == 0) {
issense = 0;
} else if ((dp->ii.irb.scsw.stctl == SCSW_STCTL_STATUS_PEND)
&& (dp->ii.irb.scsw.eswf == 0)) {
issense = 0;
} else if ((dp->ii.irb.scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_INTER_STATUS))
&& ((dp->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED) == 0)) {
issense = 0;
} else {
issense = dp->ii.irb.esw.esw0.erw.cons;
}
if (issense) {
dp->scnt = dp->ii.irb.esw.esw0.erw.scnt;
dp->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
sdevstat = sizeof (devstat_t);
#ifdef CONFIG_DEBUG_IO
if (irq != cons_dev)
printk (KERN_DEBUG "s390_process_IRQ( %04X ) : "
"concurrent sense bytes avail %d\n",
irq, dp->scnt);
#endif
CIO_MSG_EVENT(4,
"s390_process_IRQ( %04X ): "
"concurrent sense bytes avail %d\n",
irq, dp->scnt);
} else {
/* don't copy the sense data area ! */
sdevstat = sizeof (devstat_t) - SENSE_MAX_COUNT;
}
switch (irb_cc) {
case 1: /* status pending */
dp->flag |= DEVSTAT_STATUS_PENDING;
case 0: /* normal i/o interruption */
fctl = dp->ii.irb.scsw.fctl;
stctl = dp->ii.irb.scsw.stctl;
actl = dp->ii.irb.scsw.actl;
if (chnchk) {
sprintf (buffer, "s390_process_IRQ(%04X) - irb for "
"device %04X after channel check "
"or interface control check\n",
irq, dp->devno);
s390_displayhex (buffer, &(dp->ii.irb), sizeof (irb_t));
sprintf(dbf_txt, "chk%x", irq);
CIO_TRACE_EVENT(0, dbf_txt);
CIO_HEX_EVENT(0, &(dp->ii.irb), sizeof (irb_t));
}
ioinfo[irq]->stctl |= stctl;
ending_status = (stctl & SCSW_STCTL_SEC_STATUS)
|| (stctl ==
(SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))
|| (stctl == SCSW_STCTL_STATUS_PEND);
/*
* Check for unsolicited interrupts - for debug purposes only
*
* We only consider an interrupt as unsolicited, if the device was not
* actively in use (busy) and an interrupt other than an ALERT status
* was received.
*
* Note: We must not issue a message to the console, if the
* unsolicited interrupt applies to the console device
* itself !
*/
if (!(stctl & SCSW_STCTL_ALERT_STATUS)
&& (ioinfo[irq]->ui.flags.busy == 0)) {
#ifdef CONFIG_DEBUG_IO
if (irq != cons_dev)
printk (KERN_INFO
"Unsolicited interrupt received for "
"device %04X on subchannel %04X\n"
" ... device status : %02X "
"subchannel status : %02X\n",
dp->devno, irq, dp->dstat, dp->cstat);
sprintf (buffer, "s390_process_IRQ(%04X) - irb for "
"device %04X, ending_status %d\n",
irq, dp->devno, ending_status);
s390_displayhex (buffer, &(dp->ii.irb), sizeof (irb_t));
#endif
CIO_MSG_EVENT(2,
"Unsolicited interrupt "
"received for device %04X "
"on subchannel %04X\n"
" ... device status : %02X "
"subchannel status : %02X\n",
dp->devno,
irq, dp->dstat, dp->cstat);
sprintf(dbf_txt, "uint%x", irq);
CIO_TRACE_EVENT(2, dbf_txt);
CIO_HEX_EVENT(2, &(dp->ii.irb), sizeof (irb_t));
}
/*
* take fast exit if no handler is available
*/
if (!ioinfo[irq]->ui.flags.ready)
return (ending_status);
/*
* Check whether we must issue a SENSE CCW ourselves if there is no
* concurrent sense facility installed for the subchannel.
*
* Note: We should check for ioinfo[irq]->ui.flags.consns but VM
* violates the ESA/390 architecture and doesn't present an
* operand exception for virtual devices without concurrent
* sense facility available/supported when enabling the
* concurrent sense facility.
*/
if (((dp->ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK)
&& (!issense))
|| (ioinfo[irq]->ui.flags.delsense && ending_status)) {
int ret_io;
ccw1_t *s_ccw = &ioinfo[irq]->senseccw;
unsigned long s_flag = 0;
if (ending_status) {
/* there is a chance that the command
* that gave us the unit check actually
* was a basic sense, so we must not
* overwrite *udp in that case
*/
if (ioinfo[irq]->ui.flags.w4sense &&
(dp->ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK)) {
CIO_MSG_EVENT(4,"double unit check irq %04x, dstat %02x,"
"flags %8x\n", irq, dp->ii.irb.scsw.dstat,
ioinfo[irq]->ui.info, ending_status);
} else {
/*
* We copy the current status information into the device driver
* status area. Then we can use the local devstat area for device
* sensing. When finally calling the IRQ handler we must not overlay
* the original device status but copy the sense data only.
*/
memcpy (udp, dp, sizeof (devstat_t));
}
s_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
s_ccw->cda =
(__u32) virt_to_phys (ioinfo[irq]->
sense_data);
s_ccw->count = SENSE_MAX_COUNT;
s_ccw->flags = CCW_FLAG_SLI;
/*
* If free_irq() or a sync do_IO/s390_start_IO() is in
* process we have to sense synchronously
*/
if (ioinfo[irq]->ui.flags.unready
|| ioinfo[irq]->ui.flags.syncio)
s_flag = DOIO_WAIT_FOR_INTERRUPT
| DOIO_TIMEOUT
| DOIO_VALID_LPM;
else
s_flag = DOIO_VALID_LPM;
/*
* Reset status info
*
* It does not matter whether this is a sync. or async.
* SENSE request, but we have to assure we don't call
* the irq handler now, but keep the irq in busy state.
* In sync. mode s390_process_IRQ() is called recursively,
* while in async. mode we re-enter do_IRQ() with the
* next interrupt.
*
* Note : this may be a delayed sense request !
*/
allow4handler = 0;
ioinfo[irq]->ui.flags.fast = 0;
ioinfo[irq]->ui.flags.repall = 0;
ioinfo[irq]->ui.flags.w4final = 0;
ioinfo[irq]->ui.flags.delsense = 0;
dp->cstat = 0;
dp->dstat = 0;
dp->rescnt = SENSE_MAX_COUNT;
ioinfo[irq]->ui.flags.w4sense = 1;
ret_io = s390_start_IO (irq, s_ccw, 0xE2C5D5E2, /* = SENSe */
0xff,
s_flag);
switch (ret_io) {
case 0: /* OK */
break;
case -ENODEV:
/*
* The device is no longer operational.
* We won't get any sense data.
*/
ioinfo[irq]->ui.flags.w4sense = 0;
ioinfo[irq]->ui.flags.oper = 0;
allow4handler = 1; /* to notify the driver */
break;
case -EBUSY:
/*
* The channel subsystem is either busy, or we have
* a status pending. Retry later.
*/
ioinfo[irq]->ui.flags.w4sense = 0;
ioinfo[irq]->ui.flags.delsense = 1;
break;
default:
printk(KERN_ERR"irq %04X: Unexpected rc %d "
"for BASIC SENSE!\n", irq, ret_io);
ioinfo[irq]->ui.flags.w4sense = 0;
allow4handler = 1;
}
} 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;
}
}
/*
* 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);
}
/*
* 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 (KERN_DEBUG
"s390_process_IRQ( %04X ) : "
"BASIC SENSE bytes avail %d\n",
irq, sense_count);
#endif
CIO_MSG_EVENT(4,
"s390_process_IRQ( %04X ): "
"BASIC SENSE bytes avail %d\n",
irq, sense_count);
ioinfo[irq]->ui.flags.w4sense = 0;
udp->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
udp->scnt = sense_count;
if (sense_count > 0) {
memcpy (udp->ii.sense.data,
ioinfo[irq]->sense_data,
sense_count);
} else if (sense_count == 0) {
udp->flag &= ~DEVSTAT_FLAG_SENSE_AVAIL;
} else {
panic
("s390_process_IRQ(%04x) encountered "
"negative sense count\n", irq);
}
} else {
memcpy (udp, dp, sdevstat);
}
}
/*
* for status pending situations other than deferred interrupt
* conditions detected by s390_process_IRQ() itself we must not
* call the handler. This will synchronously be reported back
* to the caller instead, e.g. when detected during do_IO().
*/
if (ioinfo[irq]->ui.flags.s_pend
|| ioinfo[irq]->ui.flags.unready
|| ioinfo[irq]->ui.flags.repnone) {
if (ending_status) {
ioinfo[irq]->ui.flags.busy = 0;
ioinfo[irq]->ui.flags.doio = 0;
ioinfo[irq]->ui.flags.haltio = 0;
ioinfo[irq]->ui.flags.fast = 0;
ioinfo[irq]->ui.flags.repall = 0;
ioinfo[irq]->ui.flags.w4final = 0;
dp->flag |= DEVSTAT_FINAL_STATUS;
udp->flag |= DEVSTAT_FINAL_STATUS;
}
allow4handler = 0;
}
/*
* Call device action handler if applicable
*/
if (allow4handler) {
/*
* We only reset the busy condition when we are sure that no further
* interrupt is pending for the current I/O request (ending_status).
*/
if (ending_status || !ioinfo[irq]->ui.flags.oper) {
ioinfo[irq]->ui.flags.oper = 1; /* dev IS oper */
ioinfo[irq]->ui.flags.busy = 0;
ioinfo[irq]->ui.flags.doio = 0;
ioinfo[irq]->ui.flags.haltio = 0;
ioinfo[irq]->ui.flags.fast = 0;
ioinfo[irq]->ui.flags.repall = 0;
ioinfo[irq]->ui.flags.w4final = 0;
dp->flag |= DEVSTAT_FINAL_STATUS;
udp->flag |= DEVSTAT_FINAL_STATUS;
if (!ioinfo[irq]->ui.flags.killio)
ioinfo[irq]->irq_desc.handler (irq, udp, NULL);
/*
* reset intparm after final status or we will badly present unsolicited
* interrupts with a intparm value possibly no longer valid.
*/
dp->intparm = 0;
} else {
ioinfo[irq]->ui.flags.w4final = 1;
/*
* Eventually reset subchannel PCI status and
* set the PCI or SUSPENDED flag in the user
* device status block if appropriate.
*/
if (dp->cstat & SCHN_STAT_PCI) {
udp->flag |= DEVSTAT_PCI;
dp->cstat &= ~SCHN_STAT_PCI;
}
if (actl & SCSW_ACTL_SUSPENDED) {
udp->flag |= DEVSTAT_SUSPENDED;
}
ioinfo[irq]->irq_desc.handler (irq, udp, NULL);
}
}
break;
case 3: /* device/path not operational */
ioinfo[irq]->ui.flags.busy = 0;
ioinfo[irq]->ui.flags.doio = 0;
ioinfo[irq]->ui.flags.haltio = 0;
dp->cstat = 0;
dp->dstat = 0;
if ((dp->ii.irb.scsw.fctl != 0) &&
((dp->ii.irb.scsw.stctl & SCSW_STCTL_STATUS_PEND) != 0) &&
(((dp->ii.irb.scsw.stctl & SCSW_STCTL_INTER_STATUS) == 0) ||
((dp->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED) != 0)))
if (dp->ii.irb.scsw.pno) {
stsch(irq, &ioinfo[irq]->schib);
ioinfo[irq]->opm &=
~ioinfo[irq]->schib.pmcw.pnom;
}
if (ioinfo[irq]->opm == 0) {
ioinfo[irq]->ui.flags.oper = 0;
}
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.
*/
ioinfo[irq]->ui.flags.fast = 0;
ioinfo[irq]->ui.flags.repall = 0;
ioinfo[irq]->ui.flags.w4final = 0;
/*
* take fast exit if no handler is available
*/
if (!ioinfo[irq]->ui.flags.ready)
return (ending_status);
memcpy (udp, &(ioinfo[irq]->devstat), sdevstat);
ioinfo[irq]->devstat.intparm = 0;
if (!ioinfo[irq]->ui.flags.s_pend
&& !ioinfo[irq]->ui.flags.repnone
&& !ioinfo[irq]->ui.flags.killio) {
ioinfo[irq]->irq_desc.handler (irq, udp, NULL);
}
ending_status = 1;
break;
}
if (ending_status &&
ioinfo[irq]->ui.flags.noio &&
!ioinfo[irq]->ui.flags.syncio &&
!ioinfo[irq]->ui.flags.w4sense) {
if(ioinfo[irq]->ui.flags.ready) {
s390_schedule_path_verification(irq);
} else {
ioinfo[irq]->ui.flags.killio = 0;
ioinfo[irq]->ui.flags.noio = 0;
}
}
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().
* 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;
int rc = 0;
char dbf_txt[15];
SANITY_CHECK (irq);
if (cons_dev != -1)
return -EBUSY;
sprintf (dbf_txt, "scons%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* modify the indicated console device to operate
* on special console interrupt sublass 7
*/
ccode = stsch (irq, &(ioinfo[irq]->schib));
if (ccode) {
rc = -ENODEV;
ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
} else {
ioinfo[irq]->schib.pmcw.isc = 7;
ccode = msch (irq, &(ioinfo[irq]->schib));
if (ccode) {
rc = -EIO;
} else {
cons_dev = irq;
/*
* enable console I/O-interrupt sublass 7
*/
ctl_set_bit (6, 24);
}
}
return (rc);
}
int
wait_cons_dev (int irq)
{
int rc = 0;
long save_cr6;
char dbf_txt[15];
if (irq != cons_dev)
return -EINVAL;
sprintf (dbf_txt, "wcons%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* before entering the spinlock we may already have
* processed the interrupt on a different CPU ...
*/
if (ioinfo[irq]->ui.flags.busy == 1) {
long cr6 __attribute__ ((aligned (8)));
/*
* disable all, but isc 7 (console device)
*/
__ctl_store (cr6, 6, 6);
save_cr6 = cr6;
cr6 &= 0x01FFFFFF;
__ctl_load (cr6, 6, 6);
do {
tpi_info_t tpi_info = { 0, };
if (tpi (&tpi_info) == 1) {
s390_process_IRQ (tpi_info.irq);
} else {
s390irq_spin_unlock (irq);
udelay (100);
s390irq_spin_lock (irq);
}
eieio ();
} while (ioinfo[irq]->ui.flags.busy == 1);
/*
* restore previous isc value
*/
cr6 = save_cr6;
__ctl_load (cr6, 6, 6);
}
return (rc);
}
int
enable_cpu_sync_isc (int irq)
{
int ccode;
long cr6 __attribute__ ((aligned (8)));
int retry = 3;
int rc = 0;
char dbf_txt[15];
sprintf (dbf_txt, "enisc%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/* This one spins until it can get the sync_isc lock for irq# irq */
if ((irq <= highest_subchannel) &&
(ioinfo[irq] != INVALID_STORAGE_AREA) &&
(!ioinfo[irq]->st)) {
if (atomic_read (&sync_isc) != irq)
atomic_compare_and_swap_spin (-1, irq, &sync_isc);
sync_isc_cnt++;
if (sync_isc_cnt > 255) { /* fixme : magic number */
panic ("Too many recursive calls to enable_sync_isc");
}
/*
* we only run the STSCH/MSCH path for the first enablement
*/
else if (sync_isc_cnt == 1) {
ccode = stsch (irq, &(ioinfo[irq]->schib));
if (!ccode) {
ioinfo[irq]->schib.pmcw.isc = 5;
do {
ccode = msch (irq,
&(ioinfo[irq]->schib));
switch (ccode) {
case 0:
/*
* enable special isc
*/
__ctl_store (cr6, 6, 6);
/* enable sync isc 5 */
cr6 |= 0x04000000;
/* disable standard isc 3 */
cr6 &= 0xEFFFFFFF;
/* disable console isc 7 */
cr6 &= 0xFEFFFFFF;
ioinfo[irq]->ui.flags.syncio = 1;
__ctl_load (cr6, 6, 6);
rc = 0;
retry = 0;
break;
case 1:
/*
* process pending status
*/
ioinfo[irq]->ui.flags.s_pend =
1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend =
0;
rc = -EIO; /* might be overwritten... */
retry--;
break;
case 2: /* busy */
retry = 0;
rc = -EBUSY;
break;
case 3: /* not oper */
retry = 0;
rc = -ENODEV;
break;
}
} while (retry);
} else {
rc = -ENODEV; /* device is not-operational */
}
}
if (rc) { /* can only happen if stsch/msch fails */
sync_isc_cnt = 0;
atomic_set (&sync_isc, -1);
} else if (sync_isc_cnt == 1) {
int ccode;
ccode = stsch(irq, &ioinfo[irq]->schib);
if (!ccode && ioinfo[irq]->schib.pmcw.isc != 5) {
ioinfo[irq]->ui.flags.syncio = 0;
sync_isc_cnt = 0;
atomic_set (&sync_isc, -1);
}
}
} else {
#ifdef CONFIG_SYNC_ISC_PARANOIA
panic ("enable_sync_isc: called with invalid %x\n", irq);
#endif
rc = -EINVAL;
}
return (rc);
}
int
disable_cpu_sync_isc (int irq)
{
int rc = 0;
int retry1 = 5;
int retry2 = 5;
int clear_pend = 0;
int ccode;
long cr6 __attribute__ ((aligned (8)));
char dbf_txt[15];
sprintf (dbf_txt, "disisc%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
if ((irq <= highest_subchannel) &&
(ioinfo[irq] != INVALID_STORAGE_AREA) &&
(!ioinfo[irq]->st)) {
/*
* We disable if we're the top user only, as we may
* run recursively ...
* We must not decrease the count immediately; during
* msch() processing we may face another pending
* status we have to process recursively (sync).
*/
#ifdef CONFIG_SYNC_ISC_PARANOIA
if (atomic_read (&sync_isc) != irq)
panic
("disable_sync_isc: called for %x while %x locked\n",
irq, atomic_read (&sync_isc));
#endif
if (sync_isc_cnt == 1) {
ccode = stsch (irq, &(ioinfo[irq]->schib));
ioinfo[irq]->schib.pmcw.isc = 3;
do {
retry2 = 5;
do {
ccode =
msch (irq, &(ioinfo[irq]->schib));
switch (ccode) {
case 0:
/*
* disable special interrupt subclass in CPU
*/
__ctl_store (cr6, 6, 6);
/* disable sync isc 5 */
cr6 &= 0xFBFFFFFF;
/* enable standard isc 3 */
cr6 |= 0x10000000;
/* enable console isc 7 */
cr6 |= 0x01000000;
__ctl_load (cr6, 6, 6);
retry2 = 0;
break;
case 1: /* status pending */
ioinfo[irq]->ui.flags.s_pend =
1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend =
0;
retry2--;
break;
case 2: /* busy */
retry2--;
udelay (100); /* give it time */
break;
default: /* not oper */
retry2 = 0;
break;
}
} while (retry2);
retry1--;
/* try stopping it ... */
if ((ccode) && !clear_pend) {
clear_IO (irq, 0x00004711, 0);
clear_pend = 1;
}
udelay (100);
} while (retry1 && ccode);
ioinfo[irq]->ui.flags.syncio = 0;
sync_isc_cnt = 0;
atomic_set (&sync_isc, -1);
} else {
sync_isc_cnt--;
}
} else {
#ifdef CONFIG_SYNC_ISC_PARANOIA
if (atomic_read (&sync_isc) != -1)
panic
("disable_sync_isc: called with invalid %x while %x locked\n",
irq, atomic_read (&sync_isc));
#endif
rc = -EINVAL;
}
return (rc);
}
int diag210 (diag210_t *addr)
{
int ccode;
__asm__ __volatile__(
#ifdef CONFIG_ARCH_S390X
" sam31\n"
" diag %1,0,0x210\n"
" sam64\n"
#else
" diag %1,0,0x210\n"
#endif
" ipm %0\n"
" srl %0,28"
: "=d" (ccode)
: "a" (addr)
: "cc" );
return ccode;
}
/*
* Input :
* devno - device number
* ps - pointer to sense ID data area
* Output : none
*/
void
VM_virtual_device_info (__u16 devno, senseid_t * ps)
{
diag210_t *p_diag_data;
int ccode;
int error = 0;
CIO_TRACE_EVENT (4, "VMvdinf");
if (init_IRQ_complete) {
p_diag_data = kmalloc (sizeof (diag210_t), GFP_DMA | GFP_ATOMIC);
} else {
p_diag_data = alloc_bootmem_low (sizeof (diag210_t));
}
if (!p_diag_data)
return;
p_diag_data->vrdcdvno = devno;
p_diag_data->vrdclen = sizeof (diag210_t);
ccode = diag210 ((diag210_t *) virt_to_phys (p_diag_data));
ps->reserved = 0xff;
switch (p_diag_data->vrdcvcla) {
case 0x80:
switch (p_diag_data->vrdcvtyp) {
case 00:
ps->cu_type = 0x3215;
break;
default:
error = 1;
break;
}
break;
case 0x40:
switch (p_diag_data->vrdcvtyp) {
case 0xC0:
ps->cu_type = 0x5080;
break;
case 0x80:
ps->cu_type = 0x2250;
break;
case 0x04:
ps->cu_type = 0x3277;
break;
case 0x01:
ps->cu_type = 0x3278;
break;
default:
error = 1;
break;
}
break;
case 0x20:
switch (p_diag_data->vrdcvtyp) {
case 0x84:
ps->cu_type = 0x3505;
break;
case 0x82:
ps->cu_type = 0x2540;
break;
case 0x81:
ps->cu_type = 0x2501;
break;
default:
error = 1;
break;
}
break;
case 0x10:
switch (p_diag_data->vrdcvtyp) {
case 0x84:
ps->cu_type = 0x3525;
break;
case 0x82:
ps->cu_type = 0x2540;
break;
case 0x4F:
case 0x4E:
case 0x48:
ps->cu_type = 0x3820;
break;
case 0x4D:
case 0x49:
case 0x45:
ps->cu_type = 0x3800;
break;
case 0x4B:
ps->cu_type = 0x4248;
break;
case 0x4A:
ps->cu_type = 0x4245;
break;
case 0x47:
ps->cu_type = 0x3262;
break;
case 0x43:
ps->cu_type = 0x3203;
break;
case 0x42:
ps->cu_type = 0x3211;
break;
case 0x41:
ps->cu_type = 0x1403;
break;
default:
error = 1;
break;
}
break;
case 0x08:
switch (p_diag_data->vrdcvtyp) {
case 0x82:
ps->cu_type = 0x3422;
break;
case 0x81:
ps->cu_type = 0x3490;
break;
case 0x10:
ps->cu_type = 0x3420;
break;
case 0x02:
ps->cu_type = 0x3430;
break;
case 0x01:
ps->cu_type = 0x3480;
break;
case 0x42:
ps->cu_type = 0x3424;
break;
case 0x44:
ps->cu_type = 0x9348;
break;
default:
error = 1;
break;
}
break;
case 02: /* special device class ... */
switch (p_diag_data->vrdcvtyp) {
case 0x20: /* OSA */
ps->cu_type = 0x3088;
ps->cu_model = 0x60;
break;
default:
error = 1;
break;
}
break;
default:
error = 1;
break;
}
if (init_IRQ_complete) {
kfree (p_diag_data);
} else {
free_bootmem ((unsigned long) p_diag_data, sizeof (diag210_t));
}
if (error) {
printk (KERN_ERR "DIAG X'210' for "
"device %04X returned "
"(cc = %d): vdev class : %02X, "
"vdev type : %04X \n"
" ... rdev class : %02X, rdev type : %04X, "
"rdev model: %02X\n",
devno,
ccode,
p_diag_data->vrdcvcla,
p_diag_data->vrdcvtyp,
p_diag_data->vrdcrccl,
p_diag_data->vrdccrty, p_diag_data->vrdccrmd);
CIO_MSG_EVENT(0,
"DIAG X'210' for "
"device %04X returned "
"(cc = %d): vdev class : %02X, "
"vdev type : %04X \n ... "
"rdev class : %02X, rdev type : %04X, "
"rdev model: %02X\n",
devno,
ccode,
p_diag_data->vrdcvcla,
p_diag_data->vrdcvtyp,
p_diag_data->vrdcrccl,
p_diag_data->vrdccrty,
p_diag_data->vrdccrmd);
}
}
/*
* This routine returns the characteristics for the device
* specified. Some old devices might not provide the necessary
* command code information during SenseID processing. In this
* case the function returns -EINVAL. Otherwise the function
* allocates a decice specific data buffer and provides the
* device characteristics together with the buffer size. Its
* the callers responability to release the kernel memory if
* not longer needed. In case of persistent I/O problems -EBUSY
* is returned.
*
* The function may be called enabled or disabled. However, the
* caller must have locked the irq it is requesting data for.
*
* Note : It would have been nice to collect this information
* during init_IRQ() processing but this is not possible
*
* a) without statically pre-allocation fixed size buffers
* as virtual memory management isn't available yet.
*
* b) without unnecessarily increase system startup by
* evaluating devices eventually not used at all.
*/
int
read_dev_chars (int irq, void **buffer, int length)
{
unsigned long flags;
ccw1_t *rdc_ccw;
devstat_t devstat;
char *rdc_buf;
int devflag = 0;
int ret = 0;
int emulated = 0;
int retry = 5;
char dbf_txt[15];
if (!buffer || !length) {
return (-EINVAL);
}
SANITY_CHECK (irq);
if (ioinfo[irq]->ui.flags.oper == 0) {
return (-ENODEV);
}
if (ioinfo[irq]->ui.flags.unfriendly) {
/* don't even try it */
return -EUSERS;
}
sprintf (dbf_txt, "rddevch%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* Before playing around with irq locks we should assure
* running disabled on (just) our CPU. Sync. I/O requests
* also require to run disabled.
*
* Note : as no global lock is required, we must not use
* cli(), but __cli() instead.
*/
__save_flags (flags);
__cli ();
rdc_ccw = &ioinfo[irq]->senseccw;
if (!ioinfo[irq]->ui.flags.ready) {
ret = request_irq (irq,
init_IRQ_handler, SA_PROBE, "RDC", &devstat);
if (!ret) {
emulated = 1;
}
}
if (!ret) {
if (!*buffer) {
rdc_buf = kmalloc (length, GFP_KERNEL);
} else {
rdc_buf = *buffer;
}
if (!rdc_buf) {
ret = -ENOMEM;
} else {
do {
rdc_ccw->cmd_code = CCW_CMD_RDC;
rdc_ccw->count = length;
rdc_ccw->flags = CCW_FLAG_SLI;
ret =
set_normalized_cda (rdc_ccw, rdc_buf);
if (!ret) {
memset (ioinfo[irq]->irq_desc.dev_id,
'\0', sizeof (devstat_t));
ret = s390_start_IO (irq, rdc_ccw, 0x00524443, /* RDC */
0, /* n/a */
DOIO_WAIT_FOR_INTERRUPT
|
DOIO_DONT_CALL_INTHDLR);
retry--;
devflag =
ioinfo[irq]->irq_desc.dev_id->flag;
clear_normalized_cda (rdc_ccw);
} else {
udelay (100); /* wait for recovery */
retry--;
}
} while ((retry)
&& (ret
|| (devflag & DEVSTAT_STATUS_PENDING)));
}
if (!retry) {
ret = (ret == -ENOMEM) ? -ENOMEM : -EBUSY;
}
__restore_flags (flags);
/*
* on success we update the user input parms
*/
if (!ret) {
*buffer = rdc_buf;
}
if (emulated) {
free_irq (irq, &devstat);
}
} else {
__restore_flags (flags);
}
return (ret);
}
/*
* Read Configuration data
*/
int
read_conf_data (int irq, void **buffer, int *length, __u8 lpm)
{
unsigned long flags;
int ciw_cnt;
int found = 0; /* RCD CIW found */
int ret = 0; /* return code */
char dbf_txt[15];
SANITY_CHECK (irq);
if (!buffer || !length) {
return (-EINVAL);
} else if (ioinfo[irq]->ui.flags.oper == 0) {
return (-ENODEV);
} else if (ioinfo[irq]->ui.flags.esid == 0) {
*buffer = NULL;
*length = 0;
return (-EOPNOTSUPP);
}
if (ioinfo[irq]->ui.flags.unfriendly) {
/* don't even try it */
return -EUSERS;
}
sprintf (dbf_txt, "rdconf%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* scan for RCD command in extended SenseID data
*/
for (ciw_cnt = 0; (found == 0) && (ciw_cnt < MAX_CIWS); ciw_cnt++) {
if (ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD) {
/*
* paranoia check ...
*/
if (ioinfo[irq]->senseid.ciw[ciw_cnt].cmd != 0
&& ioinfo[irq]->senseid.ciw[ciw_cnt].count != 0) {
found = 1;
}
break;
}
}
if (found) {
devstat_t devstat; /* inline device status area */
devstat_t *pdevstat;
int ioflags;
ccw1_t *rcd_ccw = &ioinfo[irq]->senseccw;
char *rcd_buf = NULL;
int emulated = 0; /* no i/O handler installed */
int retry = 5; /* retry count */
__save_flags (flags);
__cli ();
if (!ioinfo[irq]->ui.flags.ready) {
pdevstat = &devstat;
ret = request_irq (irq,
init_IRQ_handler,
SA_PROBE, "RCD", pdevstat);
if (!ret) {
emulated = 1;
} /* endif */
} else {
pdevstat = ioinfo[irq]->irq_desc.dev_id;
} /* endif */
if (!ret) {
if (init_IRQ_complete) {
rcd_buf =
kmalloc (ioinfo[irq]->senseid.ciw[ciw_cnt].
count, GFP_DMA | GFP_ATOMIC);
} else {
rcd_buf =
alloc_bootmem_low (ioinfo[irq]->senseid.
ciw[ciw_cnt].count);
}
if (rcd_buf == NULL) {
ret = -ENOMEM;
}
if (!ret) {
memset (rcd_buf,
'\0',
ioinfo[irq]->senseid.ciw[ciw_cnt].
count);
do {
rcd_ccw->cmd_code =
ioinfo[irq]->senseid.ciw[ciw_cnt].
cmd;
rcd_ccw->cda =
(__u32) virt_to_phys (rcd_buf);
rcd_ccw->count =
ioinfo[irq]->senseid.ciw[ciw_cnt].
count;
rcd_ccw->flags = CCW_FLAG_SLI;
memset (pdevstat, '\0',
sizeof (devstat_t));
if (lpm) {
ioflags =
DOIO_WAIT_FOR_INTERRUPT |
DOIO_VALID_LPM |
DOIO_DONT_CALL_INTHDLR;
} else {
ioflags =
DOIO_WAIT_FOR_INTERRUPT |
DOIO_DONT_CALL_INTHDLR;
}
ret = s390_start_IO (irq, rcd_ccw, 0x00524344, /* == RCD */
lpm, ioflags);
switch (ret) {
case 0:
case -EIO:
if (!
(pdevstat->
flag &
(DEVSTAT_STATUS_PENDING |
DEVSTAT_NOT_OPER |
DEVSTAT_FLAG_SENSE_AVAIL)))
{
retry = 0; /* we got it ... */
} else {
retry--; /* try again ... */
}
break;
default: /* -EBUSY, -ENODEV, ??? */
retry = 0;
}
} while (retry);
}
}
__restore_flags (flags);
/*
* on success we update the user input parms
*/
if (ret == 0) {
*length = ioinfo[irq]->senseid.ciw[ciw_cnt].count;
*buffer = rcd_buf;
} else {
if (rcd_buf != NULL) {
if (init_IRQ_complete) {
kfree (rcd_buf);
} else {
free_bootmem ((unsigned long) rcd_buf,
ioinfo[irq]->senseid.
ciw[ciw_cnt].count);
}
}
*buffer = NULL;
*length = 0;
}
if (emulated)
free_irq (irq, pdevstat);
} else {
*buffer = NULL;
*length = 0;
ret = -EOPNOTSUPP;
}
return (ret);
}
int
get_dev_info (int irq, s390_dev_info_t * pdi)
{
return (get_dev_info_by_irq (irq, pdi));
}
static int __inline__
get_next_available_irq (ioinfo_t * pi)
{
int ret_val = -ENODEV;
while (pi != NULL) {
if ((!pi->st)
&& (pi->ui.flags.oper)
&& (!pi->ui.flags.unfriendly)) {
ret_val = pi->irq;
break;
} else {
pi = pi->next;
}
}
return ret_val;
}
int
get_irq_first (void)
{
int ret_irq;
if (ioinfo_head) {
if ((ioinfo_head->ui.flags.oper) &&
(!ioinfo_head->ui.flags.unfriendly) &&
(!ioinfo_head->st)) {
ret_irq = ioinfo_head->irq;
} else if (ioinfo_head->next) {
ret_irq = get_next_available_irq (ioinfo_head->next);
} else {
ret_irq = -ENODEV;
}
} else {
ret_irq = -ENODEV;
}
return ret_irq;
}
int
get_irq_next (int irq)
{
int ret_irq;
if (ioinfo[irq] != INVALID_STORAGE_AREA) {
if (ioinfo[irq]->next) {
if ((ioinfo[irq]->next->ui.flags.oper) &&
(!ioinfo[irq]->next->ui.flags.unfriendly) &&
(!ioinfo[irq]->next->st)) {
ret_irq = ioinfo[irq]->next->irq;
} else {
ret_irq =
get_next_available_irq (ioinfo[irq]->next);
}
} else {
ret_irq = -ENODEV;
}
} else {
ret_irq = -EINVAL;
}
return ret_irq;
}
int
get_dev_info_by_irq (int irq, s390_dev_info_t * pdi)
{
SANITY_CHECK (irq);
if (pdi == NULL)
return -EINVAL;
pdi->devno = ioinfo[irq]->schib.pmcw.dev;
pdi->irq = irq;
if (ioinfo[irq]->ui.flags.oper && !ioinfo[irq]->ui.flags.unknown) {
pdi->status = 0;
memcpy (&(pdi->sid_data),
&ioinfo[irq]->senseid, sizeof (senseid_t));
} else if (ioinfo[irq]->ui.flags.unfriendly) {
pdi->status = DEVSTAT_UNFRIENDLY_DEV;
memset (&(pdi->sid_data), '\0', sizeof (senseid_t));
pdi->sid_data.cu_type = 0xFFFF;
} else if (ioinfo[irq]->ui.flags.unknown) {
pdi->status = DEVSTAT_UNKNOWN_DEV;
memset (&(pdi->sid_data), '\0', sizeof (senseid_t));
pdi->sid_data.cu_type = 0xFFFF;
} else {
pdi->status = DEVSTAT_NOT_OPER;
memset (&(pdi->sid_data), '\0', sizeof (senseid_t));
pdi->sid_data.cu_type = 0xFFFF;
}
if (ioinfo[irq]->ui.flags.ready)
pdi->status |= DEVSTAT_DEVICE_OWNED;
return 0;
}
int
get_dev_info_by_devno (__u16 devno, s390_dev_info_t * pdi)
{
int i;
int rc = -ENODEV;
if (devno > 0x0000ffff)
return -ENODEV;
if (pdi == NULL)
return -EINVAL;
for (i = 0; i <= highest_subchannel; i++) {
if ((ioinfo[i] != INVALID_STORAGE_AREA) &&
(!ioinfo[i]->st) &&
(ioinfo[i]->schib.pmcw.dev == devno)) {
pdi->irq = i;
pdi->devno = devno;
if (ioinfo[i]->ui.flags.oper
&& !ioinfo[i]->ui.flags.unknown) {
pdi->status = 0;
memcpy (&(pdi->sid_data),
&ioinfo[i]->senseid,
sizeof (senseid_t));
} else if (ioinfo[i]->ui.flags.unfriendly) {
pdi->status = DEVSTAT_UNFRIENDLY_DEV;
memset (&(pdi->sid_data), '\0',
sizeof (senseid_t));
pdi->sid_data.cu_type = 0xFFFF;
} else if (ioinfo[i]->ui.flags.unknown) {
pdi->status = DEVSTAT_UNKNOWN_DEV;
memset (&(pdi->sid_data),
'\0', sizeof (senseid_t));
pdi->sid_data.cu_type = 0xFFFF;
} else {
pdi->status = DEVSTAT_NOT_OPER;
memset (&(pdi->sid_data),
'\0', sizeof (senseid_t));
pdi->sid_data.cu_type = 0xFFFF;
}
if (ioinfo[i]->ui.flags.ready)
pdi->status |= DEVSTAT_DEVICE_OWNED;
if (!ioinfo[i]->ui.flags.unfriendly)
rc = 0; /* found */
else
rc = -EUSERS;
break;
}
}
return (rc);
}
int
get_irq_by_devno (__u16 devno)
{
int i;
int rc = -1;
if (devno <= 0x0000ffff) {
for (i = 0; i <= highest_subchannel; i++) {
if ((ioinfo[i] != INVALID_STORAGE_AREA)
&& (!ioinfo[i]->st)
&& (ioinfo[i]->schib.pmcw.dev == devno)
&& (ioinfo[i]->schib.pmcw.dnv == 1)) {
rc = i;
break;
}
}
}
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]->st)
return -1;
/*
* we don't need to check for the device be operational
* as the initial STSCH will always present the device
* number defined by the IOCDS regardless of the device
* existing or not. However, there could be subchannels
* defined who's device number isn't valid ...
*/
if (ioinfo[irq]->schib.pmcw.dnv)
return (ioinfo[irq]->schib.pmcw.dev);
else
return -1;
}
/*
* s390_device_recognition_irq
*
* Used for individual device recognition. Issues the device
* independant SenseID command to obtain info the device type.
*
*/
void
s390_device_recognition_irq (int irq)
{
int ret;
char dbf_txt[15];
sprintf (dbf_txt, "devrec%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* We issue the SenseID command on I/O subchannels we think are
* operational only.
*/
if ((ioinfo[irq] != INVALID_STORAGE_AREA)
&& (!ioinfo[irq]->st)
&& (ioinfo[irq]->schib.pmcw.st == 0)
&& (ioinfo[irq]->ui.flags.oper == 1)) {
int irq_ret;
devstat_t devstat;
if (ioinfo[irq]->ui.flags.pgid_supp)
irq_ret = request_irq (irq,
init_IRQ_handler,
SA_PROBE | SA_DOPATHGROUP,
"INIT", &devstat);
else
irq_ret = request_irq (irq,
init_IRQ_handler,
SA_PROBE, "INIT", &devstat);
if (!irq_ret) {
ret = enable_cpu_sync_isc (irq);
if (!ret) {
ioinfo[irq]->ui.flags.unknown = 0;
memset (&ioinfo[irq]->senseid, '\0',
sizeof (senseid_t));
if (cio_sid_with_pgid) {
ret = s390_DevicePathVerification(irq,0);
if (ret == -EOPNOTSUPP)
/*
* Doesn't prevent us from proceeding
*/
ret = 0;
}
/*
* we'll fallthrough here if we don't want
* to do SPID before SID
*/
if (!ret) {
ret = s390_SenseID (irq, &ioinfo[irq]->senseid, 0xff);
if (ret == -ETIMEDOUT) {
/* SenseID timed out.
* We consider this device to be
* boxed for now.
*/
ioinfo[irq]->ui.flags.unfriendly = 1;
}
#if 0 /* FIXME */
/*
* We initially check the configuration data for
* those devices with more than a single path
*/
if (ioinfo[irq]->schib.pmcw.pim != 0x80) {
char *prcd;
int lrcd;
ret =
read_conf_data (irq,
(void **) &prcd,
&lrcd, 0);
if (!ret) // on success only ...
{
char buffer[80];
#ifdef CONFIG_DEBUG_IO
sprintf (buffer,
"RCD for device(%04X)/"
"subchannel(%04X) returns :\n",
ioinfo[irq]->schib.
pmcw.dev, irq);
s390_displayhex (buffer, prcd,
lrcd);
#endif
CIO_TRACE_EVENT(2, "rcddata:");
CIO_HEX_EVENT(2, prcd, lrcd);
if (init_IRQ_complete) {
kfree (prcd);
} else {
free_bootmem ((unsigned
long)
prcd,
lrcd);
}
}
}
#endif
}
disable_cpu_sync_isc (irq);
}
free_irq (irq, &devstat);
}
}
}
/*
* s390_device_recognition_all
*
* Used for system wide device recognition.
*
*/
void
s390_device_recognition_all (void)
{
int irq = 0; /* let's start with subchannel 0 ... */
do {
s390_device_recognition_irq (irq);
irq++;
} while (irq <= highest_subchannel);
}
/*
* Function: s390_redo_validation
* Look for no longer blacklisted devices
* FIXME: there must be a better way to do this...
*/
void
s390_redo_validation (void)
{
int irq = 0;
int ret;
CIO_TRACE_EVENT (0, "redoval");
do {
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
ret = s390_validate_subchannel (irq, 0);
if (!ret) {
s390_device_recognition_irq (irq);
if (ioinfo[irq]->ui.flags.oper) {
devreg_t *pdevreg;
pdevreg =
s390_search_devreg (ioinfo[irq]);
if (pdevreg != NULL) {
if (pdevreg->oper_func != NULL)
pdevreg->oper_func (irq,
pdevreg);
}
}
#ifdef CONFIG_PROC_FS
if (cio_proc_devinfo)
if (irq < MAX_CIO_PROCFS_ENTRIES) {
cio_procfs_device_create (ioinfo
[irq]->
devno);
}
#endif
}
}
irq++;
} while (irq <= highest_subchannel);
}
/*
* s390_trigger_resense
*
* try to re-sense the device on subchannel irq
* only to be called without interrupt handler
*/
int
s390_trigger_resense(int irq)
{
SANITY_CHECK(irq);
if (ioinfo[irq]->ui.flags.ready) {
printk (KERN_WARNING "s390_trigger_resense(%04X): "
"Device is in use!\n", irq);
return -EBUSY;
}
/*
* This function is called by dasd if it just executed a "steal lock".
* Therefore, re-initialize the 'unfriendly' flag to 0.
* We run into timeouts if the device is still boxed...
*/
ioinfo[irq]->ui.flags.unfriendly = 0;
s390_device_recognition_irq(irq);
return 0;
}
/*
* s390_search_devices
*
* Determines all subchannels available to the system.
*
*/
void
s390_process_subchannels (void)
{
int ret;
int irq = 0; /* Evaluate all subchannels starting with 0 ... */
do {
ret = s390_validate_subchannel (irq, 0);
if (ret != -ENXIO)
irq++;
} while ((ret != -ENXIO) && (irq < __MAX_SUBCHANNELS));
highest_subchannel = (--irq);
printk (KERN_INFO "Highest subchannel number detected (hex) : %04X\n",
highest_subchannel);
CIO_MSG_EVENT(0,
"Highest subchannel number detected "
"(hex) : %04X\n", highest_subchannel);
}
/*
* s390_validate_subchannel()
*
* Process the subchannel for the requested irq. Returns 1 for valid
* subchannels, otherwise 0.
*/
int
s390_validate_subchannel (int irq, int enable)
{
int retry; /* retry count for status pending conditions */
int ccode; /* condition code for stsch() only */
int ccode2; /* condition code for other I/O routines */
schib_t *p_schib;
int ret;
#ifdef CONFIG_CHSC
int chp = 0;
int mask;
#endif /* CONFIG_CHSC */
char dbf_txt[15];
sprintf (dbf_txt, "valsch%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
/*
* The first subchannel that is not-operational (ccode==3)
* indicates that there aren't any more devices available.
*/
if ((init_IRQ_complete)
&& (ioinfo[irq] != INVALID_STORAGE_AREA)) {
p_schib = &ioinfo[irq]->schib;
} else {
p_schib = p_init_schib;
}
/*
* 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;
}
ccode = stsch (irq, p_schib);
if (ccode) {
return -ENXIO;
}
/*
* ... just being curious we check for non I/O subchannels
*/
if (p_schib->pmcw.st) {
if (cio_show_msg) {
printk (KERN_INFO "Subchannel %04X reports "
"non-I/O subchannel type %04X\n",
irq, p_schib->pmcw.st);
}
CIO_MSG_EVENT(0,
"Subchannel %04X reports "
"non-I/O subchannel type %04X\n",
irq, p_schib->pmcw.st);
if (ioinfo[irq] != INVALID_STORAGE_AREA)
ioinfo[irq]->ui.flags.oper = 0;
}
if ((!p_schib->pmcw.dnv) && (!p_schib->pmcw.st)) {
return -ENODEV;
}
if (!p_schib->pmcw.st) {
if (is_blacklisted (p_schib->pmcw.dev)) {
/*
* This device must not be known to Linux. So we simply say that
* there is no device and return ENODEV.
*/
#ifdef CONFIG_DEBUG_IO
printk (KERN_DEBUG
"Blacklisted device detected at devno %04X\n",
p_schib->pmcw.dev);
#endif
CIO_MSG_EVENT(0,
"Blacklisted device detected at devno %04X\n",
p_schib->pmcw.dev);
return -ENODEV;
}
}
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
if (!init_IRQ_complete) {
ioinfo[irq] = (ioinfo_t *)
alloc_bootmem_low (sizeof (ioinfo_t));
} else {
ioinfo[irq] = (ioinfo_t *)
kmalloc (sizeof (ioinfo_t), GFP_DMA | GFP_ATOMIC);
}
if (!ioinfo[irq])
return -ENOMEM;
memset (ioinfo[irq], '\0', sizeof (ioinfo_t));
memcpy (&ioinfo[irq]->schib, p_init_schib, sizeof (schib_t));
/*
* We have to insert the new ioinfo element
* into the linked list, either at its head,
* its tail or insert it.
*/
if (ioinfo_head == NULL) { /* first element */
ioinfo_head = ioinfo[irq];
ioinfo_tail = ioinfo[irq];
} else if (irq < ioinfo_head->irq) { /* new head */
ioinfo[irq]->next = ioinfo_head;
ioinfo_head->prev = ioinfo[irq];
ioinfo_head = ioinfo[irq];
} else if (irq > ioinfo_tail->irq) { /* new tail */
ioinfo_tail->next = ioinfo[irq];
ioinfo[irq]->prev = ioinfo_tail;
ioinfo_tail = ioinfo[irq];
} else { /* insert element */
ioinfo_t *pi = ioinfo_head;
for (pi = ioinfo_head; pi != NULL; pi = pi->next) {
if (irq < pi->next->irq) {
ioinfo[irq]->next = pi->next;
ioinfo[irq]->prev = pi;
pi->next->prev = ioinfo[irq];
pi->next = ioinfo[irq];
break;
}
}
}
}
/* initialize some values ... */
ioinfo[irq]->irq = irq;
ioinfo[irq]->st = ioinfo[irq]->schib.pmcw.st;
if (ioinfo[irq]->st)
return -ENODEV;
ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim
& ioinfo[irq]->schib.pmcw.pam & ioinfo[irq]->schib.pmcw.pom;
#ifdef CONFIG_CHSC
if (ioinfo[irq]->opm) {
for (chp=0;chp<=7;chp++) {
mask = 0x80 >> chp;
if (ioinfo[irq]->opm & mask) {
if (!test_bit
(ioinfo[irq]->schib.pmcw.chpid[chp],
&chpids_logical)) {
/* disable using this path */
ioinfo[irq]->opm &= ~mask;
}
} else {
/* This chpid is not available to us */
clear_bit(ioinfo[irq]->schib.pmcw.chpid[chp],
&chpids);
}
}
}
#endif /* CONFIG_CHSC */
if (cio_show_msg) {
printk (KERN_INFO
"Detected device %04X "
"on subchannel %04X"
" - PIM = %02X, PAM = %02X, POM = %02X\n",
ioinfo[irq]->schib.pmcw.dev,
irq,
ioinfo[irq]->schib.pmcw.pim,
ioinfo[irq]->schib.pmcw.pam,
ioinfo[irq]->schib.pmcw.pom);
}
CIO_MSG_EVENT(0,
"Detected device %04X "
"on subchannel %04X"
" - PIM = %02X, "
"PAM = %02X, POM = %02X\n",
ioinfo[irq]->schib.pmcw.dev,
irq,
ioinfo[irq]->schib.pmcw.pim,
ioinfo[irq]->schib.pmcw.pam,
ioinfo[irq]->schib.pmcw.pom);
/*
* initialize ioinfo structure
*/
if (!ioinfo[irq]->ui.flags.ready) {
ioinfo[irq]->nopfunc = NULL;
ioinfo[irq]->ui.flags.busy = 0;
ioinfo[irq]->ui.flags.dval = 1;
ioinfo[irq]->devstat.intparm = 0;
}
ioinfo[irq]->devstat.devno = ioinfo[irq]->schib.pmcw.dev;
ioinfo[irq]->devno = ioinfo[irq]->schib.pmcw.dev;
/*
* We should have at least one CHPID ...
*/
if (ioinfo[irq]->opm) {
/*
* We now have to initially ...
* ... set "interruption sublass"
* ... enable "concurrent sense"
* ... enable "multipath mode" if more than one
* CHPID is available. This is done regardless
* whether multiple paths are available for us.
*
* Note : we don't enable the device here, this is temporarily
* done during device sensing below.
*/
ioinfo[irq]->schib.pmcw.isc = 3; /* could be smth. else */
ioinfo[irq]->schib.pmcw.csense = 1; /* concurrent sense */
ioinfo[irq]->schib.pmcw.ena = enable;
ioinfo[irq]->schib.pmcw.intparm = ioinfo[irq]->schib.pmcw.dev;
if ((ioinfo[irq]->opm != 0x80)
&& (ioinfo[irq]->opm != 0x40)
&& (ioinfo[irq]->opm != 0x20)
&& (ioinfo[irq]->opm != 0x10)
&& (ioinfo[irq]->opm != 0x08)
&& (ioinfo[irq]->opm != 0x04)
&& (ioinfo[irq]->opm != 0x02)
&& (ioinfo[irq]->opm != 0x01)) {
ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */
}
retry = 5;
do {
ccode2 = msch_err (irq, &ioinfo[irq]->schib);
switch (ccode2) {
case 0:
/*
* successful completion
*
* concurrent sense facility available
*/
ioinfo[irq]->ui.flags.oper = 1;
ioinfo[irq]->ui.flags.consns = 1;
ret = 0;
break;
case 1:
/*
* status pending
*
* How can we have a pending status
* as the device is disabled for
* interrupts ?
* Anyway, process it ...
*/
ioinfo[irq]->ui.flags.s_pend = 1;
s390_process_IRQ (irq);
ioinfo[irq]->ui.flags.s_pend = 0;
retry--;
ret = -EIO;
break;
case 2:
/*
* busy
*
* we mark it not-oper as we can't
* properly operate it !
*/
ioinfo[irq]->ui.flags.oper = 0;
udelay (100); /* allow for recovery */
retry--;
ret = -EBUSY;
break;
case 3: /* not operational */
ioinfo[irq]->ui.flags.oper = 0;
retry = 0;
ret = -ENODEV;
break;
default:
#define PGMCHK_OPERAND_EXC 0x15
if ((ccode2 & PGMCHK_OPERAND_EXC)
== PGMCHK_OPERAND_EXC) {
/*
* re-issue the modify subchannel without trying to
* enable the concurrent sense facility
*/
ioinfo[irq]->schib.pmcw.csense = 0;
ccode2 =
msch_err (irq, &ioinfo[irq]->schib);
if (ccode2 != 0) {
printk (KERN_ERR
" ... msch() (2) failed"
" with CC = %X\n",
ccode2);
CIO_MSG_EVENT(0,
"msch() (2) failed"
" with CC=%X\n",
ccode2);
ioinfo[irq]->ui.flags.oper = 0;
ret = -EIO;
} else {
ioinfo[irq]->ui.flags.oper = 1;
ioinfo[irq]->ui.
flags.consns = 0;
ret = 0;
}
} else {
printk (KERN_ERR
" ... msch() (1) failed with "
"CC = %X\n", ccode2);
CIO_MSG_EVENT(0,
"msch() (1) failed with "
"CC = %X\n", ccode2);
ioinfo[irq]->ui.flags.oper = 0;
ret = -EIO;
}
retry = 0;
break;
}
} while (ccode2 && retry);
if ((ccode2 != 0) && (ccode2 != 3)
&& (!retry)) {
printk (KERN_ERR
" ... msch() retry count for "
"subchannel %04X exceeded, CC = %d\n",
irq, ccode2);
CIO_MSG_EVENT(0,
" ... msch() retry count for "
"subchannel %04X exceeded, CC = %d\n",
irq, ccode2);
}
} else {
/* no path available ... */
ioinfo[irq]->ui.flags.oper = 0;
ret = -ENODEV;
}
return (ret);
}
/*
* s390_SenseID
*
* Try to obtain the 'control unit'/'device type' information
* associated with the subchannel.
*
* The function is primarily meant to be called without irq
* action handler in place. However, it also allows for
* use with an action handler in place. If there is already
* an action handler registered assure it can handle the
* s390_SenseID() related device interrupts - interruption
* parameter used is 0x00E2C9C4 ( SID ).
*/
int
s390_SenseID (int irq, senseid_t * sid, __u8 lpm)
{
ccw1_t *sense_ccw; /* ccw area for SenseID command */
senseid_t isid; /* internal sid */
devstat_t devstat; /* required by request_irq() */
__u8 pathmask; /* calulate path mask */
__u8 domask; /* path mask to use */
int inlreq; /* inline request_irq() */
int irq_ret; /* return code */
devstat_t *pdevstat; /* ptr to devstat in use */
int retry; /* retry count */
int io_retry; /* retry indicator */
senseid_t *psid = sid; /* start with the external buffer */
int sbuffer = 0; /* switch SID data buffer */
char dbf_txt[15];
int i;
int failure = 0; /* nothing went wrong yet */
SANITY_CHECK (irq);
if (ioinfo[irq]->ui.flags.oper == 0) {
return (-ENODEV);
}
if (ioinfo[irq]->ui.flags.unfriendly) {
/* don't even try it */
return -EUSERS;
}
sprintf (dbf_txt, "snsID%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
inlreq = 0; /* to make the compiler quiet... */
if (!ioinfo[irq]->ui.flags.ready) {
pdevstat = &devstat;
/*
* Perform SENSE ID command processing. We have to request device
* ownership and provide a dummy I/O handler. We issue sync. I/O
* requests and evaluate the devstat area on return therefore
* we don't need a real I/O handler in place.
*/
irq_ret =
request_irq (irq, init_IRQ_handler, SA_PROBE, "SID",
&devstat);
if (irq_ret == 0)
inlreq = 1;
} else {
inlreq = 0;
irq_ret = 0;
pdevstat = ioinfo[irq]->irq_desc.dev_id;
}
if (irq_ret) {
return irq_ret;
}
s390irq_spin_lock (irq);
if (init_IRQ_complete) {
sense_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA | GFP_ATOMIC);
} else {
sense_ccw = alloc_bootmem_low (2 * sizeof (ccw1_t));
}
if (!sense_ccw) {
s390irq_spin_unlock (irq);
if (inlreq)
free_irq (irq, &devstat);
return -ENOMEM;
}
/* more than one path installed ? */
if (ioinfo[irq]->schib.pmcw.pim != 0x80) {
sense_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN;
sense_ccw[0].cda = 0;
sense_ccw[0].count = 0;
sense_ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
sense_ccw[1].cmd_code = CCW_CMD_SENSE_ID;
sense_ccw[1].cda = (__u32) virt_to_phys (sid);
sense_ccw[1].count = sizeof (senseid_t);
sense_ccw[1].flags = CCW_FLAG_SLI;
} else {
sense_ccw[0].cmd_code = CCW_CMD_SENSE_ID;
sense_ccw[0].cda = (__u32) virt_to_phys (sid);
sense_ccw[0].count = sizeof (senseid_t);
sense_ccw[0].flags = CCW_FLAG_SLI;
}
for (i = 0; (i < 8); i++) {
pathmask = 0x80 >> i;
domask = ioinfo[irq]->opm & pathmask;
if (lpm)
domask &= lpm;
if (!domask)
continue;
failure = 0;
memset(psid, 0, sizeof(senseid_t));
psid->cu_type = 0xFFFF; /* initialize fields ... */
retry = 5; /* retry count */
io_retry = 1; /* enable retries */
/*
* We now issue a SenseID request. In case of BUSY,
* STATUS PENDING or non-CMD_REJECT error conditions
* we run simple retries.
*/
do {
memset (pdevstat, '\0', sizeof (devstat_t));
irq_ret = s390_start_IO (irq, sense_ccw, 0x00E2C9C4, /* == SID */
domask,
DOIO_WAIT_FOR_INTERRUPT
| DOIO_TIMEOUT
| DOIO_VALID_LPM
| DOIO_DONT_CALL_INTHDLR);
if ((psid->cu_type != 0xFFFF)
&& (psid->reserved == 0xFF)) {
if (!sbuffer) { /* switch buffers */
/*
* we report back the
* first hit only
*/
psid = &isid;
if (ioinfo[irq]->schib.pmcw.pim != 0x80) {
sense_ccw[1].cda = (__u32)
virt_to_phys (psid);
} else {
sense_ccw[0].cda = (__u32)
virt_to_phys (psid);
}
/*
* if just the very first
* was requested to be
* sensed disable further
* scans.
*/
if (!lpm)
lpm = domask;
sbuffer = 1;
}
if (pdevstat->rescnt < (sizeof (senseid_t) - 8)) {
ioinfo[irq]->ui.flags.esid = 1;
}
io_retry = 0;
break;
}
failure = 1;
if (pdevstat->flag & DEVSTAT_STATUS_PENDING) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_DEBUG
"SenseID : device %04X on "
"Subchannel %04X "
"reports pending status, "
"retry : %d\n",
ioinfo[irq]->schib.pmcw.dev, irq,
retry);
#endif
CIO_MSG_EVENT(2,
"SenseID : device %04X on "
"Subchannel %04X "
"reports pending status, "
"retry : %d\n",
ioinfo
[irq]->schib.pmcw.dev, irq, retry);
}
else if (pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL) {
/*
* if the device doesn't support the SenseID
* command further retries wouldn't help ...
*/
if (pdevstat->ii.sense.data[0]
& (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR
"SenseID : device %04X on "
"Subchannel %04X "
"reports cmd reject or "
"intervention required\n",
ioinfo[irq]->schib.pmcw.dev,
irq);
#endif
CIO_MSG_EVENT(2,
"SenseID : device %04X on "
"Subchannel %04X "
"reports cmd reject or "
"intervention required\n",
ioinfo[irq]->schib.pmcw.dev,
irq);
io_retry = 0;
} else {
#ifdef CONFIG_DEBUG_IO
printk
(KERN_WARNING
"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
CIO_MSG_EVENT(2,
"SenseID : UC on "
"dev %04X, "
"retry %d, "
"lpum %02X, "
"cnt %02d, "
"sns :"
" %02X%02X%02X%02X "
"%02X%02X%02X%02X ...\n",
ioinfo[irq]->
schib.pmcw.dev,
retry,
pdevstat->lpum,
pdevstat->scnt,
pdevstat->
ii.sense.data[0],
pdevstat->
ii.sense.data[1],
pdevstat->
ii.sense.data[2],
pdevstat->
ii.sense.data[3],
pdevstat->
ii.sense.data[4],
pdevstat->
ii.sense.data[5],
pdevstat->
ii.sense.data[6],
pdevstat->
ii.sense.data[7]);
}
} else if ((pdevstat->flag & DEVSTAT_NOT_OPER)
|| (irq_ret == -ENODEV)) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR
"SenseID : path %02X for "
"device %04X on "
"subchannel %04X "
"is 'not operational'\n",
domask,
ioinfo[irq]->schib.pmcw.dev, irq);
#endif
CIO_MSG_EVENT(2,
"SenseID : path %02X for "
"device %04X on "
"subchannel %04X "
"is 'not operational'\n",
domask,
ioinfo[irq]->schib.pmcw.dev, irq);
io_retry = 0;
ioinfo[irq]->opm &= ~domask;
} else {
#ifdef CONFIG_DEBUG_IO
printk (KERN_INFO
"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
CIO_MSG_EVENT(2,
"SenseID : start_IO() for "
"device %04X on "
"subchannel %04X "
"returns %d, retry %d, "
"status %04X\n",
ioinfo[irq]->schib.pmcw.dev, irq,
irq_ret, retry, pdevstat->flag);
if (irq_ret == -ETIMEDOUT) {
int xret;
/*
* Seems we need to cancel the first ssch sometimes...
* On the next try, the ssch will usually be fine.
*/
xret = cancel_IO (irq);
if (!xret)
CIO_MSG_EVENT(2,
"SenseID: sch canceled "
"successfully for irq %x\n",
irq);
}
}
if (io_retry) {
retry--;
if (retry == 0) {
io_retry = 0;
}
}
if ((failure) && (io_retry)) {
/* reset fields... */
failure = 0;
memset(psid, 0, sizeof(senseid_t));
psid->cu_type = 0xFFFF;
}
} while ((io_retry));
}
if (init_IRQ_complete) {
kfree (sense_ccw);
} else {
free_bootmem ((unsigned long) sense_ccw, 2 * sizeof (ccw1_t));
}
s390irq_spin_unlock (irq);
/*
* If we installed the irq action handler we have to
* release it too.
*/
if (inlreq)
free_irq (irq, pdevstat);
/*
* if running under VM check there ... perhaps we should do
* only if we suffered a command reject, but it doesn't harm
*/
if ((sid->cu_type == 0xFFFF)
&& (MACHINE_IS_VM)) {
VM_virtual_device_info (ioinfo[irq]->schib.pmcw.dev, sid);
}
if (sid->cu_type == 0xFFFF) {
/*
* SenseID CU-type of 0xffff indicates that no device
* information could be retrieved (pre-init value).
*
* If we can't couldn't identify the device type we
* consider the device "not operational".
*/
#ifdef CONFIG_DEBUG_IO
printk (KERN_WARNING
"SenseID : unknown device %04X on subchannel %04X\n",
ioinfo[irq]->schib.pmcw.dev, irq);
#endif
CIO_MSG_EVENT(2,
"SenseID : unknown device %04X on subchannel %04X\n",
ioinfo[irq]->schib.pmcw.dev, irq);
ioinfo[irq]->ui.flags.unknown = 1;
}
/*
* Issue device info message if unit was operational .
*/
if (!ioinfo[irq]->ui.flags.unknown) {
if (sid->dev_type != 0) {
if (cio_show_msg)
printk (KERN_INFO
"SenseID : device %04X reports: "
"CU Type/Mod = %04X/%02X,"
" Dev Type/Mod = %04X/%02X\n",
ioinfo[irq]->schib.pmcw.dev,
sid->cu_type, sid->cu_model,
sid->dev_type, sid->dev_model);
CIO_MSG_EVENT(2,
"SenseID : device %04X reports: "
"CU Type/Mod = %04X/%02X,"
" Dev Type/Mod = %04X/%02X\n",
ioinfo[irq]->schib.
pmcw.dev,
sid->cu_type,
sid->cu_model,
sid->dev_type,
sid->dev_model);
} else {
if (cio_show_msg)
printk (KERN_INFO
"SenseID : device %04X reports:"
" Dev Type/Mod = %04X/%02X\n",
ioinfo[irq]->schib.pmcw.dev,
sid->cu_type, sid->cu_model);
CIO_MSG_EVENT(2,
"SenseID : device %04X reports:"
" Dev Type/Mod = %04X/%02X\n",
ioinfo[irq]->schib.
pmcw.dev,
sid->cu_type,
sid->cu_model);
}
}
if (!ioinfo[irq]->ui.flags.unknown)
irq_ret = 0;
else if (irq_ret != -ETIMEDOUT)
irq_ret = -ENODEV;
return (irq_ret);
}
static int __inline__
s390_SetMultiPath (int irq)
{
int cc;
cc = stsch (irq, &ioinfo[irq]->schib);
if (!cc) {
ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */
cc = msch (irq, &ioinfo[irq]->schib);
}
return (cc);
}
static int
s390_do_path_verification(int irq, __u8 usermask)
{
__u8 domask;
int i;
pgid_t pgid;
__u8 dev_path;
int first = 1;
int ret = 0;
char dbf_txt[15];
sprintf(dbf_txt, "dopv%x", irq);
CIO_TRACE_EVENT(2, dbf_txt);
dev_path = usermask ? usermask : ioinfo[irq]->opm;
if (ioinfo[irq]->ui.flags.pgid == 0) {
memcpy (&ioinfo[irq]->pgid, global_pgid, sizeof (pgid_t));
ioinfo[irq]->ui.flags.pgid = 1;
}
for (i = 0; i < 8 && !ret; i++) {
domask = dev_path & (0x80>>i);
if (!domask)
continue;
if (!test_bit(ioinfo[irq]->schib.pmcw.chpid[i],
&chpids_logical))
/* Chpid is logically offline, don't do io */
continue;
ret = s390_SetPGID (irq, domask);
/*
* For the *first* path we are prepared for recovery
*
* - If we fail setting the PGID we assume its
* using a different PGID already (VM) we
* try to sense.
*/
if (ret == -EOPNOTSUPP && first) {
*(int *) &pgid = 0;
ret = s390_SensePGID (irq, domask, &pgid);
first = 0;
if (ret == 0) {
/*
* Check whether we retrieved
* a reasonable PGID ...
*/
if (pgid.inf.ps.state1 == SNID_STATE1_GROUPED)
memcpy (&ioinfo[irq]->pgid,
&pgid, sizeof (pgid_t));
else /* ungrouped or garbage ... */
ret = -EOPNOTSUPP;
} else {
ioinfo[irq]->ui.flags.pgid_supp = 0;
#ifdef CONFIG_DEBUG_IO
printk (KERN_WARNING
"PathVerification(%04X) - Device %04X "
"doesn't support path grouping\n",
irq, ioinfo[irq]->schib.pmcw.dev);
#endif
CIO_MSG_EVENT(2, "PathVerification(%04X) "
"- Device %04X doesn't "
" support path grouping\n",
irq,
ioinfo[irq]->schib.pmcw.dev);
}
} else if (ret == -EIO) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR "PathVerification(%04X) - I/O error "
"on device %04X\n", irq,
ioinfo[irq]->schib.pmcw.dev);
#endif
CIO_MSG_EVENT(2, "PathVerification(%04X) - I/O error "
"on device %04X\n", irq,
ioinfo[irq]->schib.pmcw.dev);
ioinfo[irq]->ui.flags.pgid_supp = 0;
} else if (ret == -ETIMEDOUT) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR "PathVerification(%04X) - I/O timed "
"out on device %04X\n", irq,
ioinfo[irq]->schib.pmcw.dev);
#endif
CIO_MSG_EVENT(2, "PathVerification(%04X) - I/O timed "
"out on device %04X\n", irq,
ioinfo[irq]->schib.pmcw.dev);
ioinfo[irq]->ui.flags.pgid_supp = 0;
} else if (ret == -EAGAIN) {
ret = 0;
} else if (ret == -EUSERS) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR "PathVerification(%04X) "
"- Device is locked by someone else!\n",
irq);
#endif
CIO_MSG_EVENT(2, "PathVerification(%04X) "
"- Device is locked by someone else!\n",
irq);
} else if (ret == -ENODEV) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR "PathVerification(%04X) "
"- Device %04X is no longer there?!?\n",
irq, ioinfo[irq]->schib.pmcw.dev);
#endif
CIO_MSG_EVENT(2, "PathVerification(%04X) "
"- Device %04X is no longer there?!?\n",
irq, ioinfo[irq]->schib.pmcw.dev);
} else if (ret == -EBUSY) {
/*
* The device is busy. Schedule the path verification
* bottom half and we'll hopefully get in next time.
*/
if (!ioinfo[irq]->ui.flags.noio) {
s390_schedule_path_verification(irq);
}
return -EINPROGRESS;
} else if (ret) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_ERR "PathVerification(%04X) "
"- Unexpected error %d on device %04X\n",
irq, ret, ioinfo[irq]->schib.pmcw.dev);
#endif
CIO_MSG_EVENT(2, "PathVerification(%04X) - "
"Unexpected error %d on device %04X\n",
irq, ret, ioinfo[irq]->schib.pmcw.dev);
ioinfo[irq]->ui.flags.pgid_supp = 0;
}
}
if (stsch(irq, &ioinfo[irq]->schib) != 0)
/* FIXME: tell driver device is dead. */
return -ENODEV;
/*
* stsch() doesn't always yield the correct pim, pam, and pom
* values, if no device selection has been performed yet.
* However, after complete path verification they are up to date.
*/
ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim &
ioinfo[irq]->schib.pmcw.pam &
ioinfo[irq]->schib.pmcw.pom;
#ifdef CONFIG_CHSC
if (ioinfo[irq]->opm) {
for (i=0;i<=7;i++) {
int mask = 0x80 >> i;
if ((ioinfo[irq]->opm & mask) &&
(!test_bit(ioinfo[irq]->schib.pmcw.chpid[i],
&chpids_logical)))
/* disable using this path */
ioinfo[irq]->opm &= ~mask;
}
}
#endif /* CONFIG_CHSC */
ioinfo[irq]->ui.flags.noio = 0;
/* Eventually wake up the device driver. */
if (ioinfo[irq]->opm != 0) {
devreg_t *pdevreg;
pdevreg = s390_search_devreg(ioinfo[irq]);
if (pdevreg && pdevreg->oper_func)
pdevreg->oper_func(irq, pdevreg);
}
return ret;
}
/*
* Device Path Verification
*
* Path verification is accomplished by checking which paths (CHPIDs) are
* available. Further, a path group ID is set, if possible in multipath
* mode, otherwise in single path mode.
*
* Note : This function must not be called during normal device recognition,
* but during device driver initiated request_irq() processing only.
*/
int
s390_DevicePathVerification (int irq, __u8 usermask)
{
int ccode;
#ifdef CONFIG_CHSC
int chp;
int mask;
int old_opm = 0;
#endif /* CONFIG_CHSC */
int ret = 0;
char dbf_txt[15];
devreg_t *pdevreg;
sprintf (dbf_txt, "dpver%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
if (ioinfo[irq]->st)
return -ENODEV;
#ifdef CONFIG_CHSC
old_opm = ioinfo[irq]->opm;
#endif /* CONFIG_CHSC */
ccode = stsch (irq, &(ioinfo[irq]->schib));
if (ccode)
return -ENODEV;
if (ioinfo[irq]->schib.pmcw.pim == 0x80) {
/*
* no error, just not required for single path only devices
*/
ioinfo[irq]->ui.flags.pgid_supp = 0;
ret = 0;
ioinfo[irq]->ui.flags.noio = 0;
#ifdef CONFIG_CHSC
/*
* disable if chpid is logically offline
*/
if (!test_bit(ioinfo[irq]->schib.pmcw.chpid[0],
&chpids_logical)) {
ioinfo[irq]->opm = 0;
ioinfo[irq]->ui.flags.oper = 0;
printk(KERN_WARNING
"No logical path for sch %d...\n",
irq);
if (ioinfo[irq]->nopfunc) {
if (ioinfo[irq]->ui.flags.notacccap)
ioinfo[irq]->nopfunc(irq,
DEVSTAT_NOT_ACC);
else {
not_oper_handler_func_t nopfunc =
ioinfo[irq]->nopfunc;
#ifdef CONFIG_PROC_FS
/* remove procfs entry */
if (cio_proc_devinfo)
cio_procfs_device_remove
(ioinfo[irq]->devno);
#endif
free_irq(irq,
ioinfo[irq]->irq_desc.dev_id);
nopfunc(irq, DEVSTAT_DEVICE_GONE);
}
}
return -ENODEV;
}
if (!old_opm) {
ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim
& ioinfo[irq]->schib.pmcw.pam
& ioinfo[irq]->schib.pmcw.pom;
if (ioinfo[irq]->opm) {
ioinfo[irq]->ui.flags.oper = 1;
pdevreg = s390_search_devreg(ioinfo[irq]);
if (pdevreg && pdevreg->oper_func)
pdevreg->oper_func(irq, pdevreg);
ret = 0;
} else {
ret = -ENODEV;
}
}
#endif /* CONFIG_CHSC */
return ret;
}
ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim
& ioinfo[irq]->schib.pmcw.pam & ioinfo[irq]->schib.pmcw.pom;
#ifdef CONFIG_CHSC
if (ioinfo[irq]->opm) {
for (chp=0;chp<=7;chp++) {
mask = 0x80 >> chp;
if ((ioinfo[irq]->opm & mask)
&&(!test_bit(ioinfo[irq]->schib.pmcw.chpid[chp],
&chpids_logical)))
/* disable using this path */
ioinfo[irq]->opm &= ~mask;
}
}
#endif /* CONFIG_CHSC */
if (ioinfo[irq]->ui.flags.pgid_supp == 0) {
if (ioinfo[irq]->opm == 0)
return -ENODEV;
ioinfo[irq]->ui.flags.oper = 1;
ioinfo[irq]->ui.flags.noio = 0;
pdevreg = s390_search_devreg(ioinfo[irq]);
if (pdevreg && pdevreg->oper_func)
pdevreg->oper_func(irq, pdevreg);
return 0;
}
if (ioinfo[irq]->ui.flags.ready)
return s390_do_path_verification (irq, usermask);
return 0;
}
void
s390_kick_path_verification (unsigned long irq)
{
long cr6 __attribute__ ((aligned (8)));
atomic_set (&ioinfo[irq]->pver_pending, 0);
/* Do not enter path verification if sync_isc is enabled. */
__ctl_store (cr6, 6, 6);
if (cr6 & 0x04000000) {
s390_schedule_path_verification (irq);
return;
}
ioinfo[irq]->ui.flags.killio = 0;
s390_DevicePathVerification(irq, 0xff);
}
static void
s390_schedule_path_verification(unsigned long irq)
{
/* Protect against rescheduling, when already running */
if (atomic_compare_and_swap (0, 1, &ioinfo[irq]->pver_pending)) {
return;
}
/*
* Call path verification.
* Note this is always called from inside the i/o layer, so we don't
* need to care about the usermask.
*/
INIT_LIST_HEAD (&ioinfo[irq]->pver_bh.list);
ioinfo[irq]->pver_bh.sync = 0;
ioinfo[irq]->pver_bh.routine = (void*) (void*) s390_kick_path_verification;
ioinfo[irq]->pver_bh.data = (void*) irq;
queue_task (&ioinfo[irq]->pver_bh, &tq_immediate);
mark_bh (IMMEDIATE_BH);
}
/*
* s390_SetPGID
*
* Set Path Group ID
*
*/
int
s390_SetPGID (int irq, __u8 lpm)
{
ccw1_t *spid_ccw; /* ccw area for SPID command */
devstat_t devstat; /* required by request_irq() */
devstat_t *pdevstat = &devstat;
unsigned long flags;
char dbf_txt[15];
int irq_ret = 0; /* return code */
int retry = 5; /* retry count */
int inlreq = 0; /* inline request_irq() */
int mpath = 1; /* try multi-path first */
SANITY_CHECK (irq);
if (ioinfo[irq]->ui.flags.oper == 0) {
return (-ENODEV);
}
if (ioinfo[irq]->ui.flags.unfriendly) {
/* don't even try it */
return -EUSERS;
}
sprintf (dbf_txt, "SPID%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
if (!ioinfo[irq]->ui.flags.ready) {
/*
* Perform SetPGID command processing. We have to request device
* ownership and provide a dummy I/O handler. We issue sync. I/O
* requests and evaluate the devstat area on return therefore
* we don't need a real I/O handler in place.
*/
irq_ret = request_irq (irq,
init_IRQ_handler,
SA_PROBE, "SPID", pdevstat);
if (irq_ret == 0)
inlreq = 1;
} else {
pdevstat = ioinfo[irq]->irq_desc.dev_id;
}
if (irq_ret) {
return irq_ret;
}
s390irq_spin_lock_irqsave (irq, flags);
if (init_IRQ_complete) {
spid_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA | GFP_ATOMIC);
} else {
spid_ccw = alloc_bootmem_low (2 * sizeof (ccw1_t));
}
if (!spid_ccw) {
s390irq_spin_unlock_irqrestore(irq, flags);
if (inlreq)
free_irq(irq, pdevstat);
return -ENOMEM;
}
spid_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN;
spid_ccw[0].cda = 0;
spid_ccw[0].count = 0;
spid_ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
spid_ccw[1].cmd_code = CCW_CMD_SET_PGID;
spid_ccw[1].cda = (__u32) virt_to_phys (&ioinfo[irq]->pgid);
spid_ccw[1].count = sizeof (pgid_t);
spid_ccw[1].flags = CCW_FLAG_SLI;
ioinfo[irq]->pgid.inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH;
/*
* We now issue a SetPGID request. In case of BUSY
* or STATUS PENDING conditions we retry 5 times.
*/
do {
memset (pdevstat, '\0', sizeof (devstat_t));
irq_ret = s390_start_IO (irq, spid_ccw, 0xE2D7C9C4, /* == SPID */
lpm, /* n/a */
DOIO_WAIT_FOR_INTERRUPT
| DOIO_VALID_LPM
| DOIO_DONT_CALL_INTHDLR
| DOIO_TIMEOUT);
if (!irq_ret) {
if (pdevstat->flag & DEVSTAT_STATUS_PENDING) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_DEBUG "SPID - Device %04X "
"on Subchannel %04X "
"reports pending status, "
"lpm = %x, "
"retry : %d\n",
ioinfo[irq]->schib.pmcw.dev,
irq, lpm, retry);
#endif
CIO_MSG_EVENT(2,
"SPID - Device %04X "
"on Subchannel %04X "
"reports pending status, "
"lpm = %x, "
"retry : %d\n",
ioinfo[irq]->schib.pmcw.
dev, irq, lpm, retry);
retry--;
irq_ret = -EIO;
}
if (pdevstat->flag == (DEVSTAT_START_FUNCTION
| DEVSTAT_FINAL_STATUS)) {
retry = 0; /* successfully set ... */
irq_ret = 0;
} else if (pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL) {
/*
* If the device doesn't support the
* Sense Path Group ID command
* further retries wouldn't help ...
*/
if (pdevstat->ii.sense.
data[0] & SNS0_CMD_REJECT) {
if (mpath) {
/*
* We now try single path mode.
* Note we must not issue the suspend
* multipath reconnect, or we will get
* a command reject by tapes.
*/
spid_ccw[0].cmd_code =
CCW_CMD_SET_PGID;
spid_ccw[0].cda = (__u32)
virt_to_phys (&ioinfo[irq]->pgid);
spid_ccw[0].count =
sizeof (pgid_t);
spid_ccw[0].flags =
CCW_FLAG_SLI;
ioinfo[irq]->pgid.inf.fc =
SPID_FUNC_SINGLE_PATH
| SPID_FUNC_ESTABLISH;
mpath = 0;
retry--;
irq_ret = -EIO;
} else {
irq_ret = -EOPNOTSUPP;
retry = 0;
}
} else {
#ifdef CONFIG_DEBUG_IO
printk (KERN_WARNING
"SPID - device %04X,"
" unit check,"
" retry %d, cnt %02d,"
" lpm %x, sns :"
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
ioinfo[irq]->schib.pmcw.
dev, retry,
pdevstat->scnt,
lpm,
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
CIO_MSG_EVENT(2,
"SPID - device %04X,"
" unit check,"
" retry %d, cnt %02d,"
" lpm %x, sns :"
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
ioinfo[irq]->schib.
pmcw.dev, retry,
pdevstat->scnt,
lpm,
pdevstat->ii.sense.
data[0],
pdevstat->ii.sense.
data[1],
pdevstat->ii.sense.
data[2],
pdevstat->ii.sense.
data[3],
pdevstat->ii.sense.
data[4],
pdevstat->ii.sense.
data[5],
pdevstat->ii.sense.
data[6],
pdevstat->ii.sense.
data[7]);
retry--;
irq_ret = -EIO;
}
} else if (pdevstat->flag & DEVSTAT_NOT_OPER) {
/* don't issue warnings during startup unless requested */
if (init_IRQ_complete || cio_notoper_msg) {
printk (KERN_INFO
"SPID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.pmcw.
dev, irq,
lpm);
CIO_MSG_EVENT(2,
"SPID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.
pmcw.dev, irq,
lpm);
}
retry = 0;
ioinfo[irq]->opm &= ~lpm;
irq_ret = -EAGAIN;
}
} else if (irq_ret == -ETIMEDOUT) {
/*
* SetPGID timed out, so we cancel it before
* we retry
*/
int xret;
xret = cancel_IO(irq);
if (!xret)
CIO_MSG_EVENT(2,
"SetPGID: sch canceled "
"successfully for irq %x\n",
irq);
retry--;
} else if (irq_ret == -EBUSY) {
#ifdef CONFIG_DEBUG_IO
printk(KERN_WARNING
"SPID - device %x, irq %x is busy!\n",
ioinfo[irq]->schib.pmcw.dev, irq);
#endif /* CONFIG_DEBUG_IO */
CIO_MSG_EVENT(2,
"SPID - device %x, irq %x is busy!\n",
ioinfo[irq]->schib.pmcw.dev, irq);
retry = 0;
} else if (irq_ret != -ENODEV) {
retry--;
irq_ret = -EIO;
} else if (!pdevstat->flag & DEVSTAT_NOT_OPER) {
retry = 0;
irq_ret = -ENODEV;
} else {
/* don't issue warnings during startup unless requested */
if (init_IRQ_complete || cio_notoper_msg) {
printk (KERN_INFO
"SPID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.pmcw.
dev, irq,
lpm);
CIO_MSG_EVENT(2,
"SPID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.
pmcw.dev, irq,
lpm);
}
retry = 0;
ioinfo[irq]->opm &= ~lpm;
if (ioinfo[irq]->opm != 0)
irq_ret = -EAGAIN;
else
irq_ret = -ENODEV;
}
} while (retry > 0);
if (init_IRQ_complete) {
kfree (spid_ccw);
} else {
free_bootmem ((unsigned long) spid_ccw, 2 * sizeof (ccw1_t));
}
s390irq_spin_unlock_irqrestore (irq, flags);
/*
* If we installed the irq action handler we have to
* release it too.
*/
if (inlreq)
free_irq (irq, pdevstat);
return (irq_ret);
}
/*
* s390_SensePGID
*
* Sense Path Group ID
*
*/
int
s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid)
{
ccw1_t *snid_ccw; /* ccw area for SNID command */
devstat_t devstat; /* required by request_irq() */
devstat_t *pdevstat = &devstat;
char dbf_txt[15];
pgid_t * tmp_pgid;
int irq_ret = 0; /* return code */
int retry = 5; /* retry count */
int inlreq = 0; /* inline request_irq() */
unsigned long flags;
SANITY_CHECK (irq);
if (ioinfo[irq]->ui.flags.oper == 0) {
return (-ENODEV);
}
sprintf (dbf_txt, "SNID%x", irq);
CIO_TRACE_EVENT (4, dbf_txt);
if (!ioinfo[irq]->ui.flags.ready) {
/*
* Perform SENSE PGID command processing. We have to request device
* ownership and provide a dummy I/O handler. We issue sync. I/O
* requests and evaluate the devstat area on return therefore
* we don't need a real I/O handler in place.
*/
irq_ret = request_irq (irq,
init_IRQ_handler,
SA_PROBE, "SNID", pdevstat);
if (irq_ret == 0)
inlreq = 1;
} else {
pdevstat = ioinfo[irq]->irq_desc.dev_id;
}
if (irq_ret) {
return irq_ret;
}
s390irq_spin_lock_irqsave (irq, flags);
ioinfo[irq]->ui.flags.unfriendly = 0; /* assume it's friendly... */
if (init_IRQ_complete) {
snid_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA | GFP_ATOMIC);
tmp_pgid = kmalloc (sizeof (pgid_t), GFP_DMA | GFP_ATOMIC);
} else {
snid_ccw = alloc_bootmem_low (sizeof (ccw1_t));
tmp_pgid = alloc_bootmem_low (sizeof (pgid_t));
}
if (!snid_ccw || !tmp_pgid) {
if (snid_ccw) {
if (init_IRQ_complete)
kfree(snid_ccw);
else
free_bootmem((unsigned long) snid_ccw, sizeof(ccw1_t));
}
if (tmp_pgid) {
if (init_IRQ_complete)
kfree(tmp_pgid);
else
free_bootmem((unsigned long) tmp_pgid, sizeof(pgid_t));
}
s390irq_spin_unlock_irqrestore(irq, flags);
if (inlreq)
free_irq (irq, pdevstat);
return -ENOMEM;
}
snid_ccw->cmd_code = CCW_CMD_SENSE_PGID;
snid_ccw->cda = (__u32) virt_to_phys (tmp_pgid);
snid_ccw->count = sizeof (pgid_t);
snid_ccw->flags = CCW_FLAG_SLI;
/*
* We now issue a SensePGID request. In case of BUSY
* or STATUS PENDING conditions we retry 5 times.
*/
do {
memset (pdevstat, '\0', sizeof (devstat_t));
irq_ret = s390_start_IO (irq, snid_ccw, 0xE2D5C9C4, /* == SNID */
lpm, /* n/a */
DOIO_WAIT_FOR_INTERRUPT
| DOIO_TIMEOUT
| DOIO_VALID_LPM
| DOIO_DONT_CALL_INTHDLR);
if (irq_ret == 0) {
if (pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL) {
/*
* If the device doesn't support the
* Sense Path Group ID command
* further retries wouldn't help ...
*/
if (pdevstat->ii.sense.data[0]
& (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)) {
retry = 0;
irq_ret = -EOPNOTSUPP;
} else {
#ifdef CONFIG_DEBUG_IO
printk (KERN_WARNING
"SNID - device %04X,"
" unit check,"
" flag %04X, "
" retry %d, cnt %02d,"
" sns :"
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
ioinfo[irq]->schib.pmcw.
dev, pdevstat->flag,
retry, pdevstat->scnt,
pdevstat->ii.sense.
data[0],
pdevstat->ii.sense.
data[1],
pdevstat->ii.sense.
data[2],
pdevstat->ii.sense.
data[3],
pdevstat->ii.sense.
data[4],
pdevstat->ii.sense.
data[5],
pdevstat->ii.sense.
data[6],
pdevstat->ii.sense.data[7]);
#endif
CIO_MSG_EVENT(2,
"SNID - device %04X,"
" unit check,"
" flag %04X, "
" retry %d, cnt %02d,"
" sns :"
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
ioinfo[irq]->schib.
pmcw.dev,
pdevstat->flag,
retry,
pdevstat->scnt,
pdevstat->ii.sense.
data[0],
pdevstat->ii.sense.
data[1],
pdevstat->ii.sense.
data[2],
pdevstat->ii.sense.
data[3],
pdevstat->ii.sense.
data[4],
pdevstat->ii.sense.
data[5],
pdevstat->ii.sense.
data[6],
pdevstat->ii.sense.
data[7]);
retry--;
irq_ret = -EIO;
}
} else if (pdevstat->flag & DEVSTAT_NOT_OPER) {
/* don't issue warnings during startup unless requested */
if (init_IRQ_complete || cio_notoper_msg) {
printk (KERN_INFO
"SNID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.pmcw.
dev, irq,
lpm);
CIO_MSG_EVENT(2,
"SNID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.
pmcw.dev, irq,
lpm);
}
retry = 0;
ioinfo[irq]->opm &= ~lpm;
irq_ret = -EAGAIN;
} else {
retry = 0; /* success ... */
irq_ret = 0;
/*
* Check if device is locked by someone else
* -- we'll fail other commands if that is
* the case
*/
if (tmp_pgid->inf.ps.state2 ==
SNID_STATE2_RESVD_ELSE) {
printk (KERN_WARNING
"SNID - Device %04X "
"on Subchannel %04X "
"is reserved by "
"someone else\n",
ioinfo[irq]->schib.pmcw.dev,
irq);
CIO_MSG_EVENT(2,
"SNID - Device %04X "
"on Subchannel %04X "
"is reserved by "
"someone else\n",
ioinfo[irq]->schib.
pmcw.dev,
irq);
ioinfo[irq]->ui.flags.unfriendly = 1;
} else {
/*
* device is friendly to us :)
*/
ioinfo[irq]->ui.flags.unfriendly = 0;
}
memcpy(pgid, tmp_pgid, sizeof(pgid_t));
}
} else if (irq_ret == -ETIMEDOUT) {
#ifdef CONFIG_DEBUG_IO
printk(KERN_INFO "SNID - Operation timed out "
"on Device %04X, Subchannel %04X... "
"cancelling IO\n",
ioinfo[irq]->schib.pmcw.dev,
irq);
#endif /* CONFIG_DEBUG_IO */
CIO_MSG_EVENT(2,
"SNID - Operation timed out "
"on Device %04X, Subchannel %04X... "
"cancelling IO\n",
ioinfo[irq]->schib.pmcw.dev,
irq);
cancel_IO(irq);
retry--;
} else if (irq_ret != -ENODEV) { /* -EIO, or -EBUSY */
if (pdevstat->flag & DEVSTAT_STATUS_PENDING) {
#ifdef CONFIG_DEBUG_IO
printk (KERN_INFO "SNID - Device %04X "
"on Subchannel %04X "
"reports pending status, "
"retry : %d\n",
ioinfo[irq]->schib.pmcw.dev,
irq, retry);
#endif
CIO_MSG_EVENT(2,
"SNID - Device %04X "
"on Subchannel %04X "
"reports pending status, "
"retry : %d\n",
ioinfo[irq]->schib.pmcw.
dev, irq, retry);
}
printk (KERN_WARNING "SNID - device %04X,"
" start_io() reports rc : %d, retrying ...\n",
ioinfo[irq]->schib.pmcw.dev, irq_ret);
CIO_MSG_EVENT(2,
"SNID - device %04X,"
" start_io() reports rc : %d,"
" retrying ...\n",
ioinfo[irq]->schib.pmcw.dev, irq_ret);
retry--;
irq_ret = -EIO;
} else if (!pdevstat->flag & DEVSTAT_NOT_OPER) {
retry = 0;
irq_ret = -ENODEV;
} else {
/* don't issue warnings during startup unless requested */
if (init_IRQ_complete || cio_notoper_msg) {
printk (KERN_INFO
"SNID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.pmcw.
dev, irq,
lpm);
CIO_MSG_EVENT(2,
"SNID - Device %04X "
"on Subchannel %04X, "
"lpm %02X, "
"became 'not operational'\n",
ioinfo[irq]->schib.
pmcw.dev, irq,
lpm);
}
retry = 0;
ioinfo[irq]->opm &= ~lpm;
if (ioinfo[irq]->opm != 0)
irq_ret = -EAGAIN;
else
irq_ret = -ENODEV;
}
} while (retry > 0);
if (init_IRQ_complete) {
kfree (snid_ccw);
kfree (tmp_pgid);
} else {
free_bootmem ((unsigned long) snid_ccw, sizeof (ccw1_t));
free_bootmem ((unsigned long) tmp_pgid, sizeof (pgid_t));
}
s390irq_spin_unlock_irqrestore (irq, flags);
/*
* If we installed the irq action handler we have to
* release it too.
*/
if (inlreq)
free_irq (irq, pdevstat);
return (irq_ret);
}
void
s390_process_subchannel_source (int irq)
{
int dev_oper = 0;
int dev_no = -1;
int lock = 0;
int is_owned = 0;
/*
* If the device isn't known yet
* we can't lock it ...
*/
if (ioinfo[irq] != INVALID_STORAGE_AREA) {
s390irq_spin_lock (irq);
lock = 1;
if (!ioinfo[irq]->st) {
dev_oper = ioinfo[irq]->ui.flags.oper;
if (ioinfo[irq]->ui.flags.dval)
dev_no = ioinfo[irq]->devno;
is_owned = ioinfo[irq]->ui.flags.ready;
}
}
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG
"do_crw_pending : subchannel validation - start ...\n");
#endif
CIO_CRW_EVENT(4, "subchannel validation - start\n");
s390_validate_subchannel (irq, is_owned);
if (irq > highest_subchannel)
highest_subchannel = irq;
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG "do_crw_pending : subchannel validation - done\n");
#endif
CIO_CRW_EVENT(4, "subchannel validation - done\n");
/*
* After the validate processing
* the ioinfo control block
* should be allocated ...
*/
if (lock) {
s390irq_spin_unlock (irq);
}
if (ioinfo[irq] != INVALID_STORAGE_AREA) {
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG "do_crw_pending : ioinfo at "
#ifdef CONFIG_ARCH_S390X
"%08lX\n", (unsigned long) ioinfo[irq]
#else /* CONFIG_ARCH_S390X */
"%08X\n", (unsigned) ioinfo[irq]
#endif /* CONFIG_ARCH_S390X */
);
#endif
#ifdef CONFIG_ARCH_S390X
CIO_CRW_EVENT(4, "ioinfo at %08lX\n",
(unsigned long)ioinfo[irq]);
#else /* CONFIG_ARCH_S390X */
CIO_CRW_EVENT(4, "ioinfo at %08X\n",
(unsigned)ioinfo[irq]);
#endif /* CONFIG_ARCH_S390X */
if (ioinfo[irq]->st)
return;
if (ioinfo[irq]->ui.flags.oper == 0) {
not_oper_handler_func_t nopfunc = ioinfo[irq]->nopfunc;
#ifdef CONFIG_PROC_FS
/* remove procfs entry */
if (cio_proc_devinfo)
cio_procfs_device_remove (dev_no);
#endif
/*
* If the device has gone
* call not oper handler
*/
if ((dev_oper == 1)
&& (nopfunc != NULL)) {
free_irq (irq, ioinfo[irq]->irq_desc.dev_id);
nopfunc (irq, DEVSTAT_DEVICE_GONE);
}
} else {
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG
"do_crw_pending : device "
"recognition - start ...\n");
#endif
CIO_CRW_EVENT( 4,
"device recognition - start\n");
s390_device_recognition_irq (irq);
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG
"do_crw_pending : device "
"recognition - done\n");
#endif
CIO_CRW_EVENT( 4,
"device recognition - done\n");
/*
* the device became operational
*/
if (dev_oper == 0) {
devreg_t *pdevreg;
pdevreg = s390_search_devreg (ioinfo[irq]);
if (pdevreg && pdevreg->oper_func)
pdevreg->oper_func(irq, pdevreg);
#ifdef CONFIG_PROC_FS
/* add new procfs entry */
if (cio_proc_devinfo)
if (highest_subchannel <
MAX_CIO_PROCFS_ENTRIES) {
cio_procfs_device_create
(ioinfo[irq]->devno);
}
#endif
}
/*
* ... it is and was operational, but
* the devno may have changed
*/
else if ((ioinfo[irq]->devno != dev_no)
&& (ioinfo[irq]->nopfunc != NULL)) {
#ifdef CONFIG_PROC_FS
int devno_old = ioinfo[irq]->devno;
#endif
ioinfo[irq]->nopfunc (irq, DEVSTAT_REVALIDATE);
#ifdef CONFIG_PROC_FS
/* remove old entry, add new */
if (cio_proc_devinfo) {
cio_procfs_device_remove (devno_old);
cio_procfs_device_create
(ioinfo[irq]->devno);
}
#endif
}
}
#ifdef CONFIG_PROC_FS
/* get rid of dead procfs entries */
if (cio_proc_devinfo)
cio_procfs_device_purge ();
#endif
}
}
#ifdef CONFIG_CHSC
static int
chsc_get_sch_desc_irq(int irq)
{
int j = 0;
int ccode;
spin_lock(&chsc_lock_ssd);
if (!chsc_area_ssd)
chsc_area_ssd = kmalloc(sizeof(chsc_area_t),GFP_KERNEL);
if (!chsc_area_ssd) {
printk( KERN_CRIT "No memory to determine sch descriptions...\n");
spin_unlock(&chsc_lock_ssd);
return -ENOMEM;
}
memset(chsc_area_ssd, 0, sizeof(chsc_area_t));
chsc_area_ssd->request_block.command_code1=0x0010;
chsc_area_ssd->request_block.command_code2=0x0004;
chsc_area_ssd->request_block.request_block_data.ssd_req.f_sch=irq;
chsc_area_ssd->request_block.request_block_data.ssd_req.l_sch=irq;
ccode = chsc(chsc_area_ssd);
#ifdef CONFIG_DEBUG_CHSC
if (ccode)
printk( KERN_DEBUG "chsc returned with ccode = %d\n",ccode);
#endif /* CONFIG_DEBUG_CHSC */
if (!ccode) {
if (chsc_area_ssd->response_block.response_code == 0x0003) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING "Error in chsc request block!\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2, "Error in chsc request block!\n");
spin_unlock(&chsc_lock_ssd);
return -EINVAL;
} else if (chsc_area_ssd->response_block.response_code == 0x0004) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING "Model does not provide ssd\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2, "Model does not provide ssd\n");
spin_unlock(&chsc_lock_ssd);
return -EOPNOTSUPP;
} else if (chsc_area_ssd->response_block.response_code == 0x0002) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING "chsc: Invalid command!\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2,
"chsc: Invalid command!\n");
return -EINVAL;
} else if (chsc_area_ssd->response_block.response_code == 0x0001) {
/* everything ok */
switch (chsc_area_ssd->response_block.response_block_data.ssd_res.st) {
case 0: /* I/O subchannel */
/*
* All fields have meaning
*/
#ifdef CONFIG_DEBUG_CHSC
if (cio_show_msg)
printk( KERN_DEBUG
"ssd: sch %x is I/O subchannel\n",
irq);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 6,
"ssd: sch %x is I/O subchannel\n",
irq);
if (ioinfo[irq] == INVALID_STORAGE_AREA)
/* FIXME: we should do device rec. here... */
break;
ioinfo[irq]->ssd_info.valid = 1;
ioinfo[irq]->ssd_info.type = 0;
for (j=0;j<8;j++) {
if ((0x80 >> j) &
chsc_area_ssd->response_block.
response_block_data.ssd_res.path_mask &
chsc_area_ssd->response_block.
response_block_data.ssd_res.fla_valid_mask) {
if (chsc_area_ssd->response_block.
response_block_data.ssd_res.chpid[j])
if (!test_and_set_bit
(chsc_area_ssd->response_block.
response_block_data.
ssd_res.chpid[j],
&chpids_known))
if (test_bit
(chsc_area_ssd->response_block.
response_block_data.
ssd_res.chpid[j],
&chpids_logical))
set_bit(chsc_area_ssd->response_block.
response_block_data.
ssd_res.chpid[j],
&chpids);
ioinfo[irq]->ssd_info.chpid[j] =
chsc_area_ssd->response_block.
response_block_data.ssd_res.chpid[j];
ioinfo[irq]->ssd_info.fla[j] =
chsc_area_ssd->response_block.
response_block_data.ssd_res.fla[j];
}
}
break;
case 1: /* CHSC subchannel */
/*
* Only sch_val, st and sch have meaning
*/
#ifdef CONFIG_DEBUG_CHSC
if (cio_show_msg)
printk( KERN_DEBUG
"ssd: sch %x is chsc subchannel\n",
irq);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 6,
"ssd: sch %x is chsc subchannel\n",
irq);
if (ioinfo[irq] == INVALID_STORAGE_AREA)
/* FIXME: we should do device rec. here... */
break;
ioinfo[irq]->ssd_info.valid = 1;
ioinfo[irq]->ssd_info.type = 1;
break;
case 2: /* Message subchannel */
/*
* All fields except unit_addr have meaning
*/
#ifdef CONFIG_DEBUG_CHSC
if (cio_show_msg)
printk( KERN_DEBUG
"ssd: sch %x is message subchannel\n",
irq);
#endif
CIO_CRW_EVENT( 6,
"ssd: sch %x is message subchannel\n",
irq);
if (ioinfo[irq] == INVALID_STORAGE_AREA)
/* FIXME: we should do device rec. here... */
break;
ioinfo[irq]->ssd_info.valid = 1;
ioinfo[irq]->ssd_info.type = 2;
for (j=0;j<8;j++) {
if ((0x80 >> j) &
chsc_area_ssd->response_block.
response_block_data.ssd_res.path_mask &
chsc_area_ssd->response_block.
response_block_data.ssd_res.fla_valid_mask) {
if (chsc_area_ssd->response_block.
response_block_data.ssd_res.chpid[j])
if (!test_and_set_bit
(chsc_area_ssd->response_block.
response_block_data.
ssd_res.chpid[j],
&chpids_known))
if (test_bit
(chsc_area_ssd->response_block.
response_block_data.
ssd_res.chpid[j],
&chpids_logical))
set_bit(chsc_area_ssd->response_block.
response_block_data.
ssd_res.chpid[j],
&chpids);
ioinfo[irq]->ssd_info.chpid[j] =
chsc_area_ssd->response_block.
response_block_data.ssd_res.chpid[j];
ioinfo[irq]->ssd_info.fla[j] =
chsc_area_ssd->response_block.
response_block_data.ssd_res.fla[j];
}
}
break;
case 3: /* ADM subchannel */
/*
* Only sch_val, st and sch have meaning
*/
#ifdef CONFIG_DEBUG_CHSC
if (cio_show_msg)
printk( KERN_DEBUG
"ssd: sch %x is ADM subchannel\n",
irq);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 6,
"ssd: sch %x is ADM subchannel\n",
irq);
if (ioinfo[irq] == INVALID_STORAGE_AREA)
/* FIXME: we should do device rec. here... */
break;
ioinfo[irq]->ssd_info.valid = 1;
ioinfo[irq]->ssd_info.type = 3;
break;
default: /* uhm, that looks strange... */
#ifdef CONFIG_DEBUG_CHSC
if (cio_show_msg)
printk( KERN_DEBUG
"Strange subchannel type %d for sch %x\n",
chsc_area_ssd->response_block.
response_block_data.ssd_res.st,
irq);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 0,
"Strange subchannel type %d for "
"sch %x\n",
chsc_area_ssd->response_block.
response_block_data.ssd_res.st,
irq);
}
spin_unlock(&chsc_lock_ssd);
return 0;
}
} else {
spin_unlock(&chsc_lock_ssd);
if (ccode == 3)
return -ENODEV;
return -EBUSY;
}
return -EIO;
}
static int
chsc_get_sch_descriptions( void )
{
int irq = 0;
int err = 0;
CIO_TRACE_EVENT( 4, "gsdesc");
/*
* get information about chpids and link addresses
* by executing the chsc command 'store subchannel description'
*/
if (init_IRQ_complete) {
for (irq=0; irq<=highest_subchannel; irq++) {
/*
* retrieve information for each sch
*/
err = chsc_get_sch_desc_irq(irq);
if (err) {
if (!cio_chsc_err_msg) {
printk( KERN_ERR
"chsc_get_sch_descriptions:"
" Error %d while doing chsc; "
"processing "
"some machine checks may "
"not work\n",
err);
cio_chsc_err_msg=1;
}
return err;
}
}
cio_chsc_desc_avail = 1;
return 0;
} else {
/* Paranoia... */
printk( KERN_ERR
"Error: chsc_get_sch_descriptions called before "
"initialization complete\n");
return -EINVAL;
}
}
__initcall(chsc_get_sch_descriptions);
static int
__check_for_io_and_kill(int irq, __u8 mask, int fatal)
{
schib_t *schib = &ioinfo[irq]->schib;
int ret = 0;
if (schib->scsw.actl & SCSW_ACTL_DEVACT) {
if ((ioinfo[irq]->opm != mask) ||
(fatal == 0)) {
ret = CIO_PATHGONE_WAIT4INT;
}
if ((schib->scsw.actl & SCSW_ACTL_SCHACT) &&
(schib->pmcw.lpum == mask) &&
(fatal != 0)) {
int cc;
/* Kill the IO. It won't complete. */
ioinfo[irq]->ui.flags.noio = 0;
ioinfo[irq]->ui.flags.killio = 1;
cc = clear_IO(irq, 0xD2C9D3D3, 0);
if (cc != 0) {
/* Eek, can't kill io. */
CIO_CRW_EVENT(0,
"Can't kill io on "
"sch %x, clear_IO "
"returned %d!\n",
irq, cc);
ioinfo[irq]->ui.flags.killio = 0;
s390irq_spin_unlock(irq);
if ((cc == -ENODEV) &&
(ioinfo[irq]->nopfunc)) {
ioinfo[irq]->ui.flags.oper = 0;
ioinfo[irq]->nopfunc(irq,
DEVSTAT_DEVICE_GONE);
}
ret = CIO_PATHGONE_DEVGONE;
} else {
ret |= CIO_PATHGONE_WAIT4INT;
}
ioinfo[irq]->ui.flags.noio = 1;
ret |= CIO_PATHGONE_IOERR;
}
} else if (schib->scsw.actl & (SCSW_ACTL_CLEAR_PEND |
SCSW_ACTL_HALT_PEND |
SCSW_ACTL_START_PEND |
SCSW_ACTL_RESUME_PEND)) {
if ((schib->pmcw.lpum != mask) ||
(fatal == 0)) {
ret = CIO_PATHGONE_WAIT4INT;
} else {
int cc;
/* Cancel the i/o. */
cc = cancel_IO(irq);
switch (cc) {
case 0:
/* i/o cancelled; we can do path verif. */
ret = CIO_PATHGONE_IOERR;
break;
case -EBUSY:
/* Status pending, we'll get an interrupt */
ret = CIO_PATHGONE_WAIT4INT;
break;
case -EINVAL:
/*
* There is either not only the start function
* specified or we are subchannel active.
* Do a clear sch.
*/
ioinfo[irq]->ui.flags.noio = 0;
ioinfo[irq]->ui.flags.killio = 1;
cc = clear_IO(irq, 0xD2C9D3D3, 0);
if (cc != 0) {
/* Eek, can't kill io. */
CIO_CRW_EVENT(0,
"Can't kill io on "
"sch %x, clear_IO "
"returned %d!\n",
irq, cc);
ioinfo[irq]->ui.flags.killio = 0;
s390irq_spin_unlock(irq);
if ((cc == -ENODEV) &&
(ioinfo[irq]->nopfunc)) {
ioinfo[irq]->nopfunc(irq,
DEVSTAT_DEVICE_GONE);
ioinfo[irq]->ui.flags.oper = 0;
}
ret = CIO_PATHGONE_DEVGONE;
} else {
ret = CIO_PATHGONE_WAIT4INT
| CIO_PATHGONE_IOERR;
ioinfo[irq]->ui.flags.noio = 1;
}
break;
default: /* -ENODEV */
s390irq_spin_unlock(irq);
if (ioinfo[irq]->nopfunc) {
ioinfo[irq]->ui.flags.oper = 0;
ioinfo[irq]->nopfunc(irq,
DEVSTAT_DEVICE_GONE);
}
ret = CIO_PATHGONE_DEVGONE;
}
}
}
return ret;
}
void
s390_do_chpid_processing( __u8 chpid)
{
int irq;
int j;
char dbf_txt[15];
sprintf(dbf_txt, "chpr%x", chpid);
CIO_TRACE_EVENT( 2, dbf_txt);
/*
* TODO: the chpid may be not the chpid with the link incident,
* but the chpid the report came in through. How to handle???
*/
clear_bit(chpid, &chpids);
if (!test_and_clear_bit(chpid, &chpids_known)) {
#ifdef CONFIG_DEBUG_CHSC
pr_debug(KERN_DEBUG"Got link incident for unknown chpid %x\n",
chpid);
#endif /* CONFIG_DEBUG_CHSC */
return; /* we didn't know the chpid anyway */
}
for (irq=0;irq<=highest_subchannel;irq++) {
schib_t *schib;
if (ioinfo[irq] == INVALID_STORAGE_AREA)
continue; /* we don't know the device anyway */
if (ioinfo[irq]->st)
continue; /* non-io subchannel */
schib = &ioinfo[irq]->schib;
for (j=0; j<8;j++) {
int mask = 0x80 >> j;
int out = 0;
int err = 0;
if (schib->pmcw.chpid[j] != chpid)
continue;
if (stsch(irq, schib) != 0) {
ioinfo[irq]->ui.flags.oper = 0;
if (ioinfo[irq]->nopfunc)
ioinfo[irq]->nopfunc(irq, DEVSTAT_DEVICE_GONE);
break;
}
s390irq_spin_lock(irq);
ioinfo[irq]->ui.flags.noio = 1;
/* Do we still expect an interrupt for outstanding io? */
if (ioinfo[irq]->ui.flags.busy) {
int rck = __check_for_io_and_kill(irq, mask, 1);
if (rck & CIO_PATHGONE_WAIT4INT)
out=1;
if (rck & CIO_PATHGONE_IOERR)
err=1;
if (rck & CIO_PATHGONE_DEVGONE)
break;
}
s390irq_spin_unlock(irq);
/*
* Tell the device driver not to disturb us.
* If the driver is not capable of handling
* DEVSTAT_NOT_ACC, it doesn't want path grouping anyway.
*/
if (ioinfo[irq]->ui.flags.ready &&
schib->pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap) {
if (err)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC_ERR);
else
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
}
ioinfo[irq]->opm &= ~mask;
if (out)
break;
/*
* Always schedule the path verification, even if opm=0.
* Reason: We can't rely on stsch() to return latest&greatest
* values, if a device selections hasn't been performed, and
* we might miss a path we didn't get a mchk for.
*/
if (ioinfo[irq]->ui.flags.ready)
s390_schedule_path_verification(irq);
else {
ioinfo[irq]->ui.flags.noio = 0;
ioinfo[irq]->ui.flags.killio = 0;
}
break;
}
}
}
void
s390_do_res_acc_processing( __u8 chpid, __u16 fla, int info)
{
char dbf_txt[15];
int irq = 0;
__u32 fla_mask = 0xffff;
int chp;
int mask;
sprintf(dbf_txt, "accpr%x", chpid);
CIO_TRACE_EVENT( 2, dbf_txt);
if (info != CHSC_SEI_ACC_CHPID) {
sprintf(dbf_txt, "fla%x", fla);
CIO_TRACE_EVENT( 2, dbf_txt);
}
sprintf(dbf_txt, "info:%d", info);
CIO_TRACE_EVENT( 2, dbf_txt);
/*
* I/O resources may have become accessible.
* Scan through all subchannels that may be concerned and
* do a validation on those.
* The more information we have (info), the less scanning
* will we have to do.
*/
if (!cio_chsc_desc_avail)
chsc_get_sch_descriptions();
if (!cio_chsc_desc_avail) {
/*
* Something went wrong...
*/
#ifdef CONFIG_DEBUG_CRW
printk( KERN_WARNING
"Error: Could not retrieve subchannel descriptions, "
"will not process css machine check...\n");
#endif /* CONFIG_DEBUG_CRW */
CIO_CRW_EVENT( 0,
"Error: Could not retrieve subchannel descriptions, "
"will not process css machine check...\n");
return;
}
if (!test_bit(chpid, &chpids_logical)) {
#ifdef CONFIG_DEBUG_CHSC
printk(KERN_DEBUG"chpid %x is logically offline, "
"skipping res acc processing\n", chpid);
#endif /* CONFIG_DEBUG_CHSC */
return; /* no need to do the rest */
}
switch (info) {
case CHSC_SEI_ACC_CHPID: /*
* worst case, we only know about the chpid
* the devices are attached to
*/
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG "Looking at chpid %x...\n", chpid);
#endif /* CONFIG_DEBUG_CHSC */
for (irq=0; irq<__MAX_SUBCHANNELS; irq++) {
if((ioinfo[irq] != INVALID_STORAGE_AREA)
&& (ioinfo[irq]->st != 0))
continue;
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
/*
* We don't know the device yet, but since a path
* may be available now to the device we'll have
* to do recognition again.
* Since we don't have any idea about which chpid
* that beast may be on we'll have to do a stsch
* on all devices, grr...
*/
int valret = 0;
valret = s390_validate_subchannel(irq,0);
if (valret == -ENXIO) {
/* We're through */
return;
}
if (irq > highest_subchannel)
highest_subchannel = irq;
if (valret == 0)
s390_device_recognition_irq(irq);
continue;
}
for (chp=0;chp<=7;chp++) {
mask = 0x80 >> chp;
/*
* check if chpid is in information
* updated by ssd
*/
if ((!ioinfo[irq]->ssd_info.valid) ||
(ioinfo[irq]->ssd_info.chpid[chp] != chpid))
continue;
/* Tell the device driver not to disturb us. */
if (ioinfo[irq]->ui.flags.ready &&
ioinfo[irq]->schib.pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
ioinfo[irq]->ui.flags.noio = 1;
/* Do we still expect an interrupt for outstanding io? */
if (ioinfo[irq]->ui.flags.busy)
/* Wait for interrupt. */
break;
if (ioinfo[irq]->ui.flags.ready) {
s390_schedule_path_verification(irq);
} else
ioinfo[irq]->ui.flags.noio = 0;
break;
}
}
break;
case CHSC_SEI_ACC_LINKADDR: /*
* better, we know the link determined by
* the link address and the chpid
*/
fla_mask = 0xff00;
/* fallthrough */
case CHSC_SEI_ACC_FULLLINKADDR: /*
* best case, we know the CU image
* by chpid and full link address
*/
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG "Looking at chpid %x, link addr %x...\n",
chpid, fla);
#endif /* CONFIG_DEBUG_CHSC */
for (irq=0; irq<__MAX_SUBCHANNELS; irq++) {
int j;
/*
* Walk through all subchannels and
* look if our chpid and our (masked) link
* address are in somewhere
* Do a stsch for the found subchannels and
* perform path grouping
*/
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
/* The full program again (see above), grr... */
int valret = 0;
valret = s390_validate_subchannel(irq,0);
if (valret == -ENXIO) {
/* We're done */
return;
}
if (irq > highest_subchannel)
highest_subchannel = irq;
if (valret == 0)
s390_device_recognition_irq(irq);
continue;
}
if (ioinfo[irq]->st != 0)
continue;
/* Update our ssd_info */
if (chsc_get_sch_desc_irq(irq))
break;
for (j=0;j<8;j++) {
if ((ioinfo[irq]->ssd_info.chpid[j] != chpid) ||
((ioinfo[irq]->ssd_info.fla[j]&fla_mask) != fla))
continue;
/* Tell the device driver not to disturb us. */
if (ioinfo[irq]->ui.flags.ready &&
ioinfo[irq]->schib.pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
ioinfo[irq]->ui.flags.noio = 1;
/* Do we still expect an interrupt for outstanding io? */
if (ioinfo[irq]->ui.flags.busy)
/* Wait for interrupt. */
break;
if (ioinfo[irq]->ui.flags.ready) {
s390_schedule_path_verification(irq);
} else
ioinfo[irq]->ui.flags.noio = 0;
break;
}
break;
}
break;
default: BUG();
}
}
static int
__get_chpid_from_lir(void *data)
{
struct lir {
u8 iq;
u8 ic;
u16 sci;
/* incident-node descriptor */
u32 indesc[28];
/* attached-node descriptor */
u32 andesc[28];
/* incident-specific information */
u32 isinfo[28];
} *lir;
lir = (struct lir*) data;
if (!(lir->iq&0x80))
/* NULL link incident record */
return -EINVAL;
if (!(lir->indesc[0]&0xc0000000))
/* node descriptor not valid */
return -EINVAL;
if (!(lir->indesc[0]&0x10000000))
/* don't handle device-type nodes - FIXME */
return -EINVAL;
/* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
return (u16) (lir->indesc[0]&0x000000ff);
}
void
s390_process_css( void )
{
int ccode, do_sei, chpid;
CIO_TRACE_EVENT( 2, "prcss");
spin_lock(&chsc_lock_sei);
if (!chsc_area_sei) {
if (init_IRQ_complete)
chsc_area_sei = kmalloc(sizeof(chsc_area_t),GFP_KERNEL);
else
chsc_area_sei = alloc_bootmem(sizeof(chsc_area_t));
}
if (!chsc_area_sei) {
printk( KERN_CRIT
"No memory to store event information...\n");
spin_unlock(&chsc_lock_sei);
return;
}
do_sei = 1;
while (do_sei) {
do_sei = 0;
/*
* build the chsc request block for store event information
* and do the call
*/
memset(chsc_area_sei,0,sizeof(chsc_area_t));
chsc_area_sei->request_block.command_code1=0x0010;
chsc_area_sei->request_block.command_code2=0x000E;
ccode = chsc(chsc_area_sei);
if (ccode)
break;
/* for debug purposes, check for problems */
if (chsc_area_sei->response_block.response_code == 0x0003) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING
"s390_process_css: error in chsc request block!\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2,
"s390_process_css: "
"error in chsc request block!\n");
break;
}
if (chsc_area_sei->response_block.response_code == 0x0005) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING
"s390_process_css: no event information stored\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2,
"s390_process_css: "
"no event information stored\n");
break;
}
if (chsc_area_sei->response_block.response_code == 0x0002) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING
"s390_process_css: invalid command!\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2,
"s390_process_css: "
"invalid command!\n");
break;
}
if (chsc_area_sei->response_block.response_code != 0x0001) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_WARNING
"s390_process_css: unknown response code %d\n",
chsc_area_sei->response_block.response_code);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2,
"s390_process_css: unknown response "
"code %d\n",
chsc_area_sei->response_block.response_code);
break;
}
/* everything ok */
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"s390_process_css: "
"event information successfully stored\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 4,
"s390_process_css: "
"event information successfully stored\n");
/* Check if there is more event information pending. */
if (chsc_area_sei->response_block.response_block_data.
sei_res.flags & 0x80) {
#ifdef CONFIG_DEBUG_CHSC
printk(KERN_INFO"s390_process_css: further event "
"information pending...\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2, "further event information pending\n");
do_sei = 1;
}
/* Check if we might have lost some information. */
if (chsc_area_sei->response_block.response_block_data.
sei_res.flags & 0x40) {
#ifdef CONFIG_DEBUG_CHSC
printk(KERN_ERR"s390_process_css: Event information has "
"been lost due to overflow!\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2, "Event information has "
"been lost due to overflow!\n");
}
if (chsc_area_sei->response_block.
response_block_data.sei_res.rs != 4) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_ERR
"s390_process_css: "
"reporting source (%04X) isn't a chpid!\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 2,
"s390_process_css: "
"reporting source (%04X) isn't a chpid!\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid);
continue;
}
/* which kind of information was stored? */
switch (chsc_area_sei->response_block.
response_block_data.sei_res.cc) {
case 1: /* link incident*/
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"s390_process_css: "
"channel subsystem reports link incident,"
" source is chpid %x\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 4,
"s390_process_css: "
"channel subsystem reports "
"link incident, "
"source is chpid %x\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid);
chpid = __get_chpid_from_lir(chsc_area_sei->response_block.
response_block_data.sei_res.
ccdf);
if (chpid >= 0)
s390_do_chpid_processing(chpid);
break;
case 2: /* i/o resource accessibiliy */
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"s390_process_css: channel subsystem "
"reports some I/O devices "
"may have become accessible\n");
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 4,
"s390_process_css: "
"channel subsystem reports "
"some I/O devices "
"may have become accessible\n");
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"Data received after sei: \n");
printk( KERN_DEBUG
"Validity flags: %x\n",
chsc_area_sei->response_block.
response_block_data.sei_res.vf);
#endif /* CONFIG_DEBUG_CHSC */
if ((chsc_area_sei->response_block.
response_block_data.sei_res.vf&0x80)
== 0) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG "chpid: %x\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid);
#endif /* CONFIG_DEBUG_CHSC */
s390_do_res_acc_processing
(chsc_area_sei->response_block.
response_block_data.sei_res.rsid,
0,
CHSC_SEI_ACC_CHPID);
} else if ((chsc_area_sei->response_block.
response_block_data.sei_res.vf&0xc0)
== 0x80) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"chpid: %x link addr: %x\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid,
chsc_area_sei->response_block.
response_block_data.sei_res.fla);
#endif /* CONFIG_DEBUG_CHSC */
s390_do_res_acc_processing
(chsc_area_sei->response_block.
response_block_data.sei_res.rsid,
chsc_area_sei->response_block.
response_block_data.sei_res.fla,
CHSC_SEI_ACC_LINKADDR);
} else if ((chsc_area_sei->response_block.
response_block_data.sei_res.vf & 0xc0)
== 0xc0) {
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"chpid: %x "
"full link addr: %x\n",
chsc_area_sei->response_block.
response_block_data.sei_res.rsid,
chsc_area_sei->response_block.
response_block_data.sei_res.fla);
#endif /* CONFIG_DEBUG_CHSC */
s390_do_res_acc_processing
(chsc_area_sei->response_block.
response_block_data.sei_res.rsid,
chsc_area_sei->response_block.
response_block_data.sei_res.fla,
CHSC_SEI_ACC_FULLLINKADDR);
}
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG "\n");
#endif /* CONFIG_DEBUG_CHSC */
break;
default: /* other stuff */
#ifdef CONFIG_DEBUG_CHSC
printk( KERN_DEBUG
"s390_process_css: event %d\n",
chsc_area_sei->response_block.
response_block_data.sei_res.cc);
#endif /* CONFIG_DEBUG_CHSC */
CIO_CRW_EVENT( 4,
"s390_process_css: event %d\n",
chsc_area_sei->response_block.
response_block_data.sei_res.cc);
break;
}
}
spin_unlock(&chsc_lock_sei);
}
#endif
static void
__process_chp_gone(int irq, int chpid)
{
schib_t *schib = &ioinfo[irq]->schib;
int i;
for (i=0;i<8;i++) {
int mask = 0x80>>i;
int out = 0;
int err = 0;
if (schib->pmcw.chpid[i] != chpid)
continue;
if (stsch(irq, schib) != 0) {
ioinfo[irq]->ui.flags.oper = 0;
if (ioinfo[irq]->nopfunc)
ioinfo[irq]->nopfunc(irq, DEVSTAT_DEVICE_GONE);
break;
}
s390irq_spin_lock(irq);
ioinfo[irq]->ui.flags.noio = 1;
/* Do we still expect an interrupt for outstanding io? */
if (ioinfo[irq]->ui.flags.busy) {
int rck = __check_for_io_and_kill(irq, mask, 1);
if (rck & CIO_PATHGONE_WAIT4INT)
out=1;
if (rck & CIO_PATHGONE_IOERR)
err=1;
if (rck & CIO_PATHGONE_DEVGONE)
break;
}
s390irq_spin_unlock(irq);
/* Tell the device driver not to disturb us. */
if (ioinfo[irq]->ui.flags.ready &&
schib->pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap) {
if (err)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC_ERR);
else
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
}
if (out)
break;
if (ioinfo[irq]->ui.flags.ready) {
s390_schedule_path_verification(irq);
} else {
ioinfo[irq]->ui.flags.noio = 0;
ioinfo[irq]->ui.flags.killio = 0;
}
break;
}
}
static void
__process_chp_come(int irq, int chpid)
{
schib_t *schib = &ioinfo[irq]->schib;
int i;
for (i=0;i<8;i++) {
if (schib->pmcw.chpid[i] != chpid)
continue;
/* Tell the device driver not to disturb us. */
if (ioinfo[irq]->ui.flags.ready &&
schib->pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
ioinfo[irq]->ui.flags.noio = 1;
/* Do we still expect an interrupt for outstanding io? */
if (ioinfo[irq]->ui.flags.busy)
/* Wait for interrupt. */
break;
if (ioinfo[irq]->ui.flags.ready)
s390_schedule_path_verification(irq);
else
ioinfo[irq]->ui.flags.noio = 0;
break;
}
}
static void
s390_process_chp_source(int chpid, int onoff)
{
int irq;
int ret;
char dbf_txt[15];
sprintf(dbf_txt, "prchp%x", chpid);
CIO_TRACE_EVENT(2, dbf_txt);
#ifdef CONFIG_CHSC
if (onoff == 0) {
clear_bit(chpid, &chpids);
} else {
set_bit(chpid, &chpids);
set_bit(chpid, &chpids_known);
}
#endif /* CONFIG_CHSC */
if (onoff == 0) {
for (irq=0;irq<=highest_subchannel;irq++) {
if ((ioinfo[irq] == INVALID_STORAGE_AREA)
|| (ioinfo[irq]->st != 0))
continue;
__process_chp_gone(irq, chpid);
}
return;
}
for (irq=0;irq<__MAX_SUBCHANNELS;irq++) {
if (ioinfo[irq] == INVALID_STORAGE_AREA) {
ret = s390_validate_subchannel(irq,0);
if (ret == 0) {
if (irq > highest_subchannel)
highest_subchannel = irq;
#ifdef CONFIG_DEBUG_CRW
printk(KERN_DEBUG"process_chp_source: Found "
"device on irq %x\n", irq);
#endif /* CONFIG_DEBUG_CRW */
CIO_CRW_EVENT(4, "Found device on irq %x\n",
irq);
s390_device_recognition_irq(irq);
}
} else if (ioinfo[irq]->st == 0) {
ret = stsch(irq, &ioinfo[irq]->schib);
if (ret != 0)
ret = -ENXIO;
} else
continue;
if (ret == -ENXIO)
/* We're through. */
return;
if (ret != 0)
continue;
__process_chp_come(irq, chpid);
}
}
/*
* s390_do_crw_pending
*
* Called by the machine check handler to process CRW pending
* conditions. It may be a single CRW, or CRWs may be chained.
*
* Note : we currently process CRWs for subchannel source only
*/
void
s390_do_crw_pending (crwe_t * pcrwe)
{
int irq;
int chpid;
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG "do_crw_pending : starting ...\n");
#endif
CIO_CRW_EVENT( 2, "do_crw_pending: starting\n");
while (pcrwe != NULL) {
switch (pcrwe->crw.rsc) {
case CRW_RSC_SCH:
irq = pcrwe->crw.rsid;
#ifdef CONFIG_DEBUG_CRW
printk (KERN_NOTICE "do_crw_pending : source is "
"subchannel %04X\n", irq);
#endif
CIO_CRW_EVENT(2, "source is subchannel %04X\n",
irq);
s390_process_subchannel_source (irq);
break;
case CRW_RSC_MONITOR:
#ifdef CONFIG_DEBUG_CRW
printk (KERN_NOTICE "do_crw_pending : source is "
"monitoring facility\n");
#endif
CIO_CRW_EVENT(2, "source is monitoring facility\n");
break;
case CRW_RSC_CPATH:
chpid = pcrwe->crw.rsid;
#ifdef CONFIG_DEBUG_CRW
printk (KERN_NOTICE "do_crw_pending : source is "
"channel path %02X\n", chpid);
#endif
CIO_CRW_EVENT(2, "source is channel path %02X\n",
chpid);
switch (pcrwe->crw.erc) {
case CRW_ERC_IPARM: /* Path has come. */
s390_process_chp_source(chpid, 1);
break;
case CRW_ERC_PERRI: /* Path has gone. */
s390_process_chp_source(chpid, 0);
break;
default:
#ifdef CONFIG_DEBUG_CRW
printk(KERN_WARNING"do_crw_pending: don't "
"know how to handle erc=%x\n",
pcrwe->crw.erc);
#endif /* CONFIG_DEBUG_CRW */
CIO_CRW_EVENT(0, "don't know how to handle "
"erc=%x\n", pcrwe->crw.erc);
}
break;
case CRW_RSC_CONFIG:
#ifdef CONFIG_DEBUG_CRW
printk (KERN_NOTICE "do_crw_pending : source is "
"configuration-alert facility\n");
#endif
CIO_CRW_EVENT(2, "source is configuration-alert facility\n");
break;
case CRW_RSC_CSS:
#ifdef CONFIG_DEBUG_CRW
printk (KERN_NOTICE "do_crw_pending : source is "
"channel subsystem\n");
#endif
CIO_CRW_EVENT(2, "source is channel subsystem\n");
#ifdef CONFIG_CHSC
s390_process_css();
#endif
break;
default:
#ifdef CONFIG_DEBUG_CRW
printk (KERN_NOTICE
"do_crw_pending : unknown source\n");
#endif
CIO_CRW_EVENT( 2, "unknown source\n");
break;
}
pcrwe = pcrwe->crwe_next;
}
#ifdef CONFIG_DEBUG_CRW
printk (KERN_DEBUG "do_crw_pending : done\n");
#endif
CIO_CRW_EVENT(2, "do_crw_pending: done\n");
return;
}
/* added by Holger Smolinski for reipl support in reipl.S */
extern void do_reipl (int);
void
reipl (int sch)
{
int i;
s390_dev_info_t dev_info;
for (i = 0; i <= highest_subchannel; i++) {
if (get_dev_info_by_irq (i, &dev_info) == 0
&& (dev_info.status & DEVSTAT_DEVICE_OWNED)) {
free_irq (i, ioinfo[i]->irq_desc.dev_id);
}
}
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0);
else
do_reipl (0x10000 | sch);
}
/*
* Function: cio_debug_init
* Initializes three debug logs (under /proc/s390dbf) for common I/O:
* - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on
* - cio_trace logs the calling of different functions
* - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on
* debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW
*/
int
cio_debug_init (void)
{
int ret = 0;
cio_debug_msg_id = debug_register ("cio_msg", 4, 4, 16 * sizeof (long));
if (cio_debug_msg_id != NULL) {
debug_register_view (cio_debug_msg_id, &debug_sprintf_view);
debug_set_level (cio_debug_msg_id, 6);
} else {
ret = -1;
}
cio_debug_trace_id = debug_register ("cio_trace", 4, 4, 8);
if (cio_debug_trace_id != NULL) {
debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view);
debug_set_level (cio_debug_trace_id, 6);
} else {
ret = -1;
}
cio_debug_crw_id = debug_register ("cio_crw", 2, 4, 16 * sizeof (long));
if (cio_debug_crw_id != NULL) {
debug_register_view (cio_debug_crw_id, &debug_sprintf_view);
debug_set_level (cio_debug_crw_id, 6);
} else {
ret = -1;
}
if (ret)
return ret;
cio_debug_initialized = 1;
return 0;
}
__initcall (cio_debug_init);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_CHSC
/*
* Function: cio_parse_chpids_proc_parameters
* parse the stuff piped to /proc/chpids
*/
void
cio_parse_chpids_proc_parameters(char* buf)
{
int i;
int cp;
int ret;
if (strstr(buf, "on ")) {
for (i=0; i<3; i++) {
buf++;
}
cp = blacklist_strtoul(buf, &buf);
chsc_get_sch_descriptions();
if (!cio_chsc_desc_avail) {
printk(KERN_ERR "Could not get chpid status, "
"vary on/off not available\n");
return;
}
if (!test_bit(cp, &chpids)) {
ret = s390_vary_chpid(cp, 1);
if (ret == -EINVAL) {
#ifdef CONFIG_DEBUG_CHSC
printk(KERN_ERR "/proc/chpids: "
"Invalid chpid specified\n");
#else /* CONFIG_DEBUG_CHSC */
printk(KERN_DEBUG "/proc/chpids: "
"Invalid chpid specified\n");
#endif /* CONFIG_DEBUG_CHSC */
} else if (ret == 0) {
printk(KERN_INFO "/proc/chpids: "
"Varied chpid %x logically online\n",
cp);
}
} else {
printk(KERN_ERR "/proc/chpids: chpid %x is "
"already online\n",
cp);
}
} else if (strstr(buf, "off ")) {
for (i=0; i<4; i++) {
buf++;
}
cp = blacklist_strtoul(buf, &buf);
chsc_get_sch_descriptions();
if (!cio_chsc_desc_avail) {
printk(KERN_ERR "Could not get chpid status, "
"vary on/off not available\n");
return;
}
if (test_bit(cp, &chpids)) {
ret = s390_vary_chpid(cp, 0);
if (ret == -EINVAL) {
#ifdef CONFIG_DEBUG_CHSC
printk(KERN_ERR "/proc/chpids: "
"Invalid chpid specified\n");
#else /* CONFIG_DEBUG_CHSC */
printk(KERN_DEBUG "/proc/chpids: "
"Invalid chpid specified\n");
#endif /* CONFIG_DEBUG_CHSC */
} else if (ret == 0) {
printk(KERN_INFO "/proc/chpids: "
"Varied chpid %x logically offline\n",
cp);
}
} else {
printk(KERN_ERR "/proc/chpids: "
"chpid %x is already offline\n",
cp);
}
} else {
printk(KERN_ERR "/proc/chpids: Parse error; "
"try using '{on,off} <chpid>'\n");
}
}
static void
__vary_chpid_offline(int irq, int chpid)
{
schib_t *schib = &ioinfo[irq]->schib;
int i;
for (i=0;i<8;i++) {
int mask = 0x80>>i;
int out = 0;
unsigned long flags;
if (ioinfo[irq]->ssd_info.chpid[i] != chpid)
continue;
s390irq_spin_lock_irqsave(irq, flags);
ioinfo[irq]->ui.flags.noio = 1;
/* Hmm, the path is not really gone... */
if (ioinfo[irq]->ui.flags.busy) {
if (__check_for_io_and_kill(irq, mask, 0) != 0)
out=1;
}
s390irq_spin_unlock_irqrestore(irq, flags);
/* Tell the device driver not to disturb us. */
if (ioinfo[irq]->ui.flags.ready &&
schib->pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
if (out)
break;
if (ioinfo[irq]->ui.flags.ready)
s390_schedule_path_verification(irq);
else
ioinfo[irq]->ui.flags.noio = 0;
break;
}
}
static void
__vary_chpid_online(int irq, int chpid)
{
schib_t *schib = &ioinfo[irq]->schib;
int i;
for (i=0;i<8;i++) {
if (schib->pmcw.chpid[i] != chpid)
continue;
/* Tell the device driver not to disturb us. */
if (ioinfo[irq]->ui.flags.ready &&
schib->pmcw.pim != 0x80 &&
ioinfo[irq]->nopfunc &&
ioinfo[irq]->ui.flags.notacccap)
ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC);
ioinfo[irq]->ui.flags.noio = 1;
/* Do we still expect an interrupt for outstanding io? */
if (ioinfo[irq]->ui.flags.busy)
/* Wait for interrupt. */
break;
s390_schedule_path_verification(irq);
break;
}
}
/*
* Function: s390_vary_chpid
* Varies the specified chpid online or offline
*/
int
s390_vary_chpid( __u8 chpid, int on)
{
char dbf_text[15];
int irq;
if ((chpid <=0) || (chpid >= NR_CHPIDS))
return -EINVAL;
sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid);
CIO_TRACE_EVENT( 2, dbf_text);
if (!test_bit(chpid, &chpids_known)) {
printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid);
return -EINVAL;
}
if (on && test_bit(chpid, &chpids_logical)) {
printk(KERN_ERR "chpid %02X already logically online\n",
chpid);
return -EINVAL;
}
if (!on && !test_bit(chpid, &chpids_logical)) {
printk(KERN_ERR "chpid %02X already logically offline\n",
chpid);
return -EINVAL;
}
if (on) {
set_bit(chpid, &chpids_logical);
set_bit(chpid, &chpids);
} else {
clear_bit(chpid, &chpids_logical);
clear_bit(chpid, &chpids);
}
/*
* Redo PathVerification on the devices the chpid connects to
*/
for (irq=0;irq<=highest_subchannel;irq++) {
if (ioinfo[irq] == INVALID_STORAGE_AREA)
continue;
if (ioinfo[irq]->st)
continue;
if (!ioinfo[irq]->ssd_info.valid)
continue;
if (on)
__vary_chpid_online(irq, chpid);
else
__vary_chpid_offline(irq, chpid);
}
return 0;
}
#endif /* CONFIG_CHSC */
/*
* Display info on subchannels in /proc/subchannels.
* Adapted from procfs stuff in dasd.c by Cornelia Huck, 02/28/01.
*/
typedef struct {
char *data;
int len;
} tempinfo_t;
#define MIN(a,b) ((a)<(b)?(a):(b))
static struct proc_dir_entry *chan_subch_entry;
static int
chan_subch_open (struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
int i = 0;
int j = 0;
tempinfo_t *info;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
if (info == NULL) {
printk (KERN_WARNING "No memory available for data\n");
return -ENOMEM;
} else {
file->private_data = (void *) info;
}
size += (highest_subchannel + 1) * 128;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
printk (KERN_WARNING "No memory available for data\n");
vfree (info);
return -ENOMEM;
}
len += sprintf (info->data + len,
"Device sch. Dev Type/Model CU in use PIM PAM POM CHPIDs\n");
len += sprintf (info->data + len,
"---------------------------------------------------------------------\n");
for (i = 0; i <= highest_subchannel; i++) {
if (!((ioinfo[i] == NULL) || (ioinfo[i] == INVALID_STORAGE_AREA)
|| (ioinfo[i]->st )|| !(ioinfo[i]->ui.flags.oper))) {
len +=
sprintf (info->data + len, "%04X %04X ",
ioinfo[i]->schib.pmcw.dev, i);
if (ioinfo[i]->senseid.dev_type != 0) {
len += sprintf (info->data + len,
"%04X/%02X %04X/%02X",
ioinfo[i]->senseid.dev_type,
ioinfo[i]->senseid.dev_model,
ioinfo[i]->senseid.cu_type,
ioinfo[i]->senseid.cu_model);
} else {
len += sprintf (info->data + len,
" %04X/%02X",
ioinfo[i]->senseid.cu_type,
ioinfo[i]->senseid.cu_model);
}
if (ioinfo[i]->ui.flags.ready) {
len += sprintf (info->data + len, " yes ");
} else {
len += sprintf (info->data + len, " ");
}
len += sprintf (info->data + len,
" %02X %02X %02X ",
ioinfo[i]->schib.pmcw.pim,
ioinfo[i]->schib.pmcw.pam,
ioinfo[i]->schib.pmcw.pom);
for (j = 0; j < 8; j++) {
len += sprintf (info->data + len,
"%02X",
ioinfo[i]->schib.pmcw.chpid[j]);
if (j == 3) {
len += sprintf (info->data + len, " ");
}
}
len += sprintf (info->data + len, "\n");
}
}
info->len = len;
return rc;
}
static int
chan_subch_close (struct inode *inode, struct file *file)
{
int rc = 0;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (p_info) {
if (p_info->data)
vfree (p_info->data);
vfree (p_info);
}
return rc;
}
static ssize_t
chan_subch_read (struct file *file, char *user_buf, size_t user_len,
loff_t * offset)
{
loff_t len;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (*offset >= p_info->len) {
return 0;
} else {
len = MIN (user_len, (p_info->len - *offset));
if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
return -EFAULT;
(*offset) += len;
return len;
}
}
static struct file_operations chan_subch_file_ops = {
read:chan_subch_read, open:chan_subch_open, release:chan_subch_close,
};
static int
chan_proc_init (void)
{
chan_subch_entry =
create_proc_entry ("subchannels", S_IFREG | S_IRUGO, &proc_root);
chan_subch_entry->proc_fops = &chan_subch_file_ops;
return 1;
}
__initcall (chan_proc_init);
void
chan_proc_cleanup (void)
{
remove_proc_entry ("subchannels", &proc_root);
}
/*
* Display device specific information under /proc/deviceinfo/<devno>
*/ static struct proc_dir_entry *cio_procfs_deviceinfo_root = NULL;
/*
* cio_procfs_device_list holds all devno-specific procfs directories
*/
typedef struct {
int devno;
struct proc_dir_entry *cio_device_entry;
struct proc_dir_entry *cio_sensedata_entry;
struct proc_dir_entry *cio_in_use_entry;
struct proc_dir_entry *cio_chpid_entry;
} cio_procfs_entry_t;
typedef struct _cio_procfs_device {
struct _cio_procfs_device *next;
cio_procfs_entry_t *entry;
} cio_procfs_device_t;
cio_procfs_device_t *cio_procfs_device_list = NULL;
/*
* File operations
*/
static int
cio_device_entry_close (struct inode *inode, struct file *file)
{
int rc = 0;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (p_info) {
if (p_info->data)
vfree (p_info->data);
vfree (p_info);
}
return rc;
}
static ssize_t
cio_device_entry_read (struct file *file, char *user_buf, size_t user_len,
loff_t * offset)
{
loff_t len;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (*offset >= p_info->len) {
return 0;
} else {
len = MIN (user_len, (p_info->len - *offset));
if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
return -EFAULT;
(*offset) += len;
return len;
}
}
static int
cio_sensedata_entry_open (struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
tempinfo_t *info;
int irq;
int devno;
char *devno_str;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
if (info == NULL) {
printk (KERN_WARNING "No memory available for data\n");
rc = -ENOMEM;
} else {
file->private_data = (void *) info;
size += 2 * 32;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
printk (KERN_WARNING "No memory available for data\n");
vfree (info);
rc = -ENOMEM;
} else {
devno_str = kmalloc (6 * sizeof (char), GFP_KERNEL);
memset (devno_str, 0, 6 * sizeof (char));
memcpy (devno_str,
file->f_dentry->d_parent->d_name.name,
strlen (file->f_dentry->d_parent->d_name.name) +
1);
devno = simple_strtoul (devno_str, &devno_str, 16);
irq = get_irq_by_devno (devno);
if (irq != -1) {
len +=
sprintf (info->data + len,
"Dev Type/Mod: ");
if (ioinfo[irq]->senseid.dev_type == 0) {
len +=
sprintf (info->data + len,
"%04X/%02X\n",
ioinfo[irq]->senseid.
cu_type,
ioinfo[irq]->senseid.
cu_model);
} else {
len +=
sprintf (info->data + len,
"%04X/%02X\n",
ioinfo[irq]->senseid.
dev_type,
ioinfo[irq]->senseid.
dev_model);
len +=
sprintf (info->data + len,
"CU Type/Mod: %04X/%02X\n",
ioinfo[irq]->senseid.
cu_type,
ioinfo[irq]->senseid.
cu_model);
}
}
info->len = len;
}
}
return rc;
}
static int
cio_in_use_entry_open (struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
tempinfo_t *info;
int irq;
int devno;
char *devno_str;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
if (info == NULL) {
printk (KERN_WARNING "No memory available for data\n");
rc = -ENOMEM;
} else {
file->private_data = (void *) info;
size += 8;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
printk (KERN_WARNING "No memory available for data\n");
vfree (info);
rc = -ENOMEM;
} else {
devno_str = kmalloc (6 * sizeof (char), GFP_KERNEL);
memset (devno_str, 0, 6 * sizeof (char));
memcpy (devno_str,
file->f_dentry->d_parent->d_name.name,
strlen (file->f_dentry->d_parent->d_name.name) +
1);
devno = simple_strtoul (devno_str, &devno_str, 16);
irq = get_irq_by_devno (devno);
if (irq != -1) {
len +=
sprintf (info->data + len, "%s\n",
ioinfo[irq]->ui.flags.
ready ? "yes" : "no");
}
info->len = len;
}
}
return rc;
}
static int
cio_chpid_entry_open (struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
tempinfo_t *info;
int irq;
int devno;
int i;
char *devno_str;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
if (info == NULL) {
printk (KERN_WARNING "No memory available for data\n");
rc = -ENOMEM;
} else {
file->private_data = (void *) info;
size += 8 * 16;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
printk (KERN_WARNING "No memory available for data\n");
vfree (info);
rc = -ENOMEM;
} else {
devno_str = kmalloc (6 * sizeof (char), GFP_KERNEL);
memset (devno_str, 0, 6 * sizeof (char));
memcpy (devno_str,
file->f_dentry->d_parent->d_name.name,
strlen (file->f_dentry->d_parent->d_name.name) +
1);
devno = simple_strtoul (devno_str, &devno_str, 16);
irq = get_irq_by_devno (devno);
if (irq != -1) {
for (i = 0; i < 8; i++) {
len +=
sprintf (info->data + len,
"CHPID[%d]: ", i);
len +=
sprintf (info->data + len, "%02X\n",
ioinfo[irq]->schib.pmcw.
chpid[i]);
}
}
info->len = len;
}
}
return rc;
}
static struct file_operations cio_sensedata_entry_file_ops = {
read:cio_device_entry_read, open:cio_sensedata_entry_open,
release:cio_device_entry_close,
};
static struct file_operations cio_in_use_entry_file_ops = {
read:cio_device_entry_read, open:cio_in_use_entry_open,
release:cio_device_entry_close,
};
static struct file_operations cio_chpid_entry_file_ops = {
read:cio_device_entry_read, open:cio_chpid_entry_open,
release:cio_device_entry_close,
};
/*
* Function: cio_procfs_device_create
* create procfs entry for given device number
* and insert it into list
*/
int
cio_procfs_device_create (int devno)
{
cio_procfs_entry_t *entry;
cio_procfs_device_t *tmp;
cio_procfs_device_t *where;
char buf[8];
int i;
int rc = 0;
/* create the directory entry */
entry =
(cio_procfs_entry_t *) kmalloc (sizeof (cio_procfs_entry_t),
GFP_KERNEL);
if (entry) {
entry->devno = devno;
sprintf (buf, "%x", devno);
entry->cio_device_entry =
proc_mkdir (buf, cio_procfs_deviceinfo_root);
if (entry->cio_device_entry) {
tmp = (cio_procfs_device_t *)
kmalloc (sizeof (cio_procfs_device_t), GFP_KERNEL);
if (tmp) {
tmp->entry = entry;
if (cio_procfs_device_list == NULL) {
cio_procfs_device_list = tmp;
tmp->next = NULL;
} else {
where = cio_procfs_device_list;
i = where->entry->devno;
while ((devno > i)
&& (where->next != NULL)) {
where = where->next;
i = where->entry->devno;
}
if (where->next == NULL) {
where->next = tmp;
tmp->next = NULL;
} else {
tmp->next = where->next;
where->next = tmp;
}
}
/* create the different entries */
entry->cio_sensedata_entry =
create_proc_entry ("sensedata",
S_IFREG | S_IRUGO,
entry->cio_device_entry);
entry->cio_sensedata_entry->proc_fops =
&cio_sensedata_entry_file_ops;
entry->cio_in_use_entry =
create_proc_entry ("in_use",
S_IFREG | S_IRUGO,
entry->cio_device_entry);
entry->cio_in_use_entry->proc_fops =
&cio_in_use_entry_file_ops;
entry->cio_chpid_entry =
create_proc_entry ("chpids",
S_IFREG | S_IRUGO,
entry->cio_device_entry);
entry->cio_chpid_entry->proc_fops =
&cio_chpid_entry_file_ops;
} else {
printk (KERN_WARNING
"Error, could not allocate procfs structure!\n");
remove_proc_entry (buf,
cio_procfs_deviceinfo_root);
kfree (entry);
rc = -ENOMEM;
}
} else {
printk (KERN_WARNING
"Error, could not allocate procfs structure!\n");
kfree (entry);
rc = -ENOMEM;
}
} else {
printk (KERN_WARNING
"Error, could not allocate procfs structure!\n");
rc = -ENOMEM;
}
return rc;
}
/*
* Function: cio_procfs_device_remove
* remove procfs entry for given device number
*/
int
cio_procfs_device_remove (int devno)
{
int rc = 0;
cio_procfs_device_t *tmp;
cio_procfs_device_t *prev = NULL;
tmp = cio_procfs_device_list;
while (tmp) {
if (tmp->entry->devno == devno)
break;
prev = tmp;
tmp = tmp->next;
}
if (tmp) {
char buf[8];
remove_proc_entry ("sensedata", tmp->entry->cio_device_entry);
remove_proc_entry ("in_use", tmp->entry->cio_device_entry);
remove_proc_entry ("chpid", tmp->entry->cio_device_entry);
sprintf (buf, "%x", devno);
remove_proc_entry (buf, cio_procfs_deviceinfo_root);
if (tmp == cio_procfs_device_list) {
cio_procfs_device_list = tmp->next;
} else {
prev->next = tmp->next;
}
kfree (tmp->entry);
kfree (tmp);
} else {
rc = -ENODEV;
}
return rc;
}
/*
* Function: cio_procfs_purge
* purge /proc/deviceinfo of entries for gone devices
*/
int
cio_procfs_device_purge (void)
{
int i;
for (i = 0; i <= highest_subchannel; i++) {
if (ioinfo[i] != INVALID_STORAGE_AREA) {
if (!ioinfo[i]->ui.flags.oper)
cio_procfs_device_remove (ioinfo[i]->devno);
}
}
return 0;
}
/*
* Function: cio_procfs_create
* create /proc/deviceinfo/ and subdirs for the devices
*/
static int
cio_procfs_create (void)
{
int irq;
if (cio_proc_devinfo) {
cio_procfs_deviceinfo_root =
proc_mkdir ("deviceinfo", &proc_root);
if (highest_subchannel >= MAX_CIO_PROCFS_ENTRIES) {
printk (KERN_ALERT
"Warning: Not enough inodes for creating all "
"entries under /proc/deviceinfo/. "
"Not every device will get an entry.\n");
}
for (irq = 0; irq <= highest_subchannel; irq++) {
if (irq >= MAX_CIO_PROCFS_ENTRIES)
break;
if (ioinfo[irq] != INVALID_STORAGE_AREA) {
if (ioinfo[irq]->ui.flags.oper)
if (cio_procfs_device_create
(ioinfo[irq]->devno) == -ENOMEM) {
printk (KERN_CRIT
"Out of memory while creating "
"entries in /proc/deviceinfo/, "
"not all devices might show up\n");
break;
}
}
}
}
return 1;
}
__initcall (cio_procfs_create);
/*
* Entry /proc/cio_ignore to display blacklisted ranges of devices.
* un-ignore devices by piping to /proc/cio_ignore:
* free all frees all blacklisted devices, free <range>,<range>,...
* frees specified ranges of devnos
* add <range>,<range>,... will add a range of devices to blacklist -
* but only for devices not already known
*/
static struct proc_dir_entry *cio_ignore_proc_entry;
static int
cio_ignore_proc_open (struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
tempinfo_t *info;
long flags;
int i, j;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
if (info == NULL) {
printk (KERN_WARNING "No memory available for data\n");
rc = -ENOMEM;
} else {
file->private_data = (void *) info;
size += nr_ignored * 6;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
printk (KERN_WARNING "No memory available for data\n");
vfree (info);
rc = -ENOMEM;
} else {
spin_lock_irqsave (&blacklist_lock, flags);
for (i = 0; i <= highest_ignored; i++)
if (test_bit (i, &bl_dev)) {
len +=
sprintf (info->data + len, "%04x ",
i);
for (j = i; (j <= highest_ignored)
&& (test_bit (j, &bl_dev)); j++) ;
j--;
if (i != j)
len +=
sprintf (info->data + len,
"- %04x", j);
len += sprintf (info->data + len, "\n");
i = j;
}
spin_unlock_irqrestore (&blacklist_lock, flags);
info->len = len;
}
}
return rc;
}
static int
cio_ignore_proc_close (struct inode *inode, struct file *file)
{
int rc = 0;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (p_info) {
if (p_info->data)
vfree (p_info->data);
vfree (p_info);
}
return rc;
}
static ssize_t
cio_ignore_proc_read (struct file *file, char *user_buf, size_t user_len,
loff_t * offset)
{
loff_t len;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (*offset >= p_info->len) {
return 0;
} else {
len = MIN (user_len, (p_info->len - *offset));
if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
return -EFAULT;
(*offset) += len;
return len;
}
}
static ssize_t
cio_ignore_proc_write (struct file *file, const char *user_buf,
size_t user_len, loff_t * offset)
{
char *buffer;
if(user_len > 65536)
user_len = 65536;
buffer = vmalloc (user_len + 1);
if (buffer == NULL)
return -ENOMEM;
if (copy_from_user (buffer, user_buf, user_len)) {
vfree (buffer);
return -EFAULT;
}
buffer[user_len] = '\0';
#ifdef CONFIG_DEBUG_IO
printk (KERN_DEBUG "/proc/cio_ignore: '%s'\n", buffer);
#endif /* CONFIG_DEBUG_IO */
blacklist_parse_proc_parameters (buffer);
vfree (buffer);
return user_len;
}
static struct file_operations cio_ignore_proc_file_ops = {
read:cio_ignore_proc_read, open:cio_ignore_proc_open,
write:cio_ignore_proc_write, release:cio_ignore_proc_close,
};
static int
cio_ignore_proc_init (void)
{
cio_ignore_proc_entry =
create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
&proc_root);
cio_ignore_proc_entry->proc_fops = &cio_ignore_proc_file_ops;
return 1;
}
__initcall (cio_ignore_proc_init);
/*
* Entry /proc/irq_count
* display how many irqs have occured per cpu...
*/
static struct proc_dir_entry *cio_irq_proc_entry;
static int
cio_irq_proc_open (struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
tempinfo_t *info;
int i;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
if (info == NULL) {
printk (KERN_WARNING "No memory available for data\n");
rc = -ENOMEM;
} else {
file->private_data = (void *) info;
size += NR_CPUS * 16;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
printk (KERN_WARNING "No memory available for data\n");
vfree (info);
rc = -ENOMEM;
} else {
for (i = 0; i < NR_CPUS; i++) {
if (s390_irq_count[i] != 0)
len +=
sprintf (info->data + len, "%lx\n",
s390_irq_count[i]);
}
info->len = len;
}
}
return rc;
}
static int
cio_irq_proc_close (struct inode *inode, struct file *file)
{
int rc = 0;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (p_info) {
if (p_info->data)
vfree (p_info->data);
vfree (p_info);
}
return rc;
}
static ssize_t
cio_irq_proc_read (struct file *file, char *user_buf, size_t user_len,
loff_t * offset)
{
loff_t len;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (*offset >= p_info->len) {
return 0;
} else {
len = MIN (user_len, (p_info->len - *offset));
if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
return -EFAULT;
(*offset) += len;
return len;
}
}
static struct file_operations cio_irq_proc_file_ops = {
read:cio_irq_proc_read, open:cio_irq_proc_open,
release:cio_irq_proc_close,
};
static int
cio_irq_proc_init (void)
{
int i;
if (cio_count_irqs) {
for (i = 0; i < NR_CPUS; i++)
s390_irq_count[i] = 0;
cio_irq_proc_entry =
create_proc_entry ("irq_count", S_IFREG | S_IRUGO,
&proc_root);
cio_irq_proc_entry->proc_fops = &cio_irq_proc_file_ops;
}
return 1;
}
__initcall (cio_irq_proc_init);
#ifdef CONFIG_CHSC
/*
* /proc/chpids to display available chpids
* vary chpids on/off by piping to it
*/
static struct proc_dir_entry *cio_chpids_proc_entry;
static int
cio_chpids_proc_open(struct inode *inode, struct file *file)
{
int rc = 0;
int size = 1;
int len = 0;
tempinfo_t *info;
int i;
if (!cio_chsc_desc_avail) {
/*
* We have not yet retrieved the link addresses,
* so we do it now.
*/
chsc_get_sch_descriptions();
}
info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
if (info == NULL) {
printk( KERN_WARNING "No memory available for data\n");
rc = -ENOMEM;
} else {
file->private_data = (void *) info;
size += NR_CHPIDS * 16;
info->data = (char *) vmalloc(size);
if ( size && info->data == NULL) {
printk( KERN_WARNING "No memory available for data\n");
vfree (info);
rc = -ENOMEM;
} else {
/* update our stuff */
chsc_get_sch_descriptions();
if (!cio_chsc_desc_avail) {
len += sprintf(info->data+len, "no info available\n");
goto cont;
}
for (i=0;i<NR_CHPIDS;i++) {
if (test_bit(i, &chpids_known)) {
if (!test_bit(i, &chpids))
len += sprintf(info->data+len,
"%02X n/a\n",
i);
else if (test_bit(i, &chpids_logical))
len += sprintf(info->data+len,
"%02X online\n",
i);
else
len += sprintf(info->data+len,
"%02X logically "
"offline\n",
i);
}
}
cont:
info->len = len;
}
}
return rc;
}
static int
cio_chpids_proc_close(struct inode *inode, struct file *file)
{
int rc = 0;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (p_info) {
if (p_info->data)
vfree( p_info->data );
vfree( p_info );
}
return rc;
}
static ssize_t
cio_chpids_proc_read( struct file *file, char *user_buf, size_t user_len, loff_t * offset)
{
loff_t len;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if ( *offset>=p_info->len) {
return 0;
} else {
len = MIN(user_len, (p_info->len - *offset));
if (copy_to_user( user_buf, &(p_info->data[*offset]), len))
return -EFAULT;
(* offset) += len;
return len;
}
}
static ssize_t
cio_chpids_proc_write (struct file *file, const char *user_buf,
size_t user_len, loff_t * offset)
{
char *buffer;
if(user_len > 65536)
user_len = 65536;
buffer = vmalloc (user_len + 1);
if (buffer == NULL)
return -ENOMEM;
if (copy_from_user (buffer, user_buf, user_len)) {
vfree (buffer);
return -EFAULT;
}
buffer[user_len]='\0';
#ifdef CIO_DEBUG_IO
printk("/proc/chpids: '%s'\n", buffer);
#endif /* CIO_DEBUG_IO */
cio_parse_chpids_proc_parameters(buffer);
vfree (buffer);
return user_len;
}
static struct file_operations cio_chpids_proc_file_ops =
{
read:cio_chpids_proc_read,
open:cio_chpids_proc_open,
write:cio_chpids_proc_write,
release:cio_chpids_proc_close,
};
static int
cio_chpids_proc_init(void)
{
cio_chpids_proc_entry = create_proc_entry("chpids", S_IFREG|S_IRUGO|S_IWUSR, &proc_root);
cio_chpids_proc_entry->proc_fops = &cio_chpids_proc_file_ops;
return 1;
}
__initcall(cio_chpids_proc_init);
#endif
/* end of procfs stuff */
#endif
schib_t *
s390_get_schib (int irq)
{
if ((irq > highest_subchannel) || (irq < 0))
return NULL;
if (ioinfo[irq] == INVALID_STORAGE_AREA)
return NULL;
if (ioinfo[irq]->st)
return NULL;
return &ioinfo[irq]->schib;
}
int
s390_set_private_data(int irq, void *data)
{
SANITY_CHECK(irq);
ioinfo[irq]->private_data = data;
return 0;
}
void *
s390_get_private_data(int irq)
{
if ((irq > highest_subchannel) || (irq < 0))
return NULL;
if (ioinfo[irq] == INVALID_STORAGE_AREA)
return NULL;
if (ioinfo[irq]->st)
return NULL;
return ioinfo[irq]->private_data;
}
EXPORT_SYMBOL (halt_IO);
EXPORT_SYMBOL (clear_IO);
EXPORT_SYMBOL (do_IO);
EXPORT_SYMBOL (resume_IO);
EXPORT_SYMBOL (ioinfo);
EXPORT_SYMBOL (diag210);
EXPORT_SYMBOL (get_dev_info_by_irq);
EXPORT_SYMBOL (get_dev_info_by_devno);
EXPORT_SYMBOL (get_irq_by_devno);
EXPORT_SYMBOL (get_devno_by_irq);
EXPORT_SYMBOL (get_irq_first);
EXPORT_SYMBOL (get_irq_next);
EXPORT_SYMBOL (read_conf_data);
EXPORT_SYMBOL (read_dev_chars);
EXPORT_SYMBOL (s390_request_irq_special);
EXPORT_SYMBOL (s390_get_schib);
EXPORT_SYMBOL (s390_register_adapter_interrupt);
EXPORT_SYMBOL (s390_unregister_adapter_interrupt);
EXPORT_SYMBOL (s390_set_private_data);
EXPORT_SYMBOL (s390_get_private_data);
EXPORT_SYMBOL (s390_trigger_resense);