/*
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
#include <asm/cio.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include "airq.h"
#include "cio.h"
#include "css.h"
#include "chsc.h"
#include "ioasm.h"
#include "blacklist.h"
#include "cio_debug.h"
debug_info_t *cio_debug_msg_id;
debug_info_t *cio_debug_trace_id;
debug_info_t *cio_debug_crw_id;
int cio_show_msg;
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);
/*
* Function: cio_debug_init
* Initializes three debug logs (under /proc/s390dbf) for common I/O:
* - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on
* - cio_trace logs the calling of different functions
* - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on
* debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW
*/
static int __init
cio_debug_init (void)
{
cio_debug_msg_id = debug_register ("cio_msg", 16, 4, 16*sizeof (long));
if (!cio_debug_msg_id)
goto out_unregister;
debug_register_view (cio_debug_msg_id, &debug_sprintf_view);
debug_set_level (cio_debug_msg_id, 2);
cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 16);
if (!cio_debug_trace_id)
goto out_unregister;
debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view);
debug_set_level (cio_debug_trace_id, 2);
cio_debug_crw_id = debug_register ("cio_crw", 4, 4, 16*sizeof (long));
if (!cio_debug_crw_id)
goto out_unregister;
debug_register_view (cio_debug_crw_id, &debug_sprintf_view);
debug_set_level (cio_debug_crw_id, 2);
pr_debug("debugging initialized\n");
return 0;
out_unregister:
if (cio_debug_msg_id)
debug_unregister (cio_debug_msg_id);
if (cio_debug_trace_id)
debug_unregister (cio_debug_trace_id);
if (cio_debug_crw_id)
debug_unregister (cio_debug_crw_id);
pr_debug("could not initialize debugging\n");
return -1;
}
arch_initcall (cio_debug_init);
int
cio_set_options (struct subchannel *sch, int flags)
{
sch->options.suspend = (flags & DOIO_ALLOW_SUSPEND) != 0;
sch->options.prefetch = (flags & DOIO_DENY_PREFETCH) != 0;
sch->options.inter = (flags & DOIO_SUPPRESS_INTER) != 0;
return 0;
}
/* FIXME: who wants to use this? */
int
cio_get_options (struct subchannel *sch)
{
int flags;
flags = 0;
if (sch->options.suspend)
flags |= DOIO_ALLOW_SUSPEND;
if (sch->options.prefetch)
flags |= DOIO_DENY_PREFETCH;
if (sch->options.inter)
flags |= DOIO_SUPPRESS_INTER;
return flags;
}
/*
* Use tpi to get a pending interrupt, call the interrupt handler and
* return a pointer to the subchannel structure.
*/
static inline int
cio_tpi(void)
{
struct tpi_info *tpi_info;
struct subchannel *sch;
struct irb *irb;
tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
if (tpi (NULL) != 1)
return 0;
irb = (struct irb *) __LC_IRB;
/* Store interrupt response block to lowcore. */
if (tsch (tpi_info->schid, irb) != 0)
/* Not status pending or not operational. */
return 1;
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
if (!sch)
return 1;
local_bh_disable();
irq_enter ();
spin_lock(&sch->lock);
memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
if (sch->driver && sch->driver->irq)
sch->driver->irq(&sch->dev);
spin_unlock(&sch->lock);
irq_exit ();
_local_bh_enable();
return 1;
}
static inline int
cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
{
char dbf_text[15];
if (lpm != 0)
sch->lpm &= ~lpm;
else
sch->lpm = 0;
stsch (sch->schid, &sch->schib);