s390io.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:198k
- /*
- * drivers/s390/s390io.c
- * S/390 common I/O routines
- * $Revision: 1.171.2.21 $
- *
- * 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>
- #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)
- #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);
- static int s390_send_nop(int irq, __u8 lpm);
- #ifdef CONFIG_PROC_FS
- static int chan_proc_init (void);
- #endif
- static inline void do_adapter_IO (__u32 intparm);
- int s390_DevicePathVerification (int irq, __u8 domask);
- int s390_register_adapter_interrupt (adapter_int_handler_t handler);
- int s390_unregister_adapter_interrupt (adapter_int_handler_t handler);
- extern int do_none (unsigned int irq, int cpu, struct pt_regs *regs);
- extern int enable_none (unsigned int irq);
- extern int disable_none (unsigned int irq);
- asmlinkage void do_IRQ (struct pt_regs regs);
- #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)) {
- printk (KERN_WARNING
- "Invalid blacklist range %x to %x, skippingn", from,
- to);
- return;
- }
- if (!locked)
- spin_lock_irqsave (&blacklist_lock, flags);
- if (!to)
- to = from;
- for (i = from; i <= to; i++) {
- 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<0) || (to > 0xffff)
- || (from<0) || (from > 0xffff)) {
- printk (KERN_WARNING
- "Invalid blacklist range %x to %x, "
- "not freeingn",
- from, to);
- return;
- }
- spin_lock_irqsave (&blacklist_lock, flags);
- for (i = from; i <= to; i++) {
- 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 = ' ';
- end++;
- }
- blacklist[count] = alloc_bootmem (len * sizeof (char));
- if (blacklist == NULL) {
- printk (KERN_WARNING
- "can't store cio_ignore= parameter no %dn",
- count + 1);
- break;
- }
- memset (blacklist[count], 0, len * sizeof (char));
- memcpy (blacklist[count], tmp, len * sizeof (char));
- count++;
- tmp = end;
- } while (tmp != NULL && *tmp != ' ');
- }
- /*
- * The blacklist parameters as one concatenated string
- */
- static char blacklist_parm_string[1024] __initdata = { 0, };
- /*
- * function: blacklist_strtoul
- * Strip leading '0x' and interpret the values as Hex
- */
- static inline int
- blacklist_strtoul (char *str, char **stra)
- {
- char *temp = str;
- int val;
- if (*temp == '0') {
- temp++; /* strip leading zero */
- if (*temp == 'x')
- temp++; /* strip leading x */
- }
- val = simple_strtoul (temp, &temp, 16); /* interpret anything as hex */
- *stra = temp;
- return val;
- }
- /*
- * Function: blacklist_parse
- * Parse the parameters given to 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 %Xn", 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 blacklistn");
- 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 parametersn");
- 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)
- {
- char *tmp;
- int i;
- char *end;
- int len = -1;
- char *param;
- int from = 0;
- int to = 0;
- long flags;
- int err = 0;
- tmp = buf;
- if (strstr (tmp, "free ")) {
- for (i = 0; i < 5; i++) {
- tmp++;
- }
- if (strstr (tmp, "all")) {
- blacklist_free_all_ranges ();
- s390_redo_validation ();
- } else {
- while (tmp != NULL) {
- end = strchr (tmp, ',');
- if (end == NULL) {
- len = strlen (tmp) + 1;
- } else {
- len = (long) end - (long) tmp + 1;
- *end = ' ';
- end++;
- }
- param =
- (char *) kmalloc (len * sizeof (char) + 1,
- GFP_KERNEL);
- strncpy (param, (const char *) tmp, len);
- tmp = end;
- from = blacklist_strtoul (param, ¶m);
- if (*param == '-') {
- param++;
- to = blacklist_strtoul (param, ¶m);
- } else {
- to = from;
- }
- blacklist_range_remove (from, to);
- kfree (param);
- }
- s390_redo_validation ();
- }
- } else if (strstr (tmp, "add ")) {
- for (i = 0; i < 4; i++) {
- tmp++;
- }
- while (tmp != NULL) {
- end = strchr (tmp, ',');
- if (end == NULL) {
- len = strlen (tmp) + 1;
- } else {
- len = (long) end - (long) tmp + 1;
- *end = ' ';
- end++;
- }
- param =
- (char *) kmalloc (len * sizeof (char) + 1,
- GFP_KERNEL);
- strncpy (param, (const char *) tmp, len);
- tmp = end;
- from = blacklist_strtoul (param, ¶m);
- if (*param == '-') {
- param++;
- to = blacklist_strtoul (param, ¶m);
- } else {
- to = from;
- }
- spin_lock_irqsave (&blacklist_lock, flags);
- /*
- * Don't allow for already known devices to be
- * blacklisted
- * The criterion is a bit dumb, devices which once were
- * there but are already gone are also caught...
- */
- err = 0;
- for (i = 0; i <= highest_subchannel; i++) {
- if (ioinfo[i] != INVALID_STORAGE_AREA) {
- if (!ioinfo[i]->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 %xn",
- from, to);
- err = 1;
- break;
- }
- }
- }
- if (!err)
- blacklist_range_add (from, to, 1);
- spin_unlock_irqrestore (&blacklist_lock, flags);
- kfree (param);
- }
- } 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_displayhex2 (char *str, void *ptr, s32 cnt, int level);
- void
- s390_displayhex (char *str, void *ptr, s32 cnt)
- {
- s32 cnt1, cnt2, maxcnt2;
- u32 *currptr = (__u32 *) ptr;
- printk ("n%sn", str);
- for (cnt1 = 0; cnt1 < cnt; cnt1 += 16) {
- printk ("%08lX ", (unsigned long) currptr);
- maxcnt2 = cnt - cnt1;
- if (maxcnt2 > 16)
- maxcnt2 = 16;
- for (cnt2 = 0; cnt2 < maxcnt2; cnt2 += 4)
- printk ("%08X ", *currptr++);
- printk ("n");
- }
- }
- void
- s390_displayhex2 (char *str, void *ptr, s32 cnt, int level)
- {
- s32 cnt1, cnt2, maxcnt2;
- u32 *currptr = (__u32 *) ptr;
- char buffer[cnt * 12];
- debug_text_event (cio_debug_msg_id, level, str);
- for (cnt1 = 0; cnt1 < cnt; cnt1 += 16) {
- sprintf (buffer, "%08lX ", (unsigned long) currptr);
- maxcnt2 = cnt - cnt1;
- if (maxcnt2 > 16)
- maxcnt2 = 16;
- for (cnt2 = 0; cnt2 < maxcnt2; cnt2 += 4)
- sprintf (buffer, "%08X ", *currptr++);
- }
- debug_text_event (cio_debug_msg_id, level, buffer);
- }
- static int __init
- cio_setup (char *parm)
- {
- if (!strcmp (parm, "yes")) {
- cio_show_msg = 1;
- } else if (!strcmp (parm, "no")) {
- cio_show_msg = 0;
- } else {
- printk (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;
- }
- /*
- * Function: s390_send_nop
- *
- * sends a nop CCW to the specified subchannel down the given path(s)
- */
- static int
- s390_send_nop(int irq, __u8 lpm)
- {
- char dbf_txt[15];
- ccw1_t *nop_ccw;
- devstat_t devstat;
- devstat_t *pdevstat = &devstat;
- unsigned long flags;
- int irq_ret = 0;
- int inlreq = 0;
- SANITY_CHECK(irq);
- if (!ioinfo[irq]->ui.flags.oper)
- /* no sense in trying */
- return -ENODEV;
- sprintf(dbf_txt, "snop%x", irq);
- CIO_TRACE_EVENT(5, dbf_txt);
- if (!ioinfo[irq]->ui.flags.ready) {
- /*
- * If there's no handler, use our dummy handler.
- */
- irq_ret = request_irq (irq,
- init_IRQ_handler,
- SA_PROBE,
- "SNOP",
- pdevstat);
- if (!irq_ret)
- 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)
- nop_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA);
- else
- nop_ccw = alloc_bootmem_low (sizeof (ccw1_t));
- nop_ccw->cmd_code = CCW_CMD_NOOP;
- nop_ccw->cda = 0;
- nop_ccw->count = 0;
- nop_ccw->flags = CCW_FLAG_SLI;
- memset (pdevstat, ' ', sizeof (devstat_t));
-
- irq_ret = s390_start_IO (irq, nop_ccw, 0xE2D5D6D7, lpm,
- DOIO_WAIT_FOR_INTERRUPT
- | DOIO_TIMEOUT
- | DOIO_DONT_CALL_INTHDLR
- | DOIO_VALID_LPM);
-
- if (irq_ret == -ETIMEDOUT) {
- /* better cancel... */
- cancel_IO(irq);
- }
- if (init_IRQ_complete)
- kfree (nop_ccw);
- else
- free_bootmem ((unsigned long) nop_ccw, sizeof (ccw1_t));
- s390irq_spin_unlock_irqrestore (irq, flags);
- if (inlreq)
- free_irq (irq, pdevstat);
- return irq_ret;
- }
- /*
- * 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, "reqs%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.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_PROBE) &&
- (!ioinfo[irq]->ui.flags.unfriendly))
- s390_DevicePathVerification (irq, 0);
- 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%dn", irq);
- #endif
- CIO_MSG_EVENT(2, "Trying to free IRQ %dn", 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 exceededn", irq,
- ioinfo[irq]->devstat.
- devno);
- CIO_MSG_EVENT( 0,
- "free_irq(%04X) - "
- "device %04X busy, "
- "retry count exceededn",
- 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);
- }
- }
- /*
- * Generic enable/disable code
- */
- int
- disable_irq (unsigned int irq)
- {
- unsigned long flags;
- int ret;
- char dbf_txt[15];
- SANITY_CHECK (irq);
- if (!ioinfo[irq]->ui.flags.ready)
- return -ENODEV;
- sprintf (dbf_txt, "dirq%x", irq);
- CIO_TRACE_EVENT (4, dbf_txt);
- s390irq_spin_lock_irqsave (irq, flags);
- ret = disable_subchannel (irq);
- s390irq_spin_unlock_irqrestore (irq, flags);
- synchronize_irq ();
- sprintf (dbf_txt, "ret:%d", ret);
- CIO_TRACE_EVENT (4, dbf_txt);
- return (ret);
- }
- int
- enable_irq (unsigned int irq)
- {
- unsigned long flags;
- int ret;
- char dbf_txt[15];
- SANITY_CHECK (irq);
- if (!ioinfo[irq]->ui.flags.ready)
- return -ENODEV;
- sprintf (dbf_txt, "eirq%x", irq);
- CIO_TRACE_EVENT (4, dbf_txt);
- s390irq_spin_lock_irqsave (irq, flags);
- ret = enable_subchannel (irq);
- s390irq_spin_unlock_irqrestore (irq, flags);
- sprintf (dbf_txt, "ret:%d", ret);
- CIO_TRACE_EVENT (4, dbf_txt);
- return (ret);
- }
- /*
- * Enable IRQ by modifying the subchannel
- */
- static int
- enable_subchannel (unsigned int irq)
- {
- int ret = 0;
- int ccode;
- int retry = 5;
- char dbf_txt[15];
- SANITY_CHECK (irq);
- sprintf (dbf_txt, "esch%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, "dsch%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)));
- 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));
- global_pgid->cpu_addr = *(__u16 *) __LC_CPUADDR;
- global_pgid->cpu_id = ((cpuid_t *) __LC_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 buffer[80];
- char dbf_txt[15];
- SANITY_CHECK (irq);
- /*
- * The flag usage is mutal exclusive ...
- */
- if ((flag & DOIO_EARLY_NOTIFICATION)
- && (flag & DOIO_REPORT_ALL)) {
- return (-EINVAL);
- }
- 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
- */
- 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磘 clear saved irb info in case of sense !
- */
- memset (&((devstat_t *) ioinfo[irq]->irq_desc.dev_id)->
- ii.irb, ' ', sizeof (irb_t));
- }
- memset (&ioinfo[irq]->devstat.ii.irb, ' ', 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;
- }
- ioinfo[irq]->ulpm = ioinfo[irq]->orb.lpm;
- /*
- * If synchronous I/O processing is requested, we have
- * to wait for the corresponding interrupt to occur by
- * polling the interrupt condition. However, as multiple
- * interrupts may be outstanding, we must not just wait
- * for the first interrupt, but must poll until ours
- * pops up.
- */
- if (flag & DOIO_WAIT_FOR_INTERRUPT) {
- 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 %dn", 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 */
- 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,
- ' ', sizeof (irb_t));
- /*
- * Let the common interrupt handler process the pending status.
- * However, we must avoid calling the user action handler, as
- * it won't be prepared to handle a pending status during
- * do_IO() processing inline. This also implies that process_IRQ
- * must terminate synchronously - especially if device sensing
- * is required.
- */
- ioinfo[irq]->ui.flags.s_pend = 1;
- ioinfo[irq]->ui.flags.busy = 1;
- ioinfo[irq]->ui.flags.doio = 1;
- s390_process_IRQ (irq);
- ioinfo[irq]->ui.flags.s_pend = 0;
- ioinfo[irq]->ui.flags.busy = 0;
- ioinfo[irq]->ui.flags.doio = 0;
- ioinfo[irq]->ui.flags.repall = 0;
- ioinfo[irq]->ui.flags.w4final = 0;
- ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS;
- /*
- * In multipath mode a condition code 3 implies the last path
- * has gone, except we have previously restricted the I/O to
- * a particular path. A condition code 1 (0 won't occur)
- * results in return code EIO as well as 3 with another path
- * than the one used (i.e. path available mask is non-zero).
- */
- if (ioinfo[irq]->devstat.ii.irb.scsw.cc == 3) {
- if (flag & DOIO_VALID_LPM) {
- ioinfo[irq]->opm &=
- ~(ioinfo[irq]->devstat.ii.irb.esw.esw1.
- lpum);
- } else {
- ioinfo[irq]->opm = 0;
- }
- 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
- {
- stsch (irq, &(ioinfo[irq]->schib));
- sprintf (buffer,
- "s390_start_IO(%04X) - irb for "
- "device %04X, after status pendingn",
- 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 pendingn",
- 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 pendingn",
- 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 (buffer,
- "s390_start_IO(%04X) - irb for "
- "device %04X, after status pendingn",
- irq, ioinfo[irq]->devstat.devno);
- s390_displayhex2 (buffer,
- &(ioinfo[irq]->devstat.ii.
- irb), sizeof (irb_t), 2);
- sprintf (buffer,
- "s390_start_IO(%04X) - schib for "
- "device %04X, after status pendingn",
- irq, ioinfo[irq]->devstat.devno);
- s390_displayhex2 (buffer,
- &(ioinfo[irq]->schib),
- sizeof (schib_t), 2);
- if (ioinfo[irq]->devstat.
- flag & DEVSTAT_FLAG_SENSE_AVAIL) {
- sprintf (buffer,
- "s390_start_IO(%04X) "
- "- sense data for device %04X,"
- " after status pendingn",
- irq,
- ioinfo[irq]->devstat.devno);
- s390_displayhex2 (buffer,
- ioinfo[irq]->irq_desc.
- dev_id->ii.sense.data,
- ioinfo[irq]->irq_desc.
- dev_id->rescnt, 2);
- }
- }
- } 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' statusn",
- irq, ioinfo[irq]->devstat.devno);
- s390_displayhex (buffer,
- &(ioinfo[irq]->schib), sizeof (schib_t));
- #endif
- if (cio_debug_initialized) {
- stsch (irq, &(ioinfo[irq]->schib));
- sprintf (buffer, "s390_start_IO(%04X) - schib for "
- "device %04X, after 'not oper' statusn",
- irq, ioinfo[irq]->devstat.devno);
- s390_displayhex2 (buffer,
- &(ioinfo[irq]->schib),
- sizeof (schib_t), 2);
- }
- 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);
- /*
- * 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);
- if ((ret == -ETIMEDOUT) && (flag & DOIO_CANCEL_ON_TIMEOUT)) {
- /*
- * We should better cancel the io request here
- * or we might not be able to do io on this sch
- * again
- */
- cancel_IO (irq);
- }
- } else if (ioinfo[irq]->ui.flags.fast) {
- /*
- * If primary status was received and ending status is missing,
- * the device driver won't be notified on the ending status
- * if early (fast) interrupt notification was requested.
- * Therefore we have to queue the next incoming request. If
- * halt_IO() is issued while there is a request queued, a HSCH
- * needs to be issued and the queued request must be deleted
- * but its intparm must be returned (see halt_IO() processing)
- */
- if (ioinfo[irq]->ui.flags.w4final
- && !ioinfo[irq]->ui.flags.doio_q) {
- ioinfo[irq]->qflag = flag;
- ioinfo[irq]->qcpa = cpa;
- ioinfo[irq]->qintparm = user_intparm;
- ioinfo[irq]->qlpm = lpm;
- } else {
- ret = -EBUSY;
- }
- } 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, "rsIO%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:
- s390_process_IRQ (irq);
- ret = -EBUSY;
- break;
- case 2:
- ret = -EINVAL;
- break;
- case 3:
- /*
- * useless to wait for request completion
- * as device is no longer operational !
- */
- ioinfo[irq]->ui.flags.oper = 0;
- ioinfo[irq]->ui.flags.busy = 0;
- ret = -ENODEV;
- break;
- }
- } 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);
- /*
- * 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;
- }
- CIO_TRACE_EVENT (2, "haltIO");
- sprintf (dbf_txt, "%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 %dn", 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 */
- ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING;
- /*
- * initialize the device driver specific devstat irb area
- */
- memset (&ioinfo[irq]->irq_desc.dev_id->ii.irb,
- ' ', 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);
- /*
- * 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;
- CIO_TRACE_EVENT (2, "clearIO");
- sprintf (dbf_txt, "%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 %dn", 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);
- CIO_TRACE_EVENT (2, "cancelIO");
- sprintf (dbf_txt, "%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 */
- /* process the pending irq... */
- s390_process_IRQ (irq);
- 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 pendingn",
- 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 "
- "receivedn"
- " ... device %04X on subchannel %04X, dev_stat "
- ": %02X sch_stat : %02Xn",
- ioinfo[irq]->devstat.devno, irq, dp->dstat,
- dp->cstat);
- CIO_MSG_EVENT(0,
- "Channel-Check or "
- "Interface-Control-Check receivedn");
- CIO_MSG_EVENT(0,
- "... device %04X on subchannel %04X,"
- " dev_stat: %02X sch_stat: %02Xn",
- 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 %dn",
- irq, dp->scnt);
- #endif
- CIO_MSG_EVENT(4,
- "s390_process_IRQ( %04X ): "
- "concurrent sense bytes avail %dn",
- 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 checkn",
- irq, dp->devno);
- s390_displayhex (buffer, &(dp->ii.irb), sizeof (irb_t));
- if (cio_debug_initialized) {
- sprintf (buffer,
- "s390_process_IRQ(%04X) - irb for "
- "device %04X after channel check "
- "or interface control checkn",
- irq, dp->devno);
- s390_displayhex2 (buffer,
- &(dp->ii.irb),
- sizeof (irb_t), 0);
- }
- }
- 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 %04Xn"
- " ... device status : %02X "
- "subchannel status : %02Xn",
- dp->devno, irq, dp->dstat, dp->cstat);
- sprintf (buffer, "s390_process_IRQ(%04X) - irb for "
- "device %04X, ending_status %dn",
- 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 %04Xn"
- " ... device status : %02X "
- "subchannel status : %02Xn",
- dp->devno,
- irq, dp->dstat, dp->cstat);
- if (cio_debug_initialized) {
- sprintf (buffer,
- "s390_process_IRQ(%04X) - irb for "
- "device %04X, ending_status %dn", irq,
- dp->devno, ending_status);
-
- s390_displayhex2 (buffer,
- &(dp->ii.irb),
- sizeof (irb_t), 2);
- }
- }
- /*
- * take fast exit if no handler is available
- */
- if (!ioinfo[irq]->ui.flags.ready)
- return (ending_status);
- /*
- * Check whether we must issue a SENSE CCW ourselves if there is no
- * concurrent sense facility installed for the subchannel.
- *
- * Note: We should check for ioinfo[irq]->ui.flags.consns but VM
- * violates the ESA/390 architecture and doesn't present an
- * operand exception for virtual devices without concurrent
- * sense facility available/supported when enabling the
- * concurrent sense facility.
- */
- if (((dp->ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK)
- && (!issense))
- || (ioinfo[irq]->ui.flags.delsense && ending_status)) {
- int ret_io;
- ccw1_t *s_ccw = &ioinfo[irq]->senseccw;
- unsigned long s_flag = 0;
- if (ending_status) {
- /*
- * We copy the current status information into the device driver
- * status area. Then we can use the local devstat area for device
- * sensing. When finally calling the IRQ handler we must not overlay
- * the original device status but copy the sense data only.
- */
- memcpy (udp, dp, sizeof (devstat_t));
- s_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
- s_ccw->cda =
- (__u32) virt_to_phys (ioinfo[irq]->
- sense_data);
- s_ccw->count = SENSE_MAX_COUNT;
- s_ccw->flags = CCW_FLAG_SLI;
- /*
- * If free_irq() or a sync do_IO/s390_start_IO() is in
- * process we have to sense synchronously
- */
- if (ioinfo[irq]->ui.flags.unready
- || ioinfo[irq]->ui.flags.syncio) {
- s_flag = DOIO_WAIT_FOR_INTERRUPT;
- }
- /*
- * Reset status info
- *
- * It does not matter whether this is a sync. or async.
- * SENSE request, but we have to assure we don't call
- * the irq handler now, but keep the irq in busy state.
- * In sync. mode s390_process_IRQ() is called recursively,
- * while in async. mode we re-enter do_IRQ() with the
- * next interrupt.
- *
- * Note : this may be a delayed sense request !
- */
- allow4handler = 0;
- ioinfo[irq]->ui.flags.fast = 0;
- ioinfo[irq]->ui.flags.repall = 0;
- ioinfo[irq]->ui.flags.w4final = 0;
- ioinfo[irq]->ui.flags.delsense = 0;
- dp->cstat = 0;
- dp->dstat = 0;
- dp->rescnt = SENSE_MAX_COUNT;
- ioinfo[irq]->ui.flags.w4sense = 1;
- ret_io = s390_start_IO (irq, s_ccw, 0xE2C5D5E2, /* = SENSe */
- 0, /* n/a */
- s_flag);
- } else {
- /*
- * we received an Unit Check but we have no final
- * status yet, therefore we must delay the SENSE
- * processing. However, we must not report this
- * intermediate status to the device interrupt
- * handler.
- */
- ioinfo[irq]->ui.flags.fast = 0;
- ioinfo[irq]->ui.flags.repall = 0;
- ioinfo[irq]->ui.flags.delsense = 1;
- allow4handler = 0;
- }
- }
- /*
- * 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 %dn",
- irq, sense_count);
- #endif
- CIO_MSG_EVENT(4,
- "s390_process_IRQ( %04X ): "
- "BASIC SENSE bytes avail %dn",
- irq, sense_count);
- ioinfo[irq]->ui.flags.w4sense = 0;
- udp->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
- udp->scnt = sense_count;
- if (sense_count >= 0) {
- memcpy (udp->ii.sense.data,
- ioinfo[irq]->sense_data,
- sense_count);
- } else {
- panic
- ("s390_process_IRQ(%04x) encountered "
- "negative sense countn", 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;
- ioinfo[irq]->irq_desc.handler (irq, udp, NULL);
- /*
- * reset intparm after final status or we will badly present unsolicited
- * interrupts with a intparm value possibly no longer valid.
- */
- dp->intparm = 0;
- /*
- * Was there anything queued ? Start the pending channel program
- * if there is one.
- */
- if (ioinfo[irq]->ui.flags.doio_q) {
- int ret;
- int do_cancel =
- ioinfo[irq]->
- qflag & DOIO_CANCEL_ON_TIMEOUT;
- ret = s390_start_IO (irq,
- ioinfo[irq]->qcpa,
- ioinfo[irq]->
- qintparm,
- ioinfo[irq]->qlpm,
- ioinfo[irq]->
- qflag);
- ioinfo[irq]->ui.flags.doio_q = 0;
- /*
- * If s390_start_IO() failed call the device's interrupt
- * handler, the IRQ related devstat area was setup by
- * s390_start_IO() accordingly already (status pending
- * condition).
- */
- if (ret) {
- ioinfo[irq]->irq_desc.
- handler (irq, udp, NULL);
- }
- /*
- * better cancel the io when we time out...
- */
- if ((ret == -ETIMEDOUT) && do_cancel) {
- cancel_IO (irq);
- }
- }
- } 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 (ioinfo[irq]->ulpm != ioinfo[irq]->opm) {
- /*
- * either it was the only path or it was restricted ...
- */
- ioinfo[irq]->opm &=
- ~(ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum);
- } else {
- ioinfo[irq]->opm = 0;
- }
- 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.
- */
- if ((ioinfo[irq]->ui.flags.fast)
- && (ioinfo[irq]->ui.flags.w4final)) {
- /*
- * If a new request was queued already, we have
- * to simulate the "not oper" status for the
- * queued request by switching the "intparm" value
- * and notify the interrupt handler.
- */
- if (ioinfo[irq]->ui.flags.doio_q) {
- ioinfo[irq]->devstat.intparm =
- ioinfo[irq]->qintparm;
- }
- }
- 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]->irq_desc.handler (irq, udp, NULL);
- }
- ending_status = 1;
- break;
- }
- return (ending_status);
- }
- /*
- * Set the special i/o-interruption sublass 7 for the
- * device specified by parameter irq. There can only
- * be a single device been operated on this special
- * isc. This function is aimed being able to check
- * on special device interrupts in disabled state,
- * without having to delay I/O processing (by queueing)
- * for non-console devices.
- *
- * Setting of this isc is done by set_cons_dev(), while
- * reset_cons_dev() resets this isc and re-enables the
- * default isc3 for this device. wait_cons_dev() allows
- * to actively wait on an interrupt for this device in
- * disabed state. When the interrupt condition is
- * encountered, wait_cons_dev(9 calls do_IRQ() to have
- * the console device driver processing the interrupt.
- */
- int
- set_cons_dev (int irq)
- {
- int ccode;
- int rc = 0;
- char dbf_txt[15];
- SANITY_CHECK (irq);
- if (cons_dev != -1)
- return -EBUSY;
- sprintf (dbf_txt, "scd%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
- reset_cons_dev (int irq)
- {
- int rc = 0;
- int ccode;
- char dbf_txt[15];
- SANITY_CHECK (irq);
- if (cons_dev != -1)
- return -EBUSY;
- sprintf (dbf_txt, "rscd%x", irq);
- CIO_TRACE_EVENT (4, dbf_txt);
- /*
- * reset the indicated console device to operate
- * on default console interrupt sublass 3
- */
- ccode = stsch (irq, &(ioinfo[irq]->schib));
- if (ccode) {
- rc = -ENODEV;
- ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER;
- } else {
- ioinfo[irq]->schib.pmcw.isc = 3;
- ccode = msch (irq, &(ioinfo[irq]->schib));
- if (ccode) {
- rc = -EIO;
- } else {
- cons_dev = -1;
- /*
- * disable special console I/O-interrupt sublass 7
- */
- ctl_clear_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, "wcd%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, "eisc%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) {
- ioinfo[irq]->ui.flags.syncio = 1;
- ccode = stsch (irq, &(ioinfo[irq]->schib));
- if (!ccode) {
- ioinfo[irq]->schib.pmcw.isc = 5;
- do {
- ccode = msch (irq,
- &(ioinfo[irq]->schib));
- switch (ccode) {
- case 0:
- /*
- * enable special isc
- */
- __ctl_store (cr6, 6, 6);
- /* enable sync isc 5 */
- cr6 |= 0x04000000;
- /* disable standard isc 3 */
- cr6 &= 0xEFFFFFFF;
- /* disable console isc 7,
- * if neccessary */
- if (cons_dev != -1)
- cr6 &= 0xFEFFFFFF;
- __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 {
- #ifdef CONFIG_SYNC_ISC_PARANOIA
- panic ("enable_sync_isc: called with invalid %xn", 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, "disc%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 lockedn",
- 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,
- * if neccessary */
- if (cons_dev != -1)
- 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 lockedn",
- irq, atomic_read (&sync_isc));
- #endif
- rc = -EINVAL;
- }
- return (rc);
- }
- /*
- * Input :
- * devno - device number
- * ps - pointer to sense ID data area
- * Output : none
- */
- void
- VM_virtual_device_info (__u16 devno, senseid_t * ps)
- {
- diag210_t *p_diag_data;
- int ccode;
- int error = 0;
- CIO_TRACE_EVENT (4, "VMvdinf");
- if (init_IRQ_complete) {
- p_diag_data = kmalloc (sizeof (diag210_t), GFP_DMA);
- } else {
- p_diag_data = alloc_bootmem_low (sizeof (diag210_t));
- }
- 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: %02Xn",
- 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: %02Xn",
- 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 int flags;
- ccw1_t *rdc_ccw;
- devstat_t devstat;
- char *rdc_buf;
- int devflag = 0;
- int ret = 0;
- int emulated = 0;
- int retry = 5;
- char dbf_txt[15];
- if (!buffer || !length) {
- return (-EINVAL);
- }
- 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, "rdc%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, (unsigned long)
- rdc_buf);
- if (!ret) {
- memset (ioinfo[irq]->irq_desc.dev_id,
- ' ', 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, "rcd%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);
- } else {
- rcd_buf =
- alloc_bootmem_low (ioinfo[irq]->senseid.
- ciw[ciw_cnt].count);
- }
- if (rcd_buf == NULL) {
- ret = -ENOMEM;
- }
- if (!ret) {
- memset (rcd_buf,
- ' ',
- 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, ' ',
- 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), ' ', 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), ' ', sizeof (senseid_t));
- pdi->sid_data.cu_type = 0xFFFF;
- } else {
- pdi->status = DEVSTAT_NOT_OPER;
- memset (&(pdi->sid_data), ' ', 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), ' ',
- 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),
- ' ', sizeof (senseid_t));
- pdi->sid_data.cu_type = 0xFFFF;
- } else {
- pdi->status = DEVSTAT_NOT_OPER;
- memset (&(pdi->sid_data),
- ' ', sizeof (senseid_t));
- pdi->sid_data.cu_type = 0xFFFF;
- }
- if (ioinfo[i]->ui.flags.ready)