Commit fe11051b authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: cio rework.

Rewrite of the s390 channel subsystem driver for the new driver model

The channel subsystem driver a.k.a s390 common I/O layer is the low level
driver for most device drivers on s390 systems. The old code is largely
unchanged from the initial linux-2.2 port and there is a lot of bitrot
on it.

In particular, concepts from the 2.5 driver model are implemented in a
completely different and more complicated way here.
This rewrite tries to get the driver ready for 2.6. The new interface is
not compatible to the old one but should be rather stable now unless
someone finds major flaws.

The 's390dyn' and 'chandev' interfaces have been removed entirely (yippii!)
and are replaced by hotplug and sysfs interfaces.

Authors: Arnd Bergmann <arndb@de.ibm.com>,
         Cornelia Huck <cohuck@de.ibm.com>,
         Martin Schwidefsky <schwidefsky@de.ibm.com>
parent d2a03c2f
......@@ -47,6 +47,7 @@
*/
unsigned int console_mode = 0;
unsigned int console_device = -1;
unsigned int console_irq = -1;
unsigned long memory_size = 0;
unsigned long machine_flags = 0;
struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } };
......@@ -187,6 +188,10 @@ static void __init conmode_default(void)
char *ptr;
if (MACHINE_IS_VM) {
cpcmd("QUERY CONSOLE", query_buffer, 1024);
console_device = simple_strtoul(query_buffer + 5, NULL, 16);
ptr = strstr(query_buffer, "SUBCHANNEL =");
console_irq = simple_strtoul(ptr + 13, NULL, 16);
cpcmd("QUERY TERM", query_buffer, 1024);
ptr = strstr(query_buffer, "CONMODE");
/*
......@@ -245,9 +250,13 @@ void (*_machine_power_off)(void) = machine_power_off_smp;
/*
* Reboot, halt and power_off routines for non SMP.
*/
extern void do_reipl(unsigned long devno);
static void do_machine_restart_nonsmp(char * __unused)
{
reipl(S390_lowcore.ipl_device);
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0);
else
do_reipl (0x10000 | S390_lowcore.ipl_device);
}
static void do_machine_halt_nonsmp(void)
......
......@@ -61,7 +61,7 @@ unsigned long cache_decay_ticks = 0;
extern char vmhalt_cmd[];
extern char vmpoff_cmd[];
extern void reipl(unsigned long devno);
extern void do_reipl(unsigned long devno);
static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
static void smp_ext_bitcall_others(ec_bit_sig);
......@@ -217,7 +217,10 @@ static void do_machine_restart(void * __unused)
* interrupted by an external interrupt and s390irq
* locks are always held disabled).
*/
reipl(S390_lowcore.ipl_device);
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0);
else
do_reipl (0x10000 | S390_lowcore.ipl_device);
}
signal_processor(smp_processor_id(), sigp_stop);
}
......
......@@ -47,6 +47,7 @@
*/
unsigned int console_mode = 0;
unsigned int console_device = -1;
unsigned int console_irq = -1;
unsigned long memory_size = 0;
unsigned long machine_flags = 0;
struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } };
......@@ -187,6 +188,10 @@ static void __init conmode_default(void)
char *ptr;
if (MACHINE_IS_VM) {
cpcmd("QUERY CONSOLE", query_buffer, 1024);
console_device = simple_strtoul(query_buffer + 5, NULL, 16);
ptr = strstr(query_buffer, "SUBCHANNEL =");
console_irq = simple_strtoul(ptr + 13, NULL, 16);
cpcmd("QUERY TERM", query_buffer, 1024);
ptr = strstr(query_buffer, "CONMODE");
/*
......@@ -245,9 +250,13 @@ void (*_machine_power_off)(void) = machine_power_off_smp;
/*
* Reboot, halt and power_off routines for non SMP.
*/
extern void do_reipl(unsigned long devno);
static void do_machine_restart_nonsmp(char * __unused)
{
reipl(S390_lowcore.ipl_device);
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0);
else
do_reipl (0x10000 | S390_lowcore.ipl_device);
}
static void do_machine_halt_nonsmp(void)
......
......@@ -60,7 +60,7 @@ unsigned long cache_decay_ticks = 0;
extern char vmhalt_cmd[];
extern char vmpoff_cmd[];
extern void reipl(unsigned long devno);
extern void do_reipl(unsigned long devno);
static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
static void smp_ext_bitcall_others(ec_bit_sig);
......@@ -216,7 +216,10 @@ static void do_machine_restart(void * __unused)
* interrupted by an external interrupt and s390irq
* locks are always held disabled).
*/
reipl(S390_lowcore.ipl_device);
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0);
else
do_reipl (0x10000 | S390_lowcore.ipl_device);
}
signal_processor(smp_processor_id(), sigp_stop);
}
......
......@@ -257,6 +257,11 @@ config TN3215_CONSOLE
Include support for using an IBM 3215 line-mode terminal as a
Linux system console.
config CCW_CONSOLE
bool
depends on TN3215_CONSOLE || TN3270_CONSOLE
default y
config SCLP
bool "Support for SCLP"
help
......
......@@ -2,11 +2,7 @@
# Makefile for the S/390 specific device drivers
#
export-objs := s390dyn.o qdio.o
obj-$(CONFIG_QDIO) += qdio.o
obj-y += s390mach.o s390dyn.o sysinfo.o
obj-y += s390mach.o sysinfo.o
obj-y += cio/ block/ char/ misc/ net/
drivers-y += drivers/s390/built-in.o
......
......@@ -2,11 +2,18 @@
# Makefile for the S/390 common i/o drivers
#
obj-y := cio_debug.o # make sure this always comes first
obj-y += airq.o blacklist.o cio.o ioinfo.o misc.o requestirq.o s390io.o chsc.o
obj-y += airq.o blacklist.o chsc.o cio.o css.o requestirq.o
export-objs += airq.o css.o chsc.o cio.o requestirq.o
obj-$(CONFIG_PROC_FS) += proc.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o
export-objs += device.o device_ops.o
export-objs += airq.o cio.o ioinfo.o requestirq.o s390io.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o
export-objs += ccwgroup.o
obj-$(CONFIG_QDIO) += qdio.o
export-objs += qdio.o
include $(TOPDIR)/Rules.make
/*
* drivers/s390/cio/airq.c
* S/390 common I/O routines -- special interrupt registration
* currently used only by qdio
* S/390 common I/O routines -- support for adapter interruptions
*
* $Revision: 1.3 $
* $Revision: 1.10 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
* ChangeLog: 11/04/2002 Arnd Bergmann Split s390io.c into multiple files,
* see s390io.c for complete list of
* changes.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/idals.h>
#include <asm/debug.h>
#include "airq.h"
#include "cio_debug.h"
#include "airq.h"
static spinlock_t adapter_lock = SPIN_LOCK_UNLOCKED;
static adapter_int_handler_t adapter_handler;
......@@ -41,7 +35,7 @@ static adapter_int_handler_t adapter_handler;
int
s390_register_adapter_interrupt (adapter_int_handler_t handler)
{
int ret = 0;
int ret;
char dbf_txt[15];
CIO_TRACE_EVENT (4, "rgaint");
......@@ -52,21 +46,23 @@ s390_register_adapter_interrupt (adapter_int_handler_t handler)
ret = -EINVAL;
else if (adapter_handler)
ret = -EBUSY;
else
else {
adapter_handler = handler;
ret = 0;
}
spin_unlock (&adapter_lock);
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (4, dbf_txt);
return ret;
return (ret);
}
int
s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
{
int ret = 0;
int ret;
char dbf_txt[15];
CIO_TRACE_EVENT (4, "urgaint");
......@@ -77,15 +73,17 @@ s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
ret = -EINVAL;
else if (handler != adapter_handler)
ret = -EINVAL;
else
else {
adapter_handler = NULL;
ret = 0;
}
spin_unlock (&adapter_lock);
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (4, dbf_txt);
return ret;
return (ret);
}
void
......
#ifndef S390_AINTERRUPT_H
#define S390_AINTERRUPT_H
typedef int (*adapter_int_handler_t)(__u32 intparm);
extern int s390_register_adapter_interrupt(adapter_int_handler_t handler);
extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler);
extern void do_adapter_IO (__u32 intparm);
#endif
/*
* drivers/s390/cio/blacklist.c
* S/390 common I/O routines -- blacklisting of specific devices
* $Revision: 1.7 $
* $Revision: 1.22 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
* ChangeLog: 11/04/2002 Arnd Bergmann Split s390io.c into multiple files,
* see s390io.c for complete list of
* changes.
* 15/04/2002 Arnd Bergmann check ranges of user input
* 18/04/2002 Arnd Bergmann remove bogus optimization and
* now unnecessary locking
* 19/04/2002 Arnd Bergmann cleanup parameter parsing
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <asm/s390dyn.h>
#include <asm/uaccess.h>
#include <asm/debug.h>
#include "blacklist.h"
#include "cio_debug.h"
#include "ioinfo.h"
#include "proc.h"
#include "s390io.h"
#include "css.h"
/*
/*
* "Blacklisting" of certain devices:
* Device numbers given in the commandline as cio_ignore=... won't be known
* to Linux.
......@@ -42,12 +32,13 @@
* These can be single devices or ranges of devices
*/
#define max_devno (0xffffUL)
static uint32_t bl_dev[(max_devno+1) / 32]; /* 65536 bits to indicate if a
devno is blacklisted or not */
/* 65536 bits to indicate if a devno is blacklisted or not */
#define __BL_DEV_WORDS (__MAX_SUBCHANNELS + (8*sizeof(long) - 1) / \
(8*sizeof(long)))
static unsigned long bl_dev[__BL_DEV_WORDS];
typedef enum {add, free} range_action;
/*
/*
* Function: blacklist_range
* (Un-)blacklist the devices from-to
*/
......@@ -57,47 +48,50 @@ blacklist_range (range_action action, unsigned int from, unsigned int to)
if (!to)
to = from;
if ((from > to) || (to > max_devno)) {
if (from > to || to > __MAX_SUBCHANNELS) {
printk (KERN_WARNING "Invalid blacklist range "
"0x%04x to 0x%04x, skipping\n", from, to);
return;
}
for (; from <= to; from++) {
(action == add) ? set_bit (from, &bl_dev)
: clear_bit (from, &bl_dev);
if (action == add)
set_bit (from, bl_dev);
else
clear_bit (from, bl_dev);
}
}
/*
/*
* function: blacklist_strtoul
* Strip leading '0x' and interpret the values as Hex
*/
static inline int
blacklist_strtoul (const char *str, char **stra)
{
if (*str == '0') {
if (*(++str) == 'x') /* strip leading zero */
str++; /* strip leading x */
}
return simple_strtoul (str, stra, 16); /* interpret anything as hex */
if (*str == '0') {
if (*(++str) == 'x') /* strip leading zero */
str++; /* strip leading x */
}
return simple_strtoul (str, stra, 16); /* interpret anything as hex */
}
static inline int
blacklist_parse_parameters (char *str, range_action action)
{
unsigned int from, to;
while (*str != 0 && *str != '\n') {
if (!isxdigit(*str)) {
printk(KERN_WARNING "blacklist_setup: error parsing "
"\"%s\"\n", str);
"\"%s\"\n", str);
return 0;
}
from = blacklist_strtoul (str, &str);
to = (*str == '-') ? blacklist_strtoul (str+1, &str) : from;
printk (KERN_INFO "blacklist_setup: adding range "
"from 0x%04x to 0x%04x\n", from, to);
pr_debug("blacklist_setup: adding range "
"from 0x%04x to 0x%04x\n", from, to);
blacklist_range (action, from, to);
if (*str == ',')
......@@ -128,13 +122,14 @@ __setup ("cio_ignore=", blacklist_setup);
/*
* Function: is_blacklisted
* Returns 1 if the given devicenumber can be found in the blacklist, otherwise 0.
* Used by s390_validate_subchannel()
* Returns 1 if the given devicenumber can be found in the blacklist,
* otherwise 0.
* Used by validate_subchannel()
*/
int
is_blacklisted (int devno)
{
return test_bit (devno, &bl_dev);
return test_bit (devno, bl_dev);
}
#ifdef CONFIG_PROC_FS
......@@ -150,23 +145,8 @@ s390_redo_validation (void)
CIO_TRACE_EVENT (0, "redoval");
for (irq=0; irq <= highest_subchannel; irq++) {
if (ioinfo[irq] != INVALID_STORAGE_AREA
|| s390_validate_subchannel (irq, 0))
continue;
/* this subchannel has just been unblacklisted,
* so now try to get it working */
s390_device_recognition_irq (irq);
if (ioinfo[irq]->ui.flags.oper) {
devreg_t *pdevreg;
pdevreg = s390_search_devreg (ioinfo[irq]);
if (pdevreg && pdevreg->oper_func != NULL)
pdevreg->oper_func (irq, pdevreg);
}
}
for (irq = 0; irq <= highest_subchannel; irq++)
css_probe_device(irq);
}
/*
......@@ -178,13 +158,13 @@ blacklist_parse_proc_parameters (char *buf)
{
if (strncmp (buf, "free ", 5) == 0) {
if (strstr (buf + 5, "all"))
blacklist_range (free, 0, max_devno);
blacklist_range (free, 0, __MAX_SUBCHANNELS);
else
blacklist_parse_parameters (buf + 5, free);
} else if (strncmp (buf, "add ", 4) == 0) {
/* FIXME: the old code was checking if the new bl'ed
* devices are already known to the system so
* s390_validate_subchannel would still give a working
* validate_subchannel would still give a working
* status. is that necessary? */
blacklist_parse_parameters (buf + 4, add);
} else {
......@@ -202,40 +182,40 @@ blacklist_parse_proc_parameters (char *buf)
static int cio_ignore_read (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
const unsigned int entry_size = 14; /* "0xABCD-0xEFGH\n" */
long devno = off; /* abuse the page variable
* as counter, see fs/proc/generic.c */
long devno;
int len;
while ((devno <= max_devno)
&& (len + entry_size < count)) {
if (test_bit (devno, &bl_dev)) {
len += sprintf(page + len, "0x%04lx", devno);
devno++;
if (test_bit (devno, &bl_dev)) { /* print range */
do { devno++; }
while (test_bit (devno, &bl_dev));
len += sprintf(page + len, "-0x%04lx", devno-1);
}
len += sprintf(page + len, "\n");
len = 0;
for (devno = off; /* abuse the page variable
* as counter, see fs/proc/generic.c */
devno <= __MAX_SUBCHANNELS && len + entry_size < count; devno++) {
if (!test_bit(devno, bl_dev))
continue;
len += sprintf(page + len, "0x%04lx", devno);
if (test_bit(devno + 1, bl_dev)) { /* print range */
while (++devno < __MAX_SUBCHANNELS)
if (!test_bit(devno, bl_dev))
break;
len += sprintf(page + len, "-0x%04lx", devno);
}
devno++;
len += sprintf(page + len, "\n");
}
if (devno <= max_devno)
if (devno <= __MAX_SUBCHANNELS)
*eof = 1;
*start = (char *) (devno - off); /* number of checked entries */
return len;
}
static int cio_ignore_write (struct file *file, const char *user_buf,
unsigned long user_len, void *data)
{
char *buf = vmalloc (user_len + 1); /* maybe better use the stack? */
char *buf;
if (user_len > 65536)
user_len = 65536;
buf = vmalloc (user_len + 1); /* maybe better use the stack? */
if (buf == NULL)
return -ENOMEM;
if (strncpy_from_user (buf, user_buf, user_len) < 0) {
......@@ -243,10 +223,7 @@ static int cio_ignore_write (struct file *file, const char *user_buf,
return -EFAULT;
}
buf[user_len] = '\0';
#if 0
CIO_DEBUG(KERN_DEBUG, 2,
"/proc/cio_ignore: '%s'\n", buf);
#endif
blacklist_parse_proc_parameters (buf);
vfree (buf);
......@@ -257,6 +234,7 @@ static int
cio_ignore_proc_init (void)
{
struct proc_dir_entry *entry;
entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
&proc_root);
if (!entry)
......
This diff is collapsed.
#ifndef S390_CHSC_H
#define S390_CHSC_H
#define NR_CHPIDS 256
#define CHP_OFFLINE 0
#define CHP_LOGICALLY_OFFLINE 1
#define CHP_STANDBY 2
#define CHP_ONLINE 3
#define CHSC_SEI_ACC_CHPID 1
#define CHSC_SEI_ACC_LINKADDR 2
#define CHSC_SEI_ACC_FULLLINKADDR 3
struct channel_path {
int id;
int state;
struct sys_device sdev;
};
extern struct channel_path *chps[];
extern void s390_process_css( void );
extern int chsc_chpid_logical (int irq, int chp);
extern void chsc_validate_chpids(int irq);
extern void switch_off_chpids(int irq, __u8 mask);
extern void chsc_validate_chpids(struct subchannel *);
#endif
This diff is collapsed.
#ifndef S390_CIO_H
#define S390_CIO_H
extern 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);
extern int cancel_IO (int irq);
extern int enable_cpu_sync_isc (int irq);
extern int disable_cpu_sync_isc (int irq);
extern int cons_dev;
extern int s390_process_IRQ (unsigned int irq);
/*
* where we put the ssd info
*/
struct ssd_info {
__u8 valid:1;
__u8 type:7; /* subchannel type */
__u8 chpid[8]; /* chpids */
__u16 fla[8]; /* full link addresses */
} __attribute__ ((packed));
/*
* path management control word
*/
struct pmcw {
__u32 intparm; /* interruption parameter */
__u32 qf : 1; /* qdio facility */
__u32 res0 : 1; /* reserved zeros */
__u32 isc : 3; /* interruption sublass */
__u32 res5 : 3; /* reserved zeros */
__u32 ena : 1; /* enabled */
__u32 lm : 2; /* limit mode */
__u32 mme : 2; /* measurement-mode enable */
__u32 mp : 1; /* multipath mode */
__u32 tf : 1; /* timing facility */
__u32 dnv : 1; /* device number valid */
__u32 dev : 16; /* device number */
__u8 lpm; /* logical path mask */
__u8 pnom; /* path not operational mask */
__u8 lpum; /* last path used mask */
__u8 pim; /* path installed mask */
__u16 mbi; /* measurement-block index */
__u8 pom; /* path operational mask */
__u8 pam; /* path available mask */
__u8 chpid[8]; /* CHPID 0-7 (if available) */
__u32 unused1 : 8; /* reserved zeros */
__u32 st : 3; /* subchannel type */
__u32 unused2 : 20; /* reserved zeros */
__u32 csense : 1; /* concurrent sense; can be enabled ...*/
/* ... per MSCH, however, if facility */
/* ... is not installed, this results */
/* ... in an operand exception. */
} __attribute__ ((packed));
/*
* subchannel information block
*/
struct schib {
struct pmcw pmcw; /* path management control word */
struct scsw scsw; /* subchannel status word */
__u8 mda[12]; /* model dependent area */
} __attribute__ ((packed,aligned(4)));
/*
* operation request block
*/
struct orb {
__u32 intparm; /* interruption parameter */
__u32 key : 4; /* flags, like key, suspend control, etc. */
__u32 spnd : 1; /* suspend control */
__u32 res1 : 1; /* reserved */
__u32 mod : 1; /* modification control */
__u32 sync : 1; /* synchronize control */
__u32 fmt : 1; /* format control */
__u32 pfch : 1; /* prefetch control */
__u32 isic : 1; /* initial-status-interruption control */
__u32 alcc : 1; /* address-limit-checking control */
__u32 ssic : 1; /* suppress-suspended-interr. control */
__u32 res2 : 1; /* reserved */
__u32 c64 : 1; /* IDAW/QDIO 64 bit control */
__u32 i2k : 1; /* IDAW 2/4kB block size control */
__u32 lpm : 8; /* logical path mask */
__u32 ils : 1; /* incorrect length */
__u32 zero : 6; /* reserved zeros */
__u32 orbx : 1; /* ORB extension control */
__u32 cpa; /* channel program address */
} __attribute__ ((packed,aligned(4)));
/* subchannel data structure used by I/O subroutines */
struct subchannel {
unsigned int irq; /* aka. subchannel number */
spinlock_t lock; /* subchannel lock */
enum {
SUBCHANNEL_TYPE_IO = 0,
SUBCHANNEL_TYPE_CHSC = 1,
SUBCHANNEL_TYPE_MESSAGE = 2,
SUBCHANNEL_TYPE_ADM = 3,
} st; /* subchannel type */
struct {
unsigned int suspend:1; /* allow suspend */
unsigned int prefetch:1;/* deny prefetch */
unsigned int inter:1; /* suppress intermediate interrupts */
} __attribute__ ((packed)) options;
__u8 vpm; /* verified path mask */
__u8 lpm; /* logical path mask */
// TODO: intparm for second start i/o
unsigned long u_intparm; /* user interruption parameter */
struct schib schib; /* subchannel information block */
struct orb orb; /* operation request block */
struct ccw1 sense_ccw; /* static ccw for sense command */
struct ssd_info ssd_info; /* subchannel description */
struct device dev; /* entry in device tree */
struct css_driver *driver;
} __attribute__ ((aligned(8)));
#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */
#define to_subchannel(n) container_of(n, struct subchannel, dev)
extern int cio_validate_subchannel (struct subchannel *, unsigned int);
extern int cio_modify (struct subchannel *);
extern int cio_enable_subchannel (struct subchannel *, unsigned int);
extern int cio_disable_subchannel (struct subchannel *);
extern int cio_cancel (struct subchannel *);
extern int cio_clear (struct subchannel *, unsigned long);
extern int cio_do_io (struct subchannel *, struct ccw1 *, unsigned long, __u8);
extern int cio_resume (struct subchannel *);
extern int cio_halt (struct subchannel *, unsigned long);
extern int cio_start (struct subchannel *, struct ccw1 *, unsigned long, __u8);
extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
/* Use with care. */
extern int cio_tpi (void);
extern struct subchannel *cio_probe_console(void);
extern void cio_release_console(void);
extern int cio_show_msg;
#endif
/*
* drivers/s390/cio/cio_debug.c
* S/390 common I/O routines -- message ids for debugging
* $Revision: 1.5 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
* ChangeLog: 11/04/2002 Arnd Bergmann Split s390io.c into multiple files,
* see s390io.c for complete list of
* changes.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/debug.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_debug_initialized;
/*
* 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)
{
int ret = 0;
cio_debug_msg_id = debug_register ("cio_msg", 4, 4, 16 * sizeof (long));
if (cio_debug_msg_id != NULL) {
debug_register_view (cio_debug_msg_id, &debug_sprintf_view);
debug_set_level (cio_debug_msg_id, 6);
} else {
ret = -1;
}
cio_debug_trace_id = debug_register ("cio_trace", 4, 4, 8);
if (cio_debug_trace_id != NULL) {
debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view);
debug_set_level (cio_debug_trace_id, 6);
} else {
ret = -1;
}
cio_debug_crw_id = debug_register ("cio_crw", 2, 4, 16 * sizeof (long));
if (cio_debug_crw_id != NULL) {
debug_register_view (cio_debug_crw_id, &debug_sprintf_view);
debug_set_level (cio_debug_crw_id, 6);
} else {
ret = -1;
}
if (ret){
printk ("could not initialize debugging\n");
} else {
printk ("debugging initialized\n");
cio_debug_initialized = 1;
}
return ret;
}
arch_initcall (cio_debug_init);
#ifndef S390_DEBUG_H
#define S390_DEBUG_H
#ifndef CIO_DEBUG_H
#define CIO_DEBUG_H
#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)
#include <asm/debug.h>
#define CIO_TRACE_EVENT(imp, txt) do { \
if (cio_debug_initialized) \
debug_text_event(cio_debug_trace_id, \
imp, \
txt); \
}while (0)
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)
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
#ifdef CONFIG_DEBUG_IO
#define DBG printk
#else /* CONFIG_DEBUG_IO */
#define DBG(args,...) do {} while (0)
#endif /* CONFIG_DEBUG_IO */
debug_sprintf_event(cio_debug_crw_id, imp , ##args); \
} while (0)
#define CIO_DEBUG(printk_level,event_level,msg...) ({\
DBG(printk_level msg); \
CIO_MSG_EVENT (event_level, msg); \
})
#define CIO_HEX_EVENT(imp, args...) do { \
debug_event(cio_debug_trace_id, imp, ##args); \
} while (0)
#define CIO_DEBUG_IFMSG(printk_level,event_level,msg...) ({\
#define CIO_DEBUG(printk_level,event_level,msg...) ({ \
if (cio_show_msg) printk(printk_level msg); \
CIO_MSG_EVENT (event_level, msg); \
})
#define CIO_DEBUG_ALWAYS(printk_level,event_level,msg...) ({\
printk(printk_level msg); \
CIO_MSG_EVENT (event_level, msg); \
})
#define CIO_DEBUG_NOCONS(irq,printk_level,func,event_level,msg...) ({\
if (irq != cons_dev) func(printk_level msg); \
CIO_MSG_EVENT (event_level, msg); \
})
/* for use of debug feature */
extern debug_info_t *cio_debug_msg_id;
extern debug_info_t *cio_debug_trace_id;
extern debug_info_t *cio_debug_crw_id;
extern int cio_debug_initialized;
#endif
/*
* drivers/s390/cio/css.c
* driver for channel subsystem
* $Revision: 1.39 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <asm/ccwdev.h> // FIXME: layering violation, remove this
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
#include "device.h" // FIXME: dito
struct subchannel *ioinfo[__MAX_SUBCHANNELS];
unsigned int highest_subchannel;
int css_init_done = 0;
struct device css_bus_device = {
.name = "Channel Subsystem 0",
.bus_id = "css0",
};
static int
css_alloc_subchannel(int irq)
{
struct subchannel *sch;
int ret;
if (ioinfo[irq])
/* There already is a struct subchannel for this irq. */
return -EBUSY;
sch = kmalloc (sizeof (*sch), GFP_DMA);
if (sch == NULL)
return -ENOMEM;
ret = cio_validate_subchannel (sch, irq);
if (ret < 0) {
kfree(sch);
return ret;
}
if (irq > highest_subchannel)
highest_subchannel = irq;
if (sch->st != SUBCHANNEL_TYPE_IO) {
/* For now we ignore all non-io subchannels. */
kfree(sch);
return -EINVAL;
}
ioinfo[irq] = sch;
return 0;
}
static void
css_free_subchannel(int irq)
{
struct subchannel *sch;
sch = ioinfo[irq];
if (sch) {
ioinfo[irq] = NULL;
kfree(sch);
}
}
static int
css_register_subchannel(struct subchannel *sch)
{
static const char *subchannel_types[] = {
"I/O Subchannel",
"CHSC Subchannel",
"Message Subchannel",
"ADM Subchannel",
"undefined subchannel type 4",
"undefined subchannel type 5",
"undefined subchannel type 6",
"undefined subchannel type 7",
"undefined subchannel type 8",
};
int ret;
/* Initialize the subchannel structure */
sch->dev.parent = &css_bus_device;
sch->dev.bus = &css_bus_type;
/* Set a name for the subchannel */
strncpy (sch->dev.name, subchannel_types[sch->st], DEVICE_NAME_SIZE);
snprintf (sch->dev.bus_id, DEVICE_ID_SIZE, "0:%04x", sch->irq);
/* make it known to the system */
ret = device_register(&sch->dev);
if (ret)
printk (KERN_WARNING "%s: could not register %s\n",
__func__, sch->dev.bus_id);
return ret;
}
int
css_probe_device(int irq)
{
int ret;
ret = css_alloc_subchannel(irq);
if (ret)
return ret;
ret = css_register_subchannel(ioinfo[irq]);
if (ret)
css_free_subchannel(irq);
return ret;
}
/*
* Rescan for new devices. FIXME: This is slow.
*/
static void
do_process_crw(void *ignore)
{
int irq, ret;
for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
if (ioinfo[irq])
continue;
ret = css_probe_device(irq);
/* No more memory. It doesn't make sense to continue. No
* panic because this can happen in midflight and just
* because we can't use a new device is no reason to crash
* the system. */
if (ret == -ENOMEM)
break;
/* -ENXIO indicates that there are no more subchannels. */
if (ret == -ENXIO)
break;
}
}
/*
* Called from the machine check handler for subchannel report words.
* Note: this is called disabled from the machine check handler itself.
*/
void
css_process_crw(int irq)
{
static DECLARE_WORK(work, do_process_crw, 0);
struct subchannel *sch;
CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq);
sch = ioinfo[irq];
if (sch == NULL) {
schedule_work(&work);
return;
}
if (!sch->dev.driver_data)
return;
/* FIXME: css_process_crw must not know about ccw_device */
dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER);
// FIXME: revalidate machine checks?
}
/*
* some of the initialization has already been done from init_IRQ(),
* here we do the rest now that the driver core is running.
* Currently, this functions scans all the subchannel structures for
* devices. The long term plan is to remove ioinfo[] and then the
* struct subchannel's will be created during probing.
*/
static int __init
init_channel_subsystem (void)
{
int ret, irq;
if ((ret = bus_register(&css_bus_type)))
goto out;
if ((ret = device_register (&css_bus_device)))
goto out_bus;
css_init_done = 1;
ctl_set_bit(6, 28);
for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
if (!ioinfo[irq]) {
ret = css_alloc_subchannel(irq);
if (ret == -ENOMEM)
panic("Out of memory in "
"init_channel_subsystem\n");
/* -ENXIO: no more subchannels. */
if (ret == -ENXIO)
break;
if (ret)
continue;
}
/*
* We register ALL valid subchannels in ioinfo, even those
* that have been present before init_channel_subsystem.
* These subchannels can't have been registered yet (kmalloc
* not working) so we do it now. This is true e.g. for the
* console subchannel.
*/
css_register_subchannel(ioinfo[irq]);
}
return 0;
out_bus:
bus_unregister(&css_bus_type);
out:
return ret;
}
/*
* find a driver for a subchannel. They identify by the subchannel
* type with the exception that the console subchannel driver has its own
* subchannel type although the device is an i/o subchannel
*/
static int
css_bus_match (struct device *dev, struct device_driver *drv)
{
struct subchannel *sch = container_of (dev, struct subchannel, dev);
struct css_driver *driver = container_of (drv, struct css_driver, drv);
if (sch->st == driver->subchannel_type)
return 1;
return 0;
}
struct bus_type css_bus_type = {
.name = "css",
.match = &css_bus_match,
};
subsys_initcall(init_channel_subsystem);
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(css_bus_type);
#ifndef _CSS_H
#define _CSS_H
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <asm/cio.h>
/*
* path grouping stuff
*/
#define SPID_FUNC_SINGLE_PATH 0x00
#define SPID_FUNC_MULTI_PATH 0x80
#define SPID_FUNC_ESTABLISH 0x00
#define SPID_FUNC_RESIGN 0x40
#define SPID_FUNC_DISBAND 0x20
#define SNID_STATE1_RESET 0
#define SNID_STATE1_UNGROUPED 2
#define SNID_STATE1_GROUPED 3
#define SNID_STATE2_NOT_RESVD 0
#define SNID_STATE2_RESVD_ELSE 2
#define SNID_STATE2_RESVD_SELF 3
#define SNID_STATE3_MULTI_PATH 1
#define SNID_STATE3_SINGLE_PATH 0
struct path_state {
__u8 state1 : 2; /* path state value 1 */
__u8 state2 : 2; /* path state value 2 */
__u8 state3 : 1; /* path state value 3 */
__u8 resvd : 3; /* reserved */
} __attribute__ ((packed));
struct pgid {
union {
__u8 fc; /* SPID function code */
struct path_state ps; /* SNID path state */
} inf;
__u32 cpu_addr : 16; /* CPU address */
__u32 cpu_id : 24; /* CPU identification */
__u32 cpu_model : 16; /* CPU model */
__u32 tod_high; /* high word TOD clock */
} __attribute__ ((packed));
extern struct pgid global_pgid;
#define MAX_CIWS 8
/*
* sense-id response buffer layout
*/
struct senseid {
/* common part */
__u8 reserved; /* always 0x'FF' */
__u16 cu_type; /* control unit type */
__u8 cu_model; /* control unit model */
__u16 dev_type; /* device type */
__u8 dev_model; /* device model */
__u8 unused; /* padding byte */
/* extended part */
struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */
} __attribute__ ((packed,aligned(4)));
struct ccw_device_private {
int state; /* device state */
__u16 devno; /* device number */
__u16 irq; /* subchannel number */
__u8 imask; /* lpm mask for SNID/SID/SPGID */
int iretry; /* retry counter SNID/SID/SPGID */
struct {
unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */
} __attribute__ ((packed)) options;
struct {
unsigned int pgid_supp:1; /* "path group ID" supported */
unsigned int pgid_single:1; /* use single path for Set PGID */
unsigned int esid:1; /* Ext. SenseID supported by HW */
unsigned int dosense:1; /* delayed SENSE required */
} __attribute__((packed)) flags;
struct qdio_irq *qdio_data;
struct irb irb; /* device status */
struct senseid senseid; /* SenseID info */
struct pgid pgid; /* path group ID */
struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */
struct work_struct kick_work;
wait_queue_head_t wait_q;
struct timer_list timer;
};
/*
* A css driver handles all subchannels of one type.
* Currently, we only care about I/O subchannels (type 0), these
* have a ccw_device connected to them.
*/
struct css_driver {
unsigned int subchannel_type;
struct device_driver drv;
void (*irq)(struct device *);
};
/*
* all css_drivers have the css_bus_type
*/
extern struct bus_type css_bus_type;
int css_probe_device(int irq);
extern unsigned int highest_subchannel;
extern int css_init_done;
#define __MAX_SUBCHANNELS 65536
extern struct subchannel *ioinfo[__MAX_SUBCHANNELS];
extern struct bus_type css_bus_type;
extern struct device css_bus_device;
#endif
This diff is collapsed.
#ifndef S390_DEVICE_H
#define S390_DEVICE_H
/*
* states of the device statemachine
*/
enum dev_state {
DEV_STATE_NOT_OPER,
DEV_STATE_SENSE_PGID,
DEV_STATE_SENSE_ID,
DEV_STATE_OFFLINE,
DEV_STATE_VERIFY,
DEV_STATE_ONLINE,
DEV_STATE_W4SENSE,
DEV_STATE_DISBAND_PGID,
DEV_STATE_BOXED,
/* special states for qdio */
DEV_STATE_QDIO_INIT,
DEV_STATE_QDIO_ACTIVE,
DEV_STATE_QDIO_CLEANUP,
/* states to wait for i/o completion before doing something */
DEV_STATE_ONLINE_VERIFY,
DEV_STATE_W4SENSE_VERIFY,
/* last element! */
NR_DEV_STATES
};
/*
* asynchronous events of the device statemachine
*/
enum dev_event {
DEV_EVENT_NOTOPER,
DEV_EVENT_INTERRUPT,
DEV_EVENT_TIMEOUT,
DEV_EVENT_VERIFY,
/* last element! */
NR_DEV_EVENTS
};
struct ccw_device;
/*
* action called through jumptable
*/
typedef void (fsm_func_t)(struct ccw_device *, enum dev_event);
extern fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS];
static inline void
dev_fsm_event(struct ccw_device *cdev, enum dev_event dev_event)
{
dev_jumptable[cdev->private->state][dev_event](cdev, dev_event);
}
/*
* Delivers 1 if the device state is final.
*/
static inline int
dev_fsm_final_state(struct ccw_device *cdev)
{
return (cdev->private->state == DEV_STATE_NOT_OPER ||
cdev->private->state == DEV_STATE_OFFLINE ||
cdev->private->state == DEV_STATE_ONLINE ||
cdev->private->state == DEV_STATE_BOXED);
}
void io_subchannel_register(void *data);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *);
int ccw_device_do_sense(struct ccw_device *, struct irb *);
/* Function prototypes for sense id stuff. */
void ccw_device_sense_id_start(struct ccw_device *);
void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event);
void ccw_device_sense_id_done(struct ccw_device *, int);
/* Function prototypes for path grouping stuff. */
void ccw_device_sense_pgid_start(struct ccw_device *);
void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event);
void ccw_device_sense_pgid_done(struct ccw_device *, int);
void ccw_device_verify_start(struct ccw_device *);
void ccw_device_verify_irq(struct ccw_device *, enum dev_event);
void ccw_device_verify_done(struct ccw_device *, int);
void ccw_device_disband_start(struct ccw_device *);
void ccw_device_disband_irq(struct ccw_device *, enum dev_event);
void ccw_device_disband_done(struct ccw_device *, int);
void ccw_device_call_handler(struct ccw_device *);
/* qdio needs this. */
void ccw_device_set_timeout(struct ccw_device *, int);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef S390_IOINFO_H
#define S390_IOINFO_H
extern unsigned int highest_subchannel;
extern ioinfo_t *ioinfo_head;
extern ioinfo_t *ioinfo_tail;
extern ioinfo_t *ioinfo[__MAX_SUBCHANNELS];
#endif
This diff is collapsed.
#ifndef S390_MISC_H
#define S390_MISC_H
#endif
This diff is collapsed.
#ifndef S390_PROC_H
#define S390_PROC_H
extern int cio_procfs_device_purge (void);
extern int cio_procfs_device_create (int devno);
extern int cio_procfs_device_remove (int devno);
//FIXME: shouldn`t this be 'struct{unsigned int len; char data[0]};' ?
/*
* Display info on subchannels in /proc/subchannels.
* Adapted from procfs stuff in dasd.c by Cornelia Huck, 02/28/01.
*/
typedef struct {
char *data;
int len;
} tempinfo_t;
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment