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

[PATCH] s390: common i/o layer update.

Common i/o layer fixes:
 - Fix for path no operational condition in cio_start.
 - Fix handling of user interruption parameter.
 - Add code to wait for devices in init_ccw_bus_type.
 - Move qdio states out of main cio state machine.
 - Reworked chsc data structures.
 - Add ccw_device_start_timeout.
 - Handle path verification required flag.
parent 71e25a79
......@@ -2,7 +2,7 @@
* drivers/s390/cio/airq.c
* S/390 common I/O routines -- support for adapter interruptions
*
* $Revision: 1.10 $
* $Revision: 1.11 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -87,14 +87,14 @@ s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
}
void
do_adapter_IO (__u32 intparm)
do_adapter_IO (void)
{
CIO_TRACE_EVENT (4, "doaio");
spin_lock (&adapter_lock);
if (adapter_handler)
(*adapter_handler) (intparm);
(*adapter_handler) ();
spin_unlock (&adapter_lock);
......
#ifndef S390_AINTERRUPT_H
#define S390_AINTERRUPT_H
typedef int (*adapter_int_handler_t)(__u32 intparm);
typedef int (*adapter_int_handler_t)(void);
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);
extern void do_adapter_IO (void);
#endif
/*
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
* $Revision: 1.57 $
* $Revision: 1.67 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -76,53 +76,77 @@ static int
chsc_get_sch_desc_irq(int irq)
{
int ccode, chpid, j;
int ret;
/* FIXME: chsc_area_sei cannot be on the stack since it needs to
* be page-aligned. Implement proper locking or dynamic
* allocation or prove that this function does not have to be
* reentrant! */
static struct ssd_area chsc_area_ssd
__attribute__ ((aligned(PAGE_SIZE)));
typeof (chsc_area_ssd.response_block)
*ssd_res = &chsc_area_ssd.response_block;
chsc_area_ssd = (struct ssd_area) {
.request_block = {
.command_code1 = 0x0010,
.command_code2 = 0x0004,
.f_sch = irq,
.l_sch = irq,
}
struct {
struct chsc_header request;
u16 reserved1;
u16 f_sch; /* first subchannel */
u16 reserved2;
u16 l_sch; /* last subchannel */
u32 reserved3;
struct chsc_header response;
u32 reserved4;
u8 sch_valid : 1;
u8 dev_valid : 1;
u8 st : 3; /* subchannel type */
u8 zeroes : 3;
u8 unit_addr; /* unit address */
u16 devno; /* device number */
u8 path_mask;
u8 fla_valid_mask;
u16 sch; /* subchannel */
u8 chpid[8]; /* chpids 0-7 */
u16 fla[8]; /* full link addresses 0-7 */
} *ssd_area;
ssd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!ssd_area) {
CIO_CRW_EVENT(0, "No memory for ssd area!\n");
return -ENOMEM;
}
ssd_area->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x0004,
};
ccode = chsc(&chsc_area_ssd);
ssd_area->f_sch = irq;
ssd_area->l_sch = irq;
ccode = chsc(ssd_area);
if (ccode > 0) {
pr_debug("chsc returned with ccode = %d\n", ccode);
if (ccode == 3)
return -ENODEV;
return -EBUSY;
ret = (ccode == 3) ? -ENODEV : -EBUSY;
goto out;
}
switch (chsc_area_ssd.response_block.response_code) {
switch (ssd_area->response.code) {
case 0x0001: /* everything ok */
ret = 0;
break;
case 0x0002:
CIO_CRW_EVENT(2, "Invalid command!\n");
case 0x0003:
CIO_CRW_EVENT(2, "Error in chsc request block!\n");
return -EINVAL;
ret = -EINVAL;
break;
case 0x0004:
CIO_CRW_EVENT(2, "Model does not provide ssd\n");
return -EOPNOTSUPP;
ret = -EOPNOTSUPP;
break;
default:
CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
chsc_area_ssd.response_block.response_code);
return -EIO;
ssd_area->response.code);
ret = -EIO;
break;
}
if (ret != 0)
goto out;
/*
* ssd_res->st stores the type of the detected
* ssd_area->st stores the type of the detected
* subchannel, with the following definitions:
*
* 0: I/O subchannel: All fields have meaning
......@@ -135,43 +159,45 @@ chsc_get_sch_desc_irq(int irq)
*
* Other types are currently undefined.
*/
if (ssd_res->st > 3) { /* uhm, that looks strange... */
if (ssd_area->st > 3) { /* uhm, that looks strange... */
CIO_CRW_EVENT(0, "Strange subchannel type %d"
" for sch %x\n", ssd_res->st, irq);
" for sch %x\n", ssd_area->st, irq);
/*
* There may have been a new subchannel type defined in the
* time since this code was written; since we don't know which
* fields have meaning and what to do with it we just jump out
*/
return 0;
goto out;
} else {
const char type[4][8] = {"I/O", "chsc", "message", "ADM"};
const char *type[4] = {"I/O", "chsc", "message", "ADM"};
CIO_CRW_EVENT(6, "ssd: sch %x is %s subchannel\n",
irq, type[ssd_res->st]);
irq, type[ssd_area->st]);
if (ioinfo[irq] == NULL)
/* FIXME: we should do device rec. here... */
return 0;
goto out;
ioinfo[irq]->ssd_info.valid = 1;
ioinfo[irq]->ssd_info.type = ssd_res->st;
ioinfo[irq]->ssd_info.type = ssd_area->st;
}
if (ssd_res->st == 0 || ssd_res->st == 2) {
if (ssd_area->st == 0 || ssd_area->st == 2) {
for (j = 0; j < 8; j++) {
if (!((0x80 >> j) & ssd_res->path_mask &
ssd_res->fla_valid_mask))
if (!((0x80 >> j) & ssd_area->path_mask &
ssd_area->fla_valid_mask))
continue;
chpid = ssd_res->chpid[j];
chpid = ssd_area->chpid[j];
if (chpid
&& (!test_and_set_bit (chpid, chpids_known))
&& (test_bit (chpid, chpids_logical)))
set_bit (chpid, chpids);
ioinfo[irq]->ssd_info.chpid[j] = chpid;
ioinfo[irq]->ssd_info.fla[j] = ssd_res->fla[j];
ioinfo[irq]->ssd_info.fla[j] = ssd_area->fla[j];
}
}
return 0;
out:
free_page ((unsigned long) ssd_area);
return ret;
}
static int
......@@ -216,6 +242,7 @@ static inline void
s390_subchannel_remove_chpid(struct subchannel *sch, __u8 chpid)
{
int j;
int mask;
for (j = 0; j < 8; j++)
if (sch->schib.pmcw.chpid[j] == chpid)
......@@ -223,16 +250,68 @@ s390_subchannel_remove_chpid(struct subchannel *sch, __u8 chpid)
if (j >= 8)
return;
mask = 0x80 >> j;
spin_lock(&sch->lock);
chsc_validate_chpids(sch);
/* just to be sure... */
sch->lpm &= ~(0x80>>j);
stsch(sch->irq, &sch->schib);
if (sch->vpm == mask) {
dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER);
goto out_unlock;
}
if ((sch->schib.scsw.actl & (SCSW_ACTL_CLEAR_PEND |
SCSW_ACTL_HALT_PEND |
SCSW_ACTL_START_PEND |
SCSW_ACTL_RESUME_PEND)) &&
(sch->schib.pmcw.lpum == mask)) {
int cc = cio_cancel(sch);
if (cc == -ENODEV) {
dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER);
goto out_unlock;
}
if (cc == -EINVAL) {
struct ccw_device *cdev;
cc = cio_clear(sch);
if (cc == -ENODEV) {
dev_fsm_event(sch->dev.driver_data,
DEV_EVENT_NOTOPER);
goto out_unlock;
}
/* Call handler. */
cdev = sch->dev.driver_data;
cdev->private->state = DEV_STATE_CLEAR_VERIFY;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-EIO));
goto out_unlock;
}
} else if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
(sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
(sch->schib.pmcw.lpum == mask)) {
struct ccw_device *cdev;
int cc;
cc = cio_clear(sch);
if (cc == -ENODEV) {
dev_fsm_event(sch->dev.driver_data, DEV_EVENT_NOTOPER);
goto out_unlock;
}
/* Call handler. */
cdev = sch->dev.driver_data;
cdev->private->state = DEV_STATE_CLEAR_VERIFY;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-EIO));
goto out_unlock;
}
/* trigger path verification. */
dev_fsm_event(sch->dev.driver_data, DEV_EVENT_VERIFY);
out_unlock:
spin_unlock(&sch->lock);
}
......@@ -265,7 +344,7 @@ s390_set_chpid_offline( __u8 chpid)
sch = ioinfo[irq];
if (sch == NULL)
continue; /* we don't know the device anyway */
/* FIXME: Kill pending I/O. */
s390_subchannel_remove_chpid(sch, chpid);
}
#endif
......@@ -349,7 +428,7 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask)
if (!test_bit(chpid, chpids_logical))
return; /* no need to do the rest */
for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) {
for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
int chp_mask;
sch = ioinfo[irq];
......@@ -369,7 +448,6 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask)
continue;
}
/* FIXME: Kill pending I/O. */
spin_lock_irq(&sch->lock);
chp_mask = s390_process_res_acc_sch(chpid, fla, fla_mask, sch);
......@@ -402,92 +480,97 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask)
static void
do_process_crw(void *ignore)
{
int do_sei;
struct {
struct chsc_header request;
u32 reserved1;
u32 reserved2;
u32 reserved3;
struct chsc_header response;
u32 reserved4;
u8 flags;
u8 vf; /* validity flags */
u8 rs; /* reporting source */
u8 cc; /* content code */
u16 fla; /* full link address */
u16 rsid; /* reporting source id */
u32 reserved5;
u32 reserved6;
u32 ccdf; /* content-code dependent field */
u32 reserved7;
u32 reserved8;
u32 reserved9;
} *sei_area;
/*
* build the chsc request block for store event information
* and do the call
*/
sei_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
/* FIXME: chsc_area_sei cannot be on the stack since it needs to
* be page-aligned. Implement proper locking or dynamic
* allocation or prove that this function does not have to be
* reentrant! */
static struct sei_area chsc_area_sei
__attribute__ ((aligned(PAGE_SIZE))) = {
.request_block = {
.command_code1 = 0x0010,
.command_code2 = 0x000e
}
};
typeof (chsc_area_sei.response_block)
*sei_res = &chsc_area_sei.response_block;
if (!sei_area) {
CIO_CRW_EVENT(0, "No memory for sei area!\n");
return;
}
CIO_TRACE_EVENT( 2, "prcss");
do_sei = 1;
while (do_sei) {
do {
int ccode;
memset(sei_area, 0, sizeof(*sei_area));
sei_area->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x000e,
};
ccode = chsc(&chsc_area_sei);
ccode = chsc(sei_area);
if (ccode > 0)
return;
goto out;
switch (sei_res->response_code) {
switch (sei_area->response.code) {
/* for debug purposes, check for problems */
case 0x0001:
CIO_CRW_EVENT(4, "chsc_process_crw: event information "
"successfully stored\n");
break; /* everything ok */
case 0x0002:
CIO_CRW_EVENT(2,
"chsc_process_crw: invalid command!\n");
return;
goto out;
case 0x0003:
CIO_CRW_EVENT(2, "chsc_process_crw: error in chsc "
"request block!\n");
return;
goto out;
case 0x0005:
CIO_CRW_EVENT(2, "chsc_process_crw: no event "
"information stored\n");
return;
goto out;
default:
CIO_CRW_EVENT(2, "chsc_process_crw: chsc response %d\n",
sei_res->response_code);
return;
sei_area->response.code);
goto out;
}
CIO_CRW_EVENT(4, "chsc_process_crw: event information "
"successfully stored\n");
/* Check if there is more event information pending. */
if (sei_res->flags & 0x80)
CIO_CRW_EVENT( 2, "chsc_process_crw: "
"further event information pending\n");
else
do_sei = 0;
/* Check if we might have lost some information. */
if (sei_res->flags & 0x40)
CIO_CRW_EVENT( 2, "chsc_process_crw: Event information "
if (sei_area->flags & 0x40)
CIO_CRW_EVENT(2, "chsc_process_crw: Event information "
"has been lost due to overflow!\n");
if (sei_res->rs != 4) {
if (sei_area->rs != 4) {
CIO_CRW_EVENT(2, "chsc_process_crw: reporting source "
"(%04X) isn't a chpid!\n",
sei_res->rsid);
sei_area->rsid);
continue;
}
/* which kind of information was stored? */
switch (sei_res->cc) {
switch (sei_area->cc) {
case 1: /* link incident*/
CIO_CRW_EVENT(4, "chsc_process_crw: "
"channel subsystem reports link incident,"
" source is chpid %x\n", sei_res->rsid);
" source is chpid %x\n", sei_area->rsid);
s390_set_chpid_offline(sei_res->rsid);
s390_set_chpid_offline(sei_area->rsid);
break;
case 2: /* i/o resource accessibiliy */
......@@ -495,27 +578,27 @@ do_process_crw(void *ignore)
"channel subsystem reports some I/O "
"devices may have become accessible\n");
pr_debug("Data received after sei: \n");
pr_debug("Validity flags: %x\n", sei_res->vf);
pr_debug("Validity flags: %x\n", sei_area->vf);
/* allocate a new channel path structure, if needed */
if (chps[sei_res->rsid] == NULL)
new_channel_path(sei_res->rsid, CHP_ONLINE);
if (chps[sei_area->rsid] == NULL)
new_channel_path(sei_area->rsid, CHP_ONLINE);
else
set_chp_status(sei_res->rsid, CHP_ONLINE);
set_chp_status(sei_area->rsid, CHP_ONLINE);
if ((sei_res->vf & 0x80) == 0) {
pr_debug("chpid: %x\n", sei_res->rsid);
s390_process_res_acc(sei_res->rsid, 0, 0);
} else if ((sei_res->vf & 0xc0) == 0x80) {
if ((sei_area->vf & 0x80) == 0) {
pr_debug("chpid: %x\n", sei_area->rsid);
s390_process_res_acc(sei_area->rsid, 0, 0);
} else if ((sei_area->vf & 0xc0) == 0x80) {
pr_debug("chpid: %x link addr: %x\n",
sei_res->rsid, sei_res->fla);
s390_process_res_acc(sei_res->rsid,
sei_res->fla, 0xff00);
} else if ((sei_res->vf & 0xc0) == 0xc0) {
sei_area->rsid, sei_area->fla);
s390_process_res_acc(sei_area->rsid,
sei_area->fla, 0xff00);
} else if ((sei_area->vf & 0xc0) == 0xc0) {
pr_debug("chpid: %x full link addr: %x\n",
sei_res->rsid, sei_res->fla);
s390_process_res_acc(sei_res->rsid,
sei_res->fla, 0xffff);
sei_area->rsid, sei_area->fla);
s390_process_res_acc(sei_area->rsid,
sei_area->fla, 0xffff);
}
pr_debug("\n");
......@@ -523,15 +606,13 @@ do_process_crw(void *ignore)
default: /* other stuff */
CIO_CRW_EVENT(4, "chsc_process_crw: event %d\n",
sei_res->cc);
sei_area->cc);
break;
}
if (do_sei) {
memset(&chsc_area_sei, 0, sizeof(struct sei_area));
chsc_area_sei.request_block.command_code1 = 0x0010;
chsc_area_sei.request_block.command_code2 = 0x000e;
}
}
} while (sei_area->flags & 0x80);
out:
free_page((unsigned long)sei_area);
}
void
......@@ -539,7 +620,7 @@ chsc_process_crw(void)
{
static DECLARE_WORK(work, do_process_crw, 0);
schedule_work(&work);
queue_work(ccw_device_work, &work);
}
static void
......@@ -555,7 +636,7 @@ chp_add(int chpid)
sprintf(dbf_txt, "cadd%x", chpid);
CIO_TRACE_EVENT(2, dbf_txt);
for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) {
for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
int i;
sch = ioinfo[irq];
......@@ -567,7 +648,6 @@ chp_add(int chpid)
continue;
}
/* FIXME: Kill pending I/O. */
spin_lock(&sch->lock);
for (i=0; i<8; i++)
if (sch->schib.pmcw.chpid[i] == chpid) {
......@@ -599,26 +679,9 @@ chp_add(int chpid)
* Handling of crw machine checks with channel path source.
*/
void
chp_process_crw(int chpid)
chp_process_crw(int chpid, int on)
{
/*
* Update our descriptions. We need this since we don't always
* get machine checks for path come and can't rely on our information
* being consistent otherwise.
*/
chsc_get_sch_descriptions();
if (!cio_chsc_desc_avail) {
/*
* Something went wrong...
* We can't reliably say whether a path was there before.
*/
CIO_CRW_EVENT(0, "Error: Could not retrieve "
"subchannel descriptions, will not process chp"
"machine check...\n");
return;
}
if (!test_bit(chpid, chpids)) {
if (on == 0) {
/* Path has gone. We use the link incident routine.*/
s390_set_chpid_offline(chpid);
} else {
......@@ -646,9 +709,6 @@ s390_vary_chpid( __u8 chpid, int on)
struct subchannel *sch;
int irq;
if (chpid <=0 || chpid >= NR_CHPIDS)
return -EINVAL;
sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid);
CIO_TRACE_EVENT( 2, dbf_text);
......
......@@ -12,85 +12,10 @@
#define CHSC_SEI_ACC_LINKADDR 2
#define CHSC_SEI_ACC_FULLLINKADDR 3
struct sei_area {
struct {
/* word 0 */
__u16 command_code1;
__u16 command_code2;
/* word 1 */
__u32 reserved1;
/* word 2 */
__u32 reserved2;
/* word 3 */
__u32 reserved3;
} __attribute__ ((packed,aligned(8))) request_block;
struct {
/* word 0 */
__u16 length;
__u16 response_code;
/* word 1 */
__u32 reserved1;
/* word 2 */
__u8 flags;
__u8 vf; /* validity flags */
__u8 rs; /* reporting source */
__u8 cc; /* content code */
/* word 3 */
__u16 fla; /* full link address */
__u16 rsid; /* reporting source id */
/* word 4 */
__u32 reserved2;
/* word 5 */
__u32 reserved3;
/* word 6 */
__u32 ccdf; /* content-code dependent field */
/* word 7 */
__u32 reserved4;
/* word 8 */
__u32 reserved5;
/* word 9 */
__u32 reserved6;
} __attribute__ ((packed,aligned(8))) response_block;
} __attribute__ ((packed,aligned(PAGE_SIZE)));
struct ssd_area {
struct {
/* word 0 */
__u16 command_code1;
__u16 command_code2;
/* word 1 */
__u16 reserved1;
__u16 f_sch; /* first subchannel */
/* word 2 */
__u16 reserved2;
__u16 l_sch; /* last subchannel */
/* word 3 */
__u32 reserved3;
} __attribute__ ((packed,aligned(8))) request_block;
struct {
/* word 0 */
__u16 length;
__u16 response_code;
/* word 1 */
__u32 reserved1;
/* word 2 */
__u8 sch_valid : 1;
__u8 dev_valid : 1;
__u8 st : 3; /* subchannel type */
__u8 zeroes : 3;
__u8 unit_addr; /* unit address */
__u16 devno; /* device number */
/* word 3 */
__u8 path_mask;
__u8 fla_valid_mask;
__u16 sch; /* subchannel */
/* words 4-5 */
__u8 chpid[8]; /* chpids 0-7 */
/* words 6-9 */
__u16 fla[8]; /* full link addresses 0-7 */
} __attribute__ ((packed,aligned(8))) response_block;
} __attribute__ ((packed,aligned(PAGE_SIZE)));
struct chsc_header {
u16 length;
u16 code;
};
struct channel_path {
int id;
......
/*
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
* $Revision: 1.91 $
* $Revision: 1.97 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -176,13 +176,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
return -ENODEV;
return (sch->lpm ? -EACCES : -ENODEV);
}
int
cio_start (struct subchannel *sch, /* subchannel structure */
struct ccw1 * cpa, /* logical channel prog addr */
unsigned long intparm, /* interruption parameter */
unsigned int intparm, /* interruption parameter */
__u8 lpm) /* logical path mask */
{
char dbf_txt[15];
......@@ -191,7 +191,7 @@ cio_start (struct subchannel *sch, /* subchannel structure */
sprintf (dbf_txt, "stIO%x", sch->irq);
CIO_TRACE_EVENT (4, dbf_txt);
sch->orb.intparm = (__u32) (long) &sch->u_intparm;
sch->orb.intparm = intparm;
sch->orb.fmt = 1;
sch->orb.pfch = sch->options.prefetch == 0;
......@@ -219,7 +219,6 @@ cio_start (struct subchannel *sch, /* subchannel structure */
/*
* initialize device status information
*/
sch->u_intparm = intparm;
sch->schib.scsw.actl |= SCSW_ACTL_START_PEND;
return 0;
case 1: /* status pending */
......@@ -265,13 +264,10 @@ cio_resume (struct subchannel *sch)
}
/*
* 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.
* halt I/O operation
*/
int
cio_halt(struct subchannel *sch, unsigned long intparm)
cio_halt(struct subchannel *sch)
{
char dbf_txt[15];
int ccode;
......@@ -297,7 +293,6 @@ cio_halt(struct subchannel *sch, unsigned long intparm)
switch (ccode) {
case 0:
sch->u_intparm = intparm;
sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND;
return 0;
case 1: /* status pending */
......@@ -309,13 +304,10 @@ cio_halt(struct subchannel *sch, unsigned long intparm)
}
/*
* 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.
* Clear I/O operation
*/
int
cio_clear(struct subchannel *sch, unsigned long intparm)
cio_clear(struct subchannel *sch)
{
char dbf_txt[15];
int ccode;
......@@ -340,7 +332,6 @@ cio_clear(struct subchannel *sch, unsigned long intparm)
switch (ccode) {
case 0:
sch->u_intparm = intparm;
sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND;
return 0;
default: /* device not operational */
......@@ -374,6 +365,8 @@ cio_cancel (struct subchannel *sch)
switch (ccode) {
case 0: /* success */
/* Update information in scsw. */
stsch (sch->irq, &sch->schib);
return 0;
case 1: /* status pending */
return -EBUSY;
......@@ -620,7 +613,7 @@ do_IRQ (struct pt_regs regs)
*/
if (tpi_info->adapter_IO == 1 &&
tpi_info->int_type == IO_INTERRUPT_TYPE) {
do_adapter_IO (tpi_info->intparm);
do_adapter_IO();
continue;
}
sch = ioinfo[tpi_info->irq];
......
......@@ -98,8 +98,6 @@ struct subchannel {
__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 */
......@@ -116,11 +114,10 @@ extern int cio_validate_subchannel (struct subchannel *, unsigned int);
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_clear (struct subchannel *);
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_halt (struct subchannel *);
extern int cio_start (struct subchannel *, struct ccw1 *, unsigned int, __u8);
extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
......
/*
* drivers/s390/cio/css.c
* driver for channel subsystem
* $Revision: 1.40 $
* $Revision: 1.43 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -41,7 +41,7 @@ css_alloc_subchannel(int irq)
/* There already is a struct subchannel for this irq. */
return -EBUSY;
sch = kmalloc (sizeof (*sch), GFP_DMA);
sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA);
if (sch == NULL)
return -ENOMEM;
ret = cio_validate_subchannel (sch, irq);
......@@ -161,7 +161,7 @@ css_process_crw(int irq)
sch = ioinfo[irq];
if (sch == NULL) {
schedule_work(&work);
queue_work(ccw_device_work, &work);
return;
}
if (!sch->dev.driver_data)
......@@ -172,7 +172,7 @@ css_process_crw(int irq)
ccode = stsch(irq, &sch->schib);
if (!ccode)
if (devno != sch->schib.pmcw.dev)
schedule_work(&work);
queue_work(ccw_device_work, &work);
}
/*
......
......@@ -79,6 +79,7 @@ struct ccw_device_private {
unsigned int esid:1; /* Ext. SenseID supported by HW */
unsigned int dosense:1; /* delayed SENSE required */
} __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data;
struct irb irb; /* device status */
struct senseid senseid; /* SenseID info */
......
/*
* drivers/s390/cio/device.c
* bus driver for ccw devices
* $Revision: 1.50 $
* $Revision: 1.53 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
......@@ -126,14 +127,32 @@ static struct css_driver io_subchannel_driver = {
.irq = io_subchannel_irq,
};
struct workqueue_struct *ccw_device_work;
static wait_queue_head_t ccw_device_init_wq;
static atomic_t ccw_device_init_count;
static int __init
init_ccw_bus_type (void)
{
int ret;
init_waitqueue_head(&ccw_device_init_wq);
atomic_set(&ccw_device_init_count, 0);
ccw_device_work = create_workqueue("cio");
if (!ccw_device_work)
return -ENOMEM; /* FIXME: better errno ? */
if ((ret = bus_register (&ccw_bus_type)))
return ret;
return driver_register(&io_subchannel_driver.drv);
if ((ret = driver_register(&io_subchannel_driver.drv)))
return ret;
wait_event(ccw_device_init_wq,
atomic_read(&ccw_device_init_count) == 0);
flush_workqueue(ccw_device_work);
return 0;
}
static void __exit
......@@ -141,6 +160,7 @@ cleanup_ccw_bus_type (void)
{
driver_unregister(&io_subchannel_driver.drv);
bus_unregister(&ccw_bus_type);
destroy_workqueue(ccw_device_work);
}
subsys_initcall(init_ccw_bus_type);
......@@ -360,7 +380,7 @@ ccw_device_release(struct device *dev)
/*
* Register recognized device.
*/
void
static void
io_subchannel_register(void *data)
{
struct ccw_device *cdev;
......@@ -389,6 +409,42 @@ io_subchannel_register(void *data)
put_device(&sch->dev);
}
/*
* subchannel recognition done. Called from the state machine.
*/
void
io_subchannel_recog_done(struct ccw_device *cdev)
{
struct subchannel *sch;
if (css_init_done == 0)
return;
switch (cdev->private->state) {
case DEV_STATE_NOT_OPER:
/* Remove device found not operational. */
sch = to_subchannel(cdev->dev.parent);
sch->dev.driver_data = 0;
put_device(&sch->dev);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
break;
case DEV_STATE_OFFLINE:
/*
* We can't register the device in interrupt context so
* we schedule a work item.
*/
INIT_WORK(&cdev->private->kick_work,
io_subchannel_register, (void *) cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
break;
case DEV_STATE_BOXED:
/* Device did not respond in time. */
break;
}
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
static void
io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
{
......@@ -419,6 +475,9 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
/* Do first half of device_register. */
device_initialize(&cdev->dev);
/* Increase counter of devices currently in recognition. */
atomic_inc(&ccw_device_init_count);
/* Start async. device sensing. */
spin_lock_irq(cdev->ccwlock);
rc = ccw_device_recognition(cdev);
......@@ -428,6 +487,8 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
put_device(&sch->dev);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
}
......@@ -452,7 +513,8 @@ io_subchannel_probe (struct device *pdev)
if (!cdev)
return -ENOMEM;
memset(cdev, 0, sizeof(struct ccw_device));
cdev->private = kmalloc(sizeof(struct ccw_device_private), GFP_DMA);
cdev->private = kmalloc(sizeof(struct ccw_device_private),
GFP_KERNEL | GFP_DMA);
if (!cdev->private) {
kfree(cdev);
return -ENOMEM;
......
......@@ -14,13 +14,11 @@ enum dev_state {
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,
DEV_STATE_CLEAR_VERIFY,
DEV_STATE_TIMEOUT_KILL,
/* last element! */
NR_DEV_STATES
};
......@@ -63,7 +61,9 @@ dev_fsm_final_state(struct ccw_device *cdev)
cdev->private->state == DEV_STATE_BOXED);
}
void io_subchannel_register(void *data);
extern struct workqueue_struct *ccw_device_work;
void io_subchannel_recog_done(struct ccw_device *cdev);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
......
......@@ -81,14 +81,14 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
/* Stage 2: halt io. */
while (cdev->private->iretry-- > 0)
if (cio_halt (sch, 0xC8C1D3E3) == 0)
if (cio_halt (sch) == 0)
return -EBUSY;
/* halt io unsuccessful. */
cdev->private->iretry = 255; /* 255 clear retries. */
}
/* Stage 3: clear io. */
while (cdev->private->iretry-- > 0)
if (cio_clear (sch, 0x40C3D3D9) == 0)
if (cio_clear (sch) == 0)
return -EBUSY;
panic("Can't stop i/o on subchannel.\n");
}
......@@ -112,10 +112,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
CIO_DEBUG(KERN_WARNING, 2,
"SenseID : unknown device %04X on subchannel %04X\n",
sch->schib.pmcw.dev, sch->irq);
sch->dev.driver_data = 0;
put_device(&sch->dev);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
break;
case DEV_STATE_OFFLINE:
/* fill out sense information */
......@@ -131,11 +127,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
"%04X/%02X\n", sch->schib.pmcw.dev,
cdev->id.cu_type, cdev->id.cu_model,
cdev->id.dev_type, cdev->id.dev_model);
if (css_init_done == 0)
break;
INIT_WORK(&cdev->private->kick_work,
io_subchannel_register, (void *) cdev);
schedule_work(&cdev->private->kick_work);
break;
case DEV_STATE_BOXED:
CIO_DEBUG(KERN_WARNING, 2,
......@@ -143,6 +134,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
sch->schib.pmcw.dev, sch->irq);
break;
}
io_subchannel_recog_done(cdev);
wake_up(&cdev->private->wait_q);
}
......@@ -219,9 +211,6 @@ ccw_device_recognition(struct ccw_device *cdev)
static void
ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
if (ccw_device_cancel_halt_clear(cdev) == 0)
ccw_device_recog_done(cdev, DEV_STATE_BOXED);
else
......@@ -349,9 +338,6 @@ ccw_device_offline(struct ccw_device *cdev)
static void
ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
if (ccw_device_cancel_halt_clear(cdev) == 0)
ccw_device_done(cdev, DEV_STATE_BOXED);
else
......@@ -393,8 +379,8 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
// FIXME: not-oper indication to device driver ?
ccw_device_call_handler(cdev);
}
device_unregister(&cdev->dev);
wake_up(&cdev->private->wait_q);
device_unregister(&cdev->dev);
}
/*
......@@ -438,14 +424,39 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
/* Accumulate status and find out if a basic sense is needed. */
ccw_device_accumulate_irb(cdev, irb);
if (cdev->private->flags.dosense) {
if (ccw_device_do_sense(cdev, irb) == 0)
cdev->private->state = DEV_STATE_W4SENSE;
if (ccw_device_do_sense(cdev, irb) == 0) {
/* Check if we have to trigger path verification. */
if (irb->esw.esw0.erw.pvrf)
cdev->private->state = DEV_STATE_W4SENSE_VERIFY;
else
cdev->private->state = DEV_STATE_W4SENSE;
}
return;
}
if (irb->esw.esw0.erw.pvrf)
/* Try to start path verification. */
ccw_device_online_verify(cdev, 0);
/* No basic sense required, call the handler. */
ccw_device_call_handler(cdev);
}
/*
* Got an timeout in online state.
*/
static void
ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
{
ccw_device_set_timeout(cdev, 0);
if (ccw_device_cancel_halt_clear(cdev) != 0) {
ccw_device_set_timeout(cdev, 3*HZ);
cdev->private->state = DEV_STATE_TIMEOUT_KILL;
return;
}
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-ETIMEDOUT));
}
static void
ccw_device_irq_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
......@@ -491,11 +502,17 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
/* Add basic sense info to irb. */
ccw_device_accumulate_basic_sense(cdev, irb);
if (cdev->private->flags.dosense) {
/* Check if we have to trigger path verification. */
if (irb->esw.esw0.erw.pvrf)
cdev->private->state = DEV_STATE_W4SENSE_VERIFY;
/* Another basic sense is needed. */
ccw_device_do_sense(cdev, irb);
return;
}
cdev->private->state = DEV_STATE_ONLINE;
if (irb->esw.esw0.erw.pvrf)
/* Try to start path verification. */
ccw_device_online_verify(cdev, 0);
/* Call the handler. */
ccw_device_call_handler(cdev);
}
......@@ -527,103 +544,68 @@ ccw_device_w4sense_verify(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_call_handler(cdev);
}
/*
* No operation action. This is used e.g. to ignore a timeout event in
* state offline.
*/
static void
ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
{
}
/*
* Bug operation action.
*/
static void
ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
{
printk(KERN_EMERG "dev_jumptable[%i][%i] == NULL\n",
cdev->private->state, dev_event);
BUG();
}
/*
* We've got an interrupt on establish queues. Check for errors and
* accordingly retry or move on.
*/
static void
ccw_device_qdio_init_irq(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
struct subchannel *sch;
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (cdev->private->qdio_data &&
cdev->private->qdio_data->establish_irq)
cdev->private->qdio_data->establish_irq(cdev, 0, irb);
wake_up(&cdev->private->wait_q);
if (cdev->handler)
cdev->handler (cdev, 0, irb);
return;
}
/* Accumulate status. We don't do basic sense. */
ccw_device_accumulate_irb(cdev, irb);
//FIXME: Basic sense?
sch = to_subchannel(cdev->dev.parent);
if (cdev->private->qdio_data && cdev->private->qdio_data->establish_irq)
cdev->private->qdio_data->establish_irq(cdev, sch->u_intparm,
&cdev->private->irb);
wake_up(&cdev->private->wait_q);
/* Try to start delayed device verification. */
ccw_device_online_verify(cdev, 0);
/* Note: Don't call handler for cio initiated clear! */
}
/*
* Run into a timeout after establish queues, retry if needed.
*/
static void
ccw_device_qdio_init_timeout(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
ccw_device_set_timeout(cdev, 0);
if (cdev->private->qdio_data &&
cdev->private->qdio_data->establish_timeout)
cdev->private->qdio_data->establish_timeout(cdev);
wake_up(&cdev->private->wait_q);
/* OK, i/o is dead now. Call interrupt handler. */
cdev->private->state = DEV_STATE_ONLINE;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-ETIMEDOUT));
}
static void
ccw_device_qdio_cleanup_irq(struct ccw_device *cdev,
enum dev_event dev_event)
ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
struct subchannel *sch;
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (cdev->private->qdio_data &&
cdev->private->qdio_data->cleanup_irq)
cdev->private->qdio_data->cleanup_irq(cdev, 0, irb);
wake_up(&cdev->private->wait_q);
if (ccw_device_cancel_halt_clear(cdev) != 0) {
ccw_device_set_timeout(cdev, 3*HZ);
return;
}
ccw_device_accumulate_irb(cdev, irb);
//FIXME: Basic sense?
sch = to_subchannel(cdev->dev.parent);
if (cdev->private->qdio_data && cdev->private->qdio_data->cleanup_irq)
cdev->private->qdio_data->cleanup_irq(cdev, sch->u_intparm,
&cdev->private->irb);
wake_up(&cdev->private->wait_q);
//FIXME: Can we get here?
cdev->private->state = DEV_STATE_ONLINE;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-ETIMEDOUT));
}
/*
* No operation action. This is used e.g. to ignore a timeout event in
* state offline.
*/
static void
ccw_device_qdio_cleanup_timeout(struct ccw_device *cdev,
enum dev_event dev_event)
ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
{
ccw_device_set_timeout(cdev, 0);
if (cdev->private->qdio_data &&
cdev->private->qdio_data->cleanup_timeout)
cdev->private->qdio_data->cleanup_timeout(cdev);
wake_up(&cdev->private->wait_q);
}
/*
* Bug operation action.
*/
static void
ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
{
printk(KERN_EMERG "dev_jumptable[%i][%i] == NULL\n",
cdev->private->state, dev_event);
BUG();
}
/*
......@@ -663,7 +645,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_STATE_ONLINE] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_irq,
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_TIMEOUT] ccw_device_online_timeout,
[DEV_EVENT_VERIFY] ccw_device_online_verify,
},
[DEV_STATE_W4SENSE] {
......@@ -684,25 +666,6 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
/* special states for qdio */
[DEV_STATE_QDIO_INIT] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_qdio_init_irq,
[DEV_EVENT_TIMEOUT] ccw_device_qdio_init_timeout,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
[DEV_STATE_QDIO_ACTIVE] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_irq,
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
[DEV_STATE_QDIO_CLEANUP] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_qdio_cleanup_irq,
[DEV_EVENT_TIMEOUT] ccw_device_qdio_cleanup_timeout,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
/* states to wait for i/o completion before doing something */
[DEV_STATE_ONLINE_VERIFY] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
......@@ -716,6 +679,18 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
[DEV_STATE_CLEAR_VERIFY] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_clear_verify,
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
[DEV_STATE_TIMEOUT_KILL] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_killing_irq,
[DEV_EVENT_TIMEOUT] ccw_device_killing_timeout,
[DEV_EVENT_VERIFY] ccw_device_nop, //FIXME
},
};
/*
......@@ -736,3 +711,4 @@ io_subchannel_irq (struct device *pdev)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
......@@ -198,11 +198,13 @@ __ccw_device_sense_id_start(struct ccw_device *cdev)
/* 0x00E2C9C4 == ebcdic "SID" */
ret = cio_start (sch, cdev->private->iccws,
0x00E2C9C4, cdev->private->imask);
/* ret is 0, -EBUSY or -ENODEV */
if (ret != -EBUSY)
/* ret is 0, -EBUSY, -EACCES or -ENODEV */
if (ret == -EBUSY) {
udelay(100);
continue;
}
if (ret != -EACCES)
return ret;
udelay(100);
continue;
}
cdev->private->imask >>= 1;
cdev->private->iretry = 5;
......
......@@ -49,12 +49,15 @@ ccw_device_clear(struct ccw_device *cdev, unsigned long intparm)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE &&
cdev->private->state != DEV_STATE_QDIO_ACTIVE)
cdev->private->state != DEV_STATE_ONLINE_VERIFY &&
cdev->private->state != DEV_STATE_W4SENSE_VERIFY)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
if (!sch)
return -ENODEV;
ret = cio_clear(sch, intparm);
ret = cio_clear(sch);
if (ret == 0)
cdev->private->intparm = intparm;
return ret;
}
......@@ -67,17 +70,35 @@ ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa,
if (!cdev)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE &&
cdev->private->state != DEV_STATE_QDIO_INIT)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
if (!sch)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE ||
sch->schib.scsw.actl != 0)
return -EBUSY;
ret = cio_set_options (sch, flags);
if (ret)
return ret;
ret = cio_start (sch, cpa, intparm, lpm);
/* 0xe4e2c5d9 == ebcdic "USER" */
ret = cio_start (sch, cpa, 0xe4e2c5d9, lpm);
if (ret == 0)
cdev->private->intparm = intparm;
return ret;
}
int
ccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa,
unsigned long intparm, __u8 lpm, unsigned long flags,
int expires)
{
int ret;
if (!cdev)
return -ENODEV;
ccw_device_set_timeout(cdev, expires);
ret = ccw_device_start(cdev, cpa, intparm, lpm, flags);
if (ret != 0)
ccw_device_set_timeout(cdev, 0);
return ret;
}
......@@ -90,12 +111,16 @@ ccw_device_halt(struct ccw_device *cdev, unsigned long intparm)
if (!cdev)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE)
cdev->private->state != DEV_STATE_W4SENSE &&
cdev->private->state != DEV_STATE_ONLINE_VERIFY &&
cdev->private->state != DEV_STATE_W4SENSE_VERIFY)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
if (!sch)
return -ENODEV;
ret = cio_halt(sch, intparm);
ret = cio_halt(sch);
if (ret == 0)
cdev->private->intparm = intparm;
return ret;
}
......@@ -106,12 +131,12 @@ ccw_device_resume(struct ccw_device *cdev)
if (!cdev)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
if (!sch)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE ||
!(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED))
return -EINVAL;
return cio_resume(sch);
}
......@@ -123,15 +148,6 @@ ccw_device_call_handler(struct ccw_device *cdev)
{
struct subchannel *sch;
unsigned int stctl;
void (*handler)(struct ccw_device *, unsigned long, struct irb *);
if (cdev->private->state == DEV_STATE_QDIO_ACTIVE) {
if (cdev->private->qdio_data)
handler = cdev->private->qdio_data->handler;
else
handler = NULL;
} else
handler = cdev->handler;
sch = to_subchannel(cdev->dev.parent);
......@@ -154,8 +170,9 @@ ccw_device_call_handler(struct ccw_device *cdev)
/*
* Now we are ready to call the device driver interrupt handler.
*/
if (handler)
handler(cdev, sch->u_intparm, &cdev->private->irb);
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
&cdev->private->irb);
/*
* Clear the old and now useless interrupt response block.
......@@ -192,6 +209,11 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
static void
ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
{
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
if (!IS_ERR(irb))
memcpy(&sch->schib.scsw, &irb->scsw, sizeof(struct scsw));
wake_up(&cdev->private->wait_q);
}
......@@ -218,8 +240,7 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
if (!cdev)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE)
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
if (!buffer || !length)
return -EINVAL;
......@@ -251,7 +272,13 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
wait_event(cdev->private->wait_q,
sch->schib.scsw.actl == 0);
spin_lock_irqsave(&sch->lock, flags);
/* FIXME: Check if we got sensible stuff. */
/* Check at least for channel end / device end */
if ((sch->schib.scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(sch->schib.scsw.cstat != 0)) {
ret = -EIO;
continue;
}
break;
}
}
......@@ -281,8 +308,7 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
if (!cdev)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE)
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
if (cdev->private->flags.esid == 0)
return -EOPNOTSUPP;
......@@ -300,7 +326,7 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
if (!ciw || ciw->cmd == 0)
return -EOPNOTSUPP;
rcd_buf = kmalloc(ciw->count, GFP_DMA);
rcd_buf = kmalloc(ciw->count, GFP_KERNEL | GFP_DMA);
if (!rcd_buf)
return -ENOMEM;
memset (rcd_buf, 0, ciw->count);
......@@ -325,7 +351,13 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
spin_unlock_irqrestore(&sch->lock, flags);
wait_event(cdev->private->wait_q, sch->schib.scsw.actl == 0);
spin_lock_irqsave(&sch->lock, flags);
/* FIXME: Check if we got sensible stuff. */
/* Check at least for channel end / device end */
if ((sch->schib.scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(sch->schib.scsw.cstat != 0)) {
ret = -EIO;
continue;
}
break;
}
/* Restore interrupt handler. */
......@@ -363,13 +395,15 @@ _ccw_device_get_device_number(struct ccw_device *cdev)
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_options);
EXPORT_SYMBOL(ccw_device_clear);
EXPORT_SYMBOL(ccw_device_halt);
EXPORT_SYMBOL(ccw_device_resume);
EXPORT_SYMBOL(ccw_device_start_timeout);
EXPORT_SYMBOL(ccw_device_start);
EXPORT_SYMBOL(ccw_device_get_ciw);
EXPORT_SYMBOL(ccw_device_get_path_mask);
EXPORT_SYMBOL (read_conf_data);
EXPORT_SYMBOL (read_dev_chars);
EXPORT_SYMBOL(read_conf_data);
EXPORT_SYMBOL(read_dev_chars);
EXPORT_SYMBOL(_ccw_device_get_subchannel_number);
EXPORT_SYMBOL(_ccw_device_get_device_number);
......@@ -51,14 +51,23 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
/* 0xe2d5c9c4 == ebcdic "SNID" */
ret = cio_start (sch, cdev->private->iccws,
0xE2D5C9C4, cdev->private->imask);
/* ret is 0, -EBUSY or -ENODEV */
if (ret != -EBUSY)
/* ret is 0, -EBUSY, -EACCES or -ENODEV */
if (ret == -EBUSY) {
CIO_MSG_EVENT(2,
"SNID - device %04X, start_io() "
"reports rc : %d, retrying ...\n",
sch->schib.pmcw.dev, ret);
udelay(100);
continue;
}
if (ret != -EACCES)
return ret;
CIO_MSG_EVENT(2, "SNID - device %04X, start_io() "
"reports rc : %d, retrying ...\n",
sch->schib.pmcw.dev, ret);
udelay(100);
continue;
CIO_MSG_EVENT(2, "SNID - Device %04X on Subchannel "
"%04X, lpm %02X, became 'not "
"operational'\n",
sch->schib.pmcw.dev, sch->irq,
cdev->private->imask);
}
cdev->private->imask >>= 1;
cdev->private->iretry = 5;
......@@ -231,7 +240,9 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
/* 0xE2D7C9C4 == ebcdic "SPID" */
ret = cio_start (sch, cdev->private->iccws,
0xE2D7C9C4, cdev->private->imask);
/* ret is 0, -EBUSY or -ENODEV */
/* ret is 0, -EBUSY, -EACCES or -ENODEV */
if (ret == -EACCES)
break;
if (ret != -EBUSY)
return ret;
udelay(100);
......
......@@ -167,7 +167,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
/* Copy authorization bit. */
cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth;
/* Copy path verification required flag. FIXME: how to verify ?? */
/* Copy path verification required flag. */
cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf;
/* Copy concurrent sense bit. */
cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons;
......@@ -309,7 +309,7 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
sch->sense_ccw.flags = CCW_FLAG_SLI;
/* 0xe2C5D5E2 == "SENS" in ebcdic */
return cio_start (sch, &sch->sense_ccw, 0xE2C5D5E2, 0);
return cio_start (sch, &sch->sense_ccw, 0xE2C5D5E2, 0xff);
}
/*
......
......@@ -34,7 +34,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
......@@ -54,8 +53,9 @@
#include "airq.h"
#include "qdio.h"
#include "ioasm.h"
#include "chsc.h"
#define VERSION_QDIO_C "$Revision: 1.34 $"
#define VERSION_QDIO_C "$Revision: 1.48 $"
/****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
......@@ -87,7 +87,6 @@ static debug_info_t *qdio_dbf_slsb_out;
static debug_info_t *qdio_dbf_slsb_in;
#endif /* QDIO_DBF_LIKE_HELL */
static struct qdio_chsc_area *chsc_area;
/* iQDIO stuff: */
static volatile struct qdio_q *iq_list=NULL; /* volatile as it could change
during a while loop */
......@@ -611,16 +610,13 @@ inline static int
iqdio_is_inbound_q_done(struct qdio_q *q)
{
int no_used;
#ifdef QDIO_DBF_LIKE_HELL
char dbf_text[15];
#endif /* QDIO_DBF_LIKE_HELL */
no_used=atomic_read(&q->number_of_buffers_used);
/* propagate the change from 82 to 80 through VM */
SYNC_MEMORY;
#ifdef QDIO_DBF_LIKE_HELL
if (no_used) {
sprintf(dbf_text,"iqisnt%02x",no_used);
QDIO_DBF_TEXT4(0,trace,dbf_text);
......@@ -628,7 +624,6 @@ iqdio_is_inbound_q_done(struct qdio_q *q)
QDIO_DBF_TEXT4(0,trace,"iniqisdo");
}
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
#endif /* QDIO_DBF_LIKE_HELL */
if (!no_used)
return 1;
......@@ -664,9 +659,7 @@ inline static int
qdio_is_inbound_q_done(struct qdio_q *q)
{
int no_used;
#ifdef QDIO_DBF_LIKE_HELL
char dbf_text[15];
#endif /* QDIO_DBF_LIKE_HELL */
no_used=atomic_read(&q->number_of_buffers_used);
......@@ -677,11 +670,9 @@ qdio_is_inbound_q_done(struct qdio_q *q)
SYNC_MEMORY;
if (!no_used) {
#ifdef QDIO_DBF_LIKE_HELL
QDIO_DBF_TEXT4(0,trace,"inqisdnA");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
QDIO_DBF_TEXT4(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
return 1;
}
......@@ -703,20 +694,16 @@ qdio_is_inbound_q_done(struct qdio_q *q)
* has (probably) not moved (see qdio_inbound_processing)
*/
if (NOW>GET_SAVED_TIMESTAMP(q)+q->timing.threshold) {
#ifdef QDIO_DBF_LIKE_HELL
QDIO_DBF_TEXT4(0,trace,"inqisdon");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used);
QDIO_DBF_TEXT4(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
return 1;
} else {
#ifdef QDIO_DBF_LIKE_HELL
QDIO_DBF_TEXT4(0,trace,"inqisntd");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used);
QDIO_DBF_TEXT4(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
return 0;
}
}
......@@ -725,12 +712,10 @@ inline static void
qdio_kick_inbound_handler(struct qdio_q *q)
{
int count, start, end, real_end, i;
#ifdef QDIO_DBF_LIKE_HELL
char dbf_text[15];
QDIO_DBF_TEXT4(0,trace,"kickinh");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
#endif /* QDIO_DBF_LIKE_HELL */
start=q->first_element_to_kick;
real_end=q->first_to_check;
......@@ -744,10 +729,8 @@ qdio_kick_inbound_handler(struct qdio_q *q)
i=(i+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
}
#ifdef QDIO_DBF_LIKE_HELL
sprintf(dbf_text,"s=%2xc=%2x",start,count);
QDIO_DBF_TEXT4(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
if (q->state==QDIO_IRQ_STATE_ACTIVE)
q->handler(q->cdev,
......@@ -950,13 +933,10 @@ inline static int
qdio_is_outbound_q_done(struct qdio_q *q)
{
int no_used;
#ifdef QDIO_DBF_LIKE_HELL
char dbf_text[15];
#endif /* QDIO_DBF_LIKE_HELL */
no_used=atomic_read(&q->number_of_buffers_used);
#ifdef QDIO_DBF_LIKE_HELL
if (no_used) {
sprintf(dbf_text,"oqisnt%02x",no_used);
QDIO_DBF_TEXT4(0,trace,dbf_text);
......@@ -964,7 +944,6 @@ qdio_is_outbound_q_done(struct qdio_q *q)
QDIO_DBF_TEXT4(0,trace,"oqisdone");
}
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
#endif /* QDIO_DBF_LIKE_HELL */
return (no_used==0);
}
......@@ -1015,10 +994,7 @@ inline static void
qdio_kick_outbound_handler(struct qdio_q *q)
{
int start, end, real_end, count;
#ifdef QDIO_DBF_LIKE_HELL
char dbf_text[15];
#endif /* QDIO_DBF_LIKE_HELL */
start = q->first_element_to_kick;
/* last_move_ftc was just updated */
......@@ -1031,10 +1007,8 @@ qdio_kick_outbound_handler(struct qdio_q *q)
QDIO_DBF_TEXT4(0,trace,"kickouth");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
#ifdef QDIO_DBF_LIKE_HELL
sprintf(dbf_text,"s=%2xc=%2x",start,count);
QDIO_DBF_TEXT4(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
if (q->state==QDIO_IRQ_STATE_ACTIVE)
q->handler(q->cdev,QDIO_STATUS_OUTBOUND_INT|
......@@ -1239,8 +1213,6 @@ qdio_release_irq_memory(struct qdio_irq *irq_ptr)
if (irq_ptr->qdr)
kfree(irq_ptr->qdr);
kfree(irq_ptr);
QDIO_DBF_TEXT3(0,setup,"MOD_DEC_");
MOD_DEC_USE_COUNT;
}
static void
......@@ -1482,7 +1454,7 @@ qdio_fill_thresholds(struct qdio_irq *irq_ptr,
}
static int
iqdio_thinint_handler(__u32 intparm)
iqdio_thinint_handler(void)
{
QDIO_DBF_TEXT4(0,trace,"thin_int");
......@@ -1500,7 +1472,7 @@ iqdio_thinint_handler(__u32 intparm)
}
static void
qdio_set_state(struct qdio_irq *irq_ptr,int state)
qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state)
{
int i;
char dbf_text[15];
......@@ -1570,23 +1542,90 @@ qdio_handle_pci(struct qdio_irq *irq_ptr)
}
}
static void qdio_establish_handle_irq(struct ccw_device*, int, int);
static inline void
qdio_handle_activate_check(struct ccw_device *cdev, unsigned long intparm,
int cstat, int dstat)
{
struct qdio_irq *irq_ptr;
struct qdio_q *q;
char dbf_text[15];
irq_ptr = cdev->private->qdio_data;
QDIO_DBF_TEXT2(1, trace, "ick2");
sprintf(dbf_text,"%s", cdev->dev.bus_id);
QDIO_DBF_TEXT2(1,trace,dbf_text);
QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int));
QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
QDIO_PRINT_ERR("received check condition on activate " \
"queues on device %s (cs=x%x, ds=x%x).\n",
cdev->dev.bus_id, cstat, dstat);
if (irq_ptr->no_input_qs) {
q=irq_ptr->input_qs[0];
} else if (irq_ptr->no_output_qs) {
q=irq_ptr->output_qs[0];
} else {
QDIO_PRINT_ERR("oops... no queue registered for device %s!?\n",
cdev->dev.bus_id);
goto omit_handler_call;
}
q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
QDIO_STATUS_LOOK_FOR_ERROR,
0,0,0,-1,-1,q->int_parm);
omit_handler_call:
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED);
}
static void
qdio_timeout_handler(struct ccw_device *cdev)
{
struct qdio_irq *irq_ptr;
char dbf_text[15];
QDIO_DBF_TEXT2(0, trace, "qtoh");
sprintf(dbf_text, "%s", cdev->dev.bus_id);
QDIO_DBF_TEXT2(0, trace, dbf_text);
irq_ptr = cdev->private->qdio_data;
sprintf(dbf_text, "state:%d", irq_ptr->state);
QDIO_DBF_TEXT2(0, trace, dbf_text);
switch (irq_ptr->state) {
case QDIO_IRQ_STATE_INACTIVE:
QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n",
irq_ptr->irq);
QDIO_DBF_TEXT2(1,setup,"eq:timeo");
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
break;
case QDIO_IRQ_STATE_CLEANUP:
QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n",
irq_ptr->irq);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
break;
default:
BUG();
}
wake_up(&cdev->private->wait_q);
}
static void
qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
{
struct qdio_irq *irq_ptr;
struct qdio_q *q;
int cstat,dstat;
char dbf_text[15];
cstat = irb->scsw.cstat;
dstat = irb->scsw.dstat;
QDIO_DBF_TEXT4(0, trace, "qint");
sprintf(dbf_text, "%s", cdev->dev.bus_id);
QDIO_DBF_TEXT4(0, trace, dbf_text);
if (!intparm || !cdev) {
QDIO_PRINT_STUPID("got unsolicited interrupt in qdio " \
if (!intparm) {
QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \
"handler, device %s\n", cdev->dev.bus_id);
return;
}
......@@ -1601,39 +1640,58 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
return;
}
if (IS_ERR(irb)) {
/* Currently running i/o is in error. */
switch (PTR_ERR(irb)) {
case -EIO:
QDIO_PRINT_ERR("i/o error on device %s\n",
cdev->dev.bus_id);
//FIXME: hm?
return;
case -ETIMEDOUT:
qdio_timeout_handler(cdev);
return;
default:
QDIO_PRINT_ERR("unknown error state %ld on device %s\n",
PTR_ERR(irb), cdev->dev.bus_id);
return;
}
}
qdio_irq_check_sense(irq_ptr->irq, irb);
if (cstat & SCHN_STAT_PCI) {
qdio_handle_pci(irq_ptr);
return;
}
sprintf(dbf_text, "state:%d", irq_ptr->state);
QDIO_DBF_TEXT4(0, trace, dbf_text);
if ((cstat&~SCHN_STAT_PCI)||dstat) {
QDIO_DBF_TEXT2(1, trace, "ick2");
sprintf(dbf_text,"%s", cdev->dev.bus_id);
QDIO_DBF_TEXT2(1,trace,dbf_text);
QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int));
QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
QDIO_PRINT_ERR("received check condition on activate " \
"queues on device %s (cs=x%x, ds=x%x).\n",
cdev->dev.bus_id, cstat, dstat);
if (irq_ptr->no_input_qs) {
q=irq_ptr->input_qs[0];
} else if (irq_ptr->no_output_qs) {
q=irq_ptr->output_qs[0];
} else {
QDIO_PRINT_ERR("oops... no queue registered for " \
"device %s!?\n", cdev->dev.bus_id);
goto omit_handler_call;
cstat = irb->scsw.cstat;
dstat = irb->scsw.dstat;
switch (irq_ptr->state) {
case QDIO_IRQ_STATE_INACTIVE:
qdio_establish_handle_irq(cdev, cstat, dstat);
break;
case QDIO_IRQ_STATE_CLEANUP:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
break;
case QDIO_IRQ_STATE_ESTABLISHED:
case QDIO_IRQ_STATE_ACTIVE:
if (cstat & SCHN_STAT_PCI) {
qdio_handle_pci(irq_ptr);
break;
}
q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
QDIO_STATUS_LOOK_FOR_ERROR,
0,0,0,-1,-1,q->int_parm);
omit_handler_call:
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED);
return;
if ((cstat&~SCHN_STAT_PCI)||dstat) {
qdio_handle_activate_check(cdev, intparm, cstat, dstat);
break;
}
default:
QDIO_PRINT_ERR("got interrupt for queues in state %d on " \
"device %s?!\n",
irq_ptr->state, cdev->dev.bus_id);
}
wake_up(&cdev->private->wait_q);
}
......@@ -1680,60 +1738,108 @@ qdio_synchronize(struct ccw_device *cdev, unsigned int flags,
static unsigned char
qdio_check_siga_needs(int sch)
{
int resp_code,result;
int result;
unsigned char qdioac;
struct {
struct chsc_header request;
u16 reserved1;
u16 first_sch;
u16 reserved2;
u16 last_sch;
u32 reserved3;
struct chsc_header response;
u32 reserved4;
u8 flags;
u8 reserved5;
u16 sch;
u8 qfmt;
u8 reserved6;
u8 qdioac;
u8 sch_class;
u8 reserved7;
u8 icnt;
u8 reserved8;
u8 ocnt;
} *ssqd_area;
ssqd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!ssqd_area) {
QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \
"SIGAs for sch x%x.\n", sch);
return -1; /* all flags set */
}
ssqd_area->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x0024,
};
memset(chsc_area,0,sizeof(struct qdio_chsc_area));
chsc_area->request_block.command_code1=0x0010; /* length */
chsc_area->request_block.command_code2=0x0024; /* op code */
chsc_area->request_block.first_sch=sch;
chsc_area->request_block.last_sch=sch;
ssqd_area->first_sch = sch;
ssqd_area->last_sch = sch;
result=chsc(chsc_area);
result=chsc(ssqd_area);
if (result) {
QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
"SIGAs for sch x%x.\n",
result,sch);
return -1; /* all flags set */
qdioac = -1; /* all flags set */
goto out;
}
resp_code=chsc_area->request_block.operation_data_area.
store_qdio_data_response.response_code;
if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
QDIO_PRINT_WARN("response upon checking SIGA needs " \
"is 0x%x. Using all SIGAs for sch x%x.\n",
resp_code,sch);
return -1; /* all flags set */
ssqd_area->response.code, sch);
qdioac = -1; /* all flags set */
goto out;
}
if (
(!(chsc_area->request_block.operation_data_area.
store_qdio_data_response.flags&CHSC_FLAG_QDIO_CAPABILITY)) ||
(!(chsc_area->request_block.operation_data_area.
store_qdio_data_response.flags&CHSC_FLAG_VALIDITY)) ||
(chsc_area->request_block.operation_data_area.
store_qdio_data_response.sch!=sch)
) {
if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
!(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||
(ssqd_area->sch != sch)) {
QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \
"using all SIGAs.\n",sch);
return CHSC_FLAG_SIGA_INPUT_NECESSARY |
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
goto out;
}
return chsc_area->request_block.operation_data_area.
store_qdio_data_response.qdioac;
qdioac = ssqd_area->qdioac;
out:
free_page ((unsigned long) ssqd_area);
return qdioac;
}
/* the chsc_area is locked by the lock in qdio_activate */
static unsigned int
iqdio_check_chsc_availability(void) {
iqdio_check_chsc_availability(void)
{
int result;
int i;
memset(chsc_area,0,sizeof(struct qdio_chsc_area));
chsc_area->request_block.command_code1=0x0010;
chsc_area->request_block.command_code2=0x0010;
result=chsc(chsc_area);
struct {
struct chsc_header request;
u32 reserved1;
u32 reserved2;
u32 reserved3;
struct chsc_header response;
u32 reserved4;
u32 general_char[510];
u32 chsc_char[518];
} *scsc_area;
scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scsc_area) {
QDIO_PRINT_WARN("Was not able to determine available" \
"CHSCs due to no memory.\n");
return -ENOMEM;
}
scsc_area->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x0010,
};
result=chsc(scsc_area);
if (result) {
QDIO_PRINT_WARN("Was not able to determine " \
"available CHSCs, cc=%i.\n",
......@@ -1741,10 +1847,8 @@ iqdio_check_chsc_availability(void) {
result=-EIO;
goto exit;
}
result=0;
i=chsc_area->request_block.operation_data_area.
store_qdio_data_response.response_code;
if (i!=1) {
if (scsc_area->response.code != 1) {
QDIO_PRINT_WARN("Was not able to determine " \
"available CHSCs.\n");
result=-EIO;
......@@ -1753,24 +1857,24 @@ iqdio_check_chsc_availability(void) {
/* 4: request block
* 2: general char
* 512: chsc char */
if ( (*(((unsigned int*)(chsc_area))+4+2+1)&0x00800000)!=0x00800000) {
if ((scsc_area->general_char[1] & 0x00800000) != 0x00800000) {
QDIO_PRINT_WARN("Adapter interruption facility not " \
"installed.\n");
result=-ENOENT;
goto exit;
}
if ( (*(((unsigned int*)(chsc_area))+4+512+3)&0x00180000)!=
0x00180000 ) {
if ((scsc_area->chsc_char[2] & 0x00180000) != 0x00180000) {
QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \
"not available.\n");
result=-ENOENT;
goto exit;
}
exit:
free_page ((unsigned long) scsc_area);
return result;
}
/* the chsc_area is locked by the lock in qdio_activate */
static unsigned int
iqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
{
......@@ -1782,7 +1886,27 @@ iqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
unsigned int resp_code;
int result;
if (!irq_ptr->is_iqdio_irq) return -ENODEV;
struct {
struct chsc_header request;
u16 operation_code;
u16 reserved1;
u32 reserved2;
u32 reserved3;
u64 summary_indicator_addr;
u64 subchannel_indicator_addr;
u32 ks:4;
u32 kc:4;
u32 reserved4:21;
u32 isc:3;
u32 reserved5[2];
u32 subsystem_id;
u32 reserved6[1004];
struct chsc_header response;
u32 reserved7;
} *scssc_area;
if (!irq_ptr->is_iqdio_irq)
return -ENODEV;
if (reset_to_zero) {
real_addr_local_summary_bit=0;
......@@ -1794,52 +1918,57 @@ iqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
}
memset(chsc_area,0,sizeof(struct qdio_chsc_area));
chsc_area->request_block.command_code1=0x0fe0;
chsc_area->request_block.command_code2=0x0021;
chsc_area->request_block.operation_code=0;
chsc_area->request_block.image_id=0;
chsc_area->request_block.operation_data_area.set_chsc.
summary_indicator_addr=real_addr_local_summary_bit;
chsc_area->request_block.operation_data_area.set_chsc.
subchannel_indicator_addr=real_addr_dev_st_chg_ind;
chsc_area->request_block.operation_data_area.set_chsc.
ks=QDIO_STORAGE_KEY;
chsc_area->request_block.operation_data_area.set_chsc.
kc=QDIO_STORAGE_KEY;
chsc_area->request_block.operation_data_area.set_chsc.
isc=IQDIO_THININT_ISC;
chsc_area->request_block.operation_data_area.set_chsc.
subsystem_id=(1<<16)+irq_ptr->irq;
result=chsc(chsc_area);
scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scssc_area) {
QDIO_PRINT_WARN("No memory for setting indicators on " \
"subchannel x%x.\n", irq_ptr->irq);
return -ENOMEM;
}
scssc_area->request = (struct chsc_header) {
.length = 0x0fe0,
.code = 0x0021,
};
scssc_area->operation_code = 0;
scssc_area->summary_indicator_addr = real_addr_local_summary_bit;
scssc_area->subchannel_indicator_addr = real_addr_dev_st_chg_ind;
scssc_area->ks = QDIO_STORAGE_KEY;
scssc_area->kc = QDIO_STORAGE_KEY;
scssc_area->isc = IQDIO_THININT_ISC;
scssc_area->subsystem_id = (1<<16) + irq_ptr->irq;
result = chsc(scssc_area);
if (result) {
QDIO_PRINT_WARN("could not set indicators on irq x%x, " \
"cc=%i.\n",irq_ptr->irq,result);
return -EIO;
result = -EIO;
goto out;
}
resp_code=chsc_area->response_block.response_code;
resp_code = scssc_area->response.code;
if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
QDIO_PRINT_WARN("response upon setting indicators " \
"is 0x%x.\n",resp_code);
sprintf(dbf_text,"sidR%4x",resp_code);
QDIO_DBF_TEXT1(0,trace,dbf_text);
QDIO_DBF_TEXT1(0,setup,dbf_text);
ptr=&chsc_area->response_block;
ptr=&scssc_area->response;
QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN);
return -EIO;
result = -EIO;
goto out;
}
QDIO_DBF_TEXT2(0,setup,"setscind");
QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit,
sizeof(unsigned long));
QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long));
return 0;
result = 0;
out:
free_page ((unsigned long) scssc_area);
return result;
}
/* chsc_area would have to be locked if called from outside qdio_activate */
static unsigned int
iqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target)
{
......@@ -1848,34 +1977,59 @@ iqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target)
void *ptr;
char dbf_text[15];
if (!irq_ptr->is_iqdio_irq) return -ENODEV;
struct {
struct chsc_header request;
u16 operation_code;
u16 reserved1;
u32 reserved2;
u32 reserved3;
u32 reserved4[2];
u32 delay_target;
u32 reserved5[1009];
struct chsc_header response;
u32 reserved6;
} *scsscf_area;
if (!irq_ptr->is_iqdio_irq)
return -ENODEV;
memset(chsc_area,0,sizeof(struct qdio_chsc_area));
chsc_area->request_block.command_code1=0x0fe0;
chsc_area->request_block.command_code2=0x1027;
chsc_area->request_block.operation_data_area.set_chsc_fast.
delay_target=delay_target<<16;
scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scsscf_area) {
QDIO_PRINT_WARN("No memory for setting delay target on " \
"subchannel x%x.\n", irq_ptr->irq);
return -ENOMEM;
}
scsscf_area->request = (struct chsc_header) {
.length = 0x0fe0,
.code = 0x1027,
};
result=chsc(chsc_area);
scsscf_area->delay_target = delay_target<<16;
result=chsc(scsscf_area);
if (result) {
QDIO_PRINT_WARN("could not set delay target on irq x%x, " \
"cc=%i. Continuing.\n",irq_ptr->irq,result);
return -EIO;
result = -EIO;
goto out;
}
resp_code=chsc_area->response_block.response_code;
resp_code = scsscf_area->response.code;
if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
QDIO_PRINT_WARN("response upon setting delay target " \
"is 0x%x. Continuing.\n",resp_code);
sprintf(dbf_text,"sdtR%4x",resp_code);
QDIO_DBF_TEXT1(0,trace,dbf_text);
QDIO_DBF_TEXT1(0,setup,dbf_text);
ptr=&chsc_area->response_block;
ptr=&scsscf_area->response;
QDIO_DBF_HEX2(1,trace,&ptr,QDIO_DBF_TRACE_LEN);
}
QDIO_DBF_TEXT2(0,trace,"delytrgt");
QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long));
return 0;
result = 0; /* not critical */
out:
free_page ((unsigned long) scsscf_area);
return result;
}
int
......@@ -1972,19 +2126,22 @@ qdio_shutdown(struct ccw_device *cdev, int how)
ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
timeout=QDIO_CLEANUP_HALT_TIMEOUT;
}
cdev->private->state = DEV_STATE_QDIO_CLEANUP;
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP);
ccw_device_set_timeout(cdev, timeout);
spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
wait_event(cdev->private->wait_q,
irq_ptr->state == QDIO_IRQ_STATE_INACTIVE ||
irq_ptr->state == QDIO_IRQ_STATE_ERR);
/* Ignore errors. */
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
out:
up(&irq_ptr->setting_up_sema);
return result;
}
static inline void
qdio_cleanup_finish(struct qdio_irq *irq_ptr)
qdio_cleanup_finish(struct ccw_device *cdev, struct qdio_irq *irq_ptr)
{
if (irq_ptr->is_iqdio_irq) {
qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind);
......@@ -1992,6 +2149,10 @@ qdio_cleanup_finish(struct qdio_irq *irq_ptr)
/* reset adapter interrupt indicators */
}
/* exchange int handlers, if necessary */
if ((void*)cdev->handler == (void*)qdio_handler)
cdev->handler=irq_ptr->original_int_handler;
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
}
......@@ -2014,51 +2175,16 @@ qdio_free(struct ccw_device *cdev)
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
qdio_cleanup_finish(irq_ptr);
qdio_cleanup_finish(cdev, irq_ptr);
cdev->private->qdio_data = 0;
up(&irq_ptr->setting_up_sema);
qdio_release_irq_memory(irq_ptr);
module_put(THIS_MODULE);
return 0;
}
static void
qdio_cleanup_handle_timeout(struct ccw_device *cdev)
{
unsigned long flags;
struct qdio_irq *irq_ptr;
irq_ptr = cdev->private->qdio_data;
spin_lock_irqsave(get_ccwdev_lock(cdev),flags);
QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n",
irq_ptr->irq);
spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags);
cdev->private->state = DEV_STATE_ONLINE;
wake_up(&cdev->private->wait_q);
}
static void
qdio_cleanup_handle_irq(struct ccw_device *cdev, unsigned long intparm,
struct irb *irb)
{
struct qdio_irq *irq_ptr;
if (intparm == 0)
QDIO_PRINT_WARN("Got unsolicited interrupt on cleanup "
"(irq 0x%x).\n", cdev->private->irq);
irq_ptr = cdev->private->qdio_data;
qdio_irq_check_sense(irq_ptr->irq, irb);
cdev->private->state = DEV_STATE_ONLINE;
wake_up(&cdev->private->wait_q);
}
static inline void
qdio_allocate_do_dbf(struct qdio_initialize *init_data)
{
......@@ -2134,24 +2260,6 @@ qdio_allocate_fill_output_desc(struct qdio_irq *irq_ptr, int i,
irq_ptr->qdr->qdf0[i+j].dkey=QDIO_STORAGE_KEY;
}
void
qdio_establish_handle_timeout(struct ccw_device *cdev)
{
struct qdio_irq *irq_ptr;
irq_ptr = cdev->private->qdio_data;
QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n",
irq_ptr->irq);
QDIO_DBF_TEXT2(1,setup,"eq:timeo");
/*
* FIXME:
* this is broken,
* we are in the context of a timer interrupt and
* qdio_shutdown calls schedule
*/
qdio_shutdown(cdev,QDIO_FLAG_CLEANUP_USING_CLEAR);
}
static inline void
qdio_initialize_set_siga_flags_input(struct qdio_irq *irq_ptr)
......@@ -2208,7 +2316,7 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
QDIO_PRINT_ERR("received check condition on establish " \
"queues on irq 0x%x (cs=x%x, ds=x%x).\n",
irq_ptr->irq,cstat,dstat);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR);
}
if (!(dstat & DEV_STAT_DEV_END)) {
......@@ -2218,13 +2326,7 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
QDIO_PRINT_ERR("establish queues on irq %04x: didn't get "
"device end: dstat=%02x, cstat=%02x\n",
irq_ptr->irq, dstat, cstat);
/*
* FIXME:
* this is broken,
* we are probably in the context of an i/o interrupt and
* qdio_shutdown calls schedule
*/
qdio_shutdown(cdev,QDIO_FLAG_CLEANUP_USING_CLEAR);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
}
......@@ -2236,36 +2338,27 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
"the following devstat: dstat=%02x, "
"cstat=%02x\n",
irq_ptr->irq, dstat, cstat);
cdev->private->state = DEV_STATE_ONLINE;
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
}
return 0;
}
static void
qdio_establish_handle_irq(struct ccw_device *cdev, unsigned long intparm,
struct irb *irb)
qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat)
{
struct qdio_irq *irq_ptr;
int cstat, dstat;
char dbf_text[15];
cstat = irb->scsw.cstat;
dstat = irb->scsw.dstat;
irq_ptr = cdev->private->qdio_data;
if (intparm == 0) {
QDIO_PRINT_WARN("Got unsolicited interrupt on establish "
"queues (irq 0x%x).\n", cdev->private->irq);
return;
}
qdio_irq_check_sense(irq_ptr->irq, irb);
sprintf(dbf_text,"qehi%4x",cdev->private->irq);
QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,trace,dbf_text);
if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat))
return;
irq_ptr = cdev->private->qdio_data;
if (MACHINE_IS_VM)
irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq);
else
......@@ -2287,6 +2380,7 @@ qdio_establish_handle_irq(struct ccw_device *cdev, unsigned long intparm,
qdio_initialize_set_siga_flags_output(irq_ptr);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
}
int
......@@ -2334,7 +2428,7 @@ qdio_allocate(struct qdio_initialize *init_data)
qdio_allocate_do_dbf(init_data);
/* create irq */
irq_ptr=kmalloc(sizeof(struct qdio_irq),GFP_DMA);
irq_ptr=kmalloc(sizeof(struct qdio_irq), GFP_KERNEL | GFP_DMA);
QDIO_DBF_TEXT0(0,setup,"irq_ptr:");
QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*));
......@@ -2347,7 +2441,7 @@ qdio_allocate(struct qdio_initialize *init_data)
memset(irq_ptr,0,sizeof(struct qdio_irq));
/* wipes qib.ac, required by ar7063 */
irq_ptr->qdr=kmalloc(sizeof(struct qdr),GFP_DMA);
irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA);
if (!(irq_ptr->qdr)) {
kfree(irq_ptr->qdr);
kfree(irq_ptr);
......@@ -2372,11 +2466,6 @@ qdio_allocate(struct qdio_initialize *init_data)
if (!irq_ptr->dev_st_chg_ind) {
QDIO_PRINT_WARN("no indicator location available " \
"for irq 0x%x\n",irq_ptr->irq);
/*
* FIXME:
* qdio_release_irq_memory does MOD_DEC_USE_COUNT
* in an unbalanced fashion (see 30 lines farther down)
*/
qdio_release_irq_memory(irq_ptr);
return -ENOBUFS;
}
......@@ -2396,19 +2485,17 @@ qdio_allocate(struct qdio_initialize *init_data)
init_data->q_format,init_data->flags,
init_data->input_sbal_addr_array,
init_data->output_sbal_addr_array)) {
/*
* FIXME:
* qdio_release_irq_memory does MOD_DEC_USE_COUNT
* in an unbalanced fashion (see 10 lines farther down)
*/
qdio_release_irq_memory(irq_ptr);
return -ENOMEM;
}
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
MOD_INC_USE_COUNT;
QDIO_DBF_TEXT3(0,setup,"MOD_INC_");
if (!try_module_get(THIS_MODULE)) {
QDIO_PRINT_CRIT("try_module_get() failed!\n");
qdio_release_irq_memory(irq_ptr);
return -EINVAL;
}
init_MUTEX_LOCKED(&irq_ptr->setting_up_sema);
......@@ -2474,6 +2561,10 @@ qdio_allocate(struct qdio_initialize *init_data)
} else
irq_ptr->aqueue = *ciw;
/* Set new interrupt handler. */
irq_ptr->original_int_handler = init_data->cdev->handler;
init_data->cdev->handler = qdio_handler;
/* the iqdio CHSC stuff */
if (irq_ptr->is_iqdio_irq) {
/* iqdio_enable_adapter_int_facility(irq_ptr);*/
......@@ -2485,25 +2576,12 @@ qdio_allocate(struct qdio_initialize *init_data)
result=iqdio_set_subchannel_ind(irq_ptr,0);
if (result) {
up(&irq_ptr->setting_up_sema);
/*
* FIXME:
* need some callback pointers to be set already,
* i.e. irq_ptr->cleanup_irq and irq_ptr->cleanup_timeout?
* (see 10 lines farther down)
*/
qdio_cleanup(init_data->cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
return result;
}
iqdio_set_delay_target(irq_ptr,IQDIO_DELAY_TARGET);
}
/* Set callback functions. */
irq_ptr->cleanup_irq = qdio_cleanup_handle_irq;
irq_ptr->cleanup_timeout = qdio_cleanup_handle_timeout;
irq_ptr->establish_irq = qdio_establish_handle_irq;
irq_ptr->establish_timeout = qdio_establish_handle_timeout;
irq_ptr->handler = qdio_handler;
up(&irq_ptr->setting_up_sema);
return 0;
......@@ -2538,13 +2616,14 @@ qdio_establish(struct ccw_device *cdev)
spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags);
ccw_device_set_timeout(cdev, QDIO_ESTABLISH_TIMEOUT);
ccw_device_set_options(cdev, 0);
result=ccw_device_start(cdev,&irq_ptr->ccw,
QDIO_DOING_ESTABLISH,0,0);
result=ccw_device_start_timeout(cdev,&irq_ptr->ccw,
QDIO_DOING_ESTABLISH,0,0,
QDIO_ESTABLISH_TIMEOUT);
if (result) {
result2=ccw_device_start(cdev,&irq_ptr->ccw,
QDIO_DOING_ESTABLISH,0,0);
result2=ccw_device_start_timeout(cdev,&irq_ptr->ccw,
QDIO_DOING_ESTABLISH,0,0,
QDIO_ESTABLISH_TIMEOUT);
sprintf(dbf_text,"eq:io%4x",result);
QDIO_DBF_TEXT2(1,setup,dbf_text);
if (result2) {
......@@ -2556,8 +2635,7 @@ qdio_establish(struct ccw_device *cdev)
irq_ptr->irq,result,result2);
result=result2;
}
if (result == 0)
cdev->private->state = DEV_STATE_QDIO_INIT;
spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags);
if (result) {
......@@ -2567,13 +2645,15 @@ qdio_establish(struct ccw_device *cdev)
}
wait_event(cdev->private->wait_q,
dev_fsm_final_state(cdev) ||
(irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED));
irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED ||
irq_ptr->state == QDIO_IRQ_STATE_ERR);
if (cdev->private->state == DEV_STATE_QDIO_INIT)
if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED)
result = 0;
else
else {
qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
result = -EIO;
}
up(&irq_ptr->setting_up_sema);
......@@ -2593,7 +2673,7 @@ qdio_activate(struct ccw_device *cdev, int flags)
if (!irq_ptr)
return -ENODEV;
if (cdev->private->state != DEV_STATE_QDIO_INIT)
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
down(&irq_ptr->setting_up_sema);
......@@ -2637,8 +2717,6 @@ qdio_activate(struct ccw_device *cdev, int flags)
if (result)
goto out;
cdev->private->state = DEV_STATE_QDIO_ACTIVE;
for (i=0;i<irq_ptr->no_input_qs;i++) {
if (irq_ptr->is_iqdio_irq) {
/*
......@@ -2659,9 +2737,9 @@ qdio_activate(struct ccw_device *cdev, int flags)
}
}
qdio_wait_nonbusy(QDIO_ACTIVATE_DELAY);
qdio_wait_nonbusy(QDIO_ACTIVATE_TIMEOUT);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ACTIVE);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
out:
up(&irq_ptr->setting_up_sema);
......@@ -2807,12 +2885,10 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags,
{
struct qdio_irq *irq_ptr;
#ifdef QDIO_DBF_LIKE_HELL
char dbf_text[20];
sprintf(dbf_text,"doQD%04x",irq);
sprintf(dbf_text,"doQD%04x",cdev->private->irq);
QDIO_DBF_TEXT3(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
if ( (qidx>QDIO_MAX_BUFFERS_PER_Q) ||
(count>QDIO_MAX_BUFFERS_PER_Q) ||
......@@ -2826,7 +2902,6 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags,
if (!irq_ptr)
return -ENODEV;
#ifdef QDIO_DBF_LIKE_HELL
if (callflags&QDIO_FLAG_SYNC_INPUT)
QDIO_DBF_HEX3(0,trace,&irq_ptr->input_qs[queue_number],
sizeof(void*));
......@@ -2837,7 +2912,6 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags,
QDIO_DBF_TEXT3(0,trace,dbf_text);
sprintf(dbf_text,"qi%02xct%02x",qidx,count);
QDIO_DBF_TEXT3(0,trace,dbf_text);
#endif /* QDIO_DBF_LIKE_HELL */
if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE)
return -EBUSY;
......@@ -2989,25 +3063,12 @@ qdio_get_qdio_memory(void)
GFP_KERNEL);
if (!indicators) return -ENOMEM;
memset(indicators,0,sizeof(__u32)*(INDICATORS_PER_CACHELINE));
chsc_area=(struct qdio_chsc_area *)
kmalloc(sizeof(struct qdio_chsc_area),GFP_KERNEL);
QDIO_DBF_TEXT3(0,trace,"chscarea"); \
QDIO_DBF_HEX3(0,trace,&chsc_area,sizeof(void*)); \
if (!chsc_area) {
QDIO_PRINT_ERR("not enough memory for chsc area. Cannot " \
"initialize QDIO.\n");
kfree(indicators);
return -ENOMEM;
}
memset(chsc_area,0,sizeof(struct qdio_chsc_area));
return 0;
}
static void
qdio_release_qdio_memory(void)
{
kfree(chsc_area);
if (indicators)
kfree(indicators);
}
......
#ifndef _CIO_QDIO_H
#define _CIO_QDIO_H
#define VERSION_CIO_QDIO_H "$Revision: 1.11 $"
#define VERSION_CIO_QDIO_H "$Revision: 1.16 $"
//#define QDIO_DBF_LIKE_HELL
......@@ -48,25 +48,25 @@
#define QDIO_STATS_CLASSES 2
#define QDIO_STATS_COUNT_NEEDED 2*/
#define QDIO_ACTIVATE_DELAY 5 /* according to brenton belmar and paul
gioquindo it can take up to 5ms before
queues are really active */
#define QDIO_NO_USE_COUNT_TIME 10
#define QDIO_NO_USE_COUNT_TIMEOUT 1000 /* wait for 1 sec on each q before
exiting without having use_count
of the queue to 0 */
#define QDIO_ESTABLISH_TIMEOUT 1000
#define QDIO_ACTIVATE_TIMEOUT 100
#define QDIO_ACTIVATE_TIMEOUT 5
#define QDIO_CLEANUP_CLEAR_TIMEOUT 20000
#define QDIO_CLEANUP_HALT_TIMEOUT 10000
#define QDIO_IRQ_STATE_FRESH 0 /* must be 0 -> memset has set it to 0 */
#define QDIO_IRQ_STATE_INACTIVE 1
#define QDIO_IRQ_STATE_ESTABLISHED 2
#define QDIO_IRQ_STATE_ACTIVE 3
#define QDIO_IRQ_STATE_STOPPED 4
enum qdio_irq_states {
QDIO_IRQ_STATE_INACTIVE,
QDIO_IRQ_STATE_ESTABLISHED,
QDIO_IRQ_STATE_ACTIVE,
QDIO_IRQ_STATE_STOPPED,
QDIO_IRQ_STATE_CLEANUP,
QDIO_IRQ_STATE_ERR,
NR_QDIO_IRQ_STATES,
};
/* used as intparm in do_IO: */
#define QDIO_DOING_SENSEID 0
......@@ -443,81 +443,6 @@ do_clear_global_summary(void)
#define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08
#define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
struct qdio_chsc_area {
struct {
/* word 0 */
__u16 command_code1;
__u16 command_code2;
/* word 1 */
__u16 operation_code;
__u16 first_sch;
/* word 2 */
__u8 reserved1;
__u8 image_id;
__u16 last_sch;
/* word 3 */
__u32 reserved2;
/* word 4 */
union {
struct {
/* word 4&5 */
__u64 summary_indicator_addr;
/* word 6&7 */
__u64 subchannel_indicator_addr;
/* word 8 */
int ks:4;
int kc:4;
int reserved1:21;
int isc:3;
/* word 9&10 */
__u32 reserved2[2];
/* word 11 */
__u32 subsystem_id;
/* word 12-1015 */
__u32 reserved3[1004];
} __attribute__ ((packed,aligned(4))) set_chsc;
struct {
/* word 4&5 */
__u32 reserved1[2];
/* word 6 */
__u32 delay_target;
/* word 7-1015 */
__u32 reserved4[1009];
} __attribute__ ((packed,aligned(4))) set_chsc_fast;
struct {
/* word 0 */
__u16 length;
__u16 response_code;
/* word 1 */
__u32 reserved1;
/* words 2 to 9 for st sch qdio data */
__u8 flags;
__u8 reserved2;
__u16 sch;
__u8 qfmt;
__u8 reserved3;
__u8 qdioac;
__u8 sch_class;
__u8 reserved4;
__u8 icnt;
__u8 reserved5;
__u8 ocnt;
/* plus 5 words of reserved fields */
} __attribute__ ((packed,aligned(8)))
store_qdio_data_response;
} operation_data_area;
} __attribute__ ((packed,aligned(8))) request_block;
struct {
/* word 0 */
__u16 length;
__u16 response_code;
/* word 1 */
__u32 reserved1;
} __attribute__ ((packed,aligned(8))) response_block;
} __attribute__ ((packed,aligned(PAGE_SIZE)));
#ifdef QDIO_PERFORMANCE_STATS
struct qdio_perf_stats {
unsigned int tl_runs;
......@@ -623,7 +548,7 @@ struct qdio_q {
struct tasklet_struct tasklet;
#endif /* QDIO_USE_TIMERS_FOR_POLLING */
unsigned int state;
enum qdio_irq_states state;
/* used to store the error condition during a data transfer */
unsigned int qdio_error;
......@@ -674,7 +599,7 @@ struct qdio_irq {
unsigned int hydra_gives_outbound_pcis;
unsigned int sync_done_on_outb_pcis;
unsigned int state;
enum qdio_irq_states state;
struct semaphore setting_up_sema;
unsigned int no_input_qs;
......@@ -694,13 +619,8 @@ struct qdio_irq {
struct qib qib;
/* Functions called via the generic cio layer */
void (*cleanup_irq) (struct ccw_device *, unsigned long, struct irb *);
void (*cleanup_timeout) (struct ccw_device *);
void (*establish_irq) (struct ccw_device *, unsigned long,
struct irb *);
void (*establish_timeout) (struct ccw_device *);
void (*handler) (struct ccw_device *, unsigned long, struct irb *);
void (*original_int_handler) (struct ccw_device *,
unsigned long, struct irb *);
};
#endif
......@@ -44,7 +44,11 @@ init_IRQ(void)
/*
* Let's build our path group ID here.
*/
global_pgid.cpu_addr = *(__u16 *) __LC_CPUADDR;
#ifdef CONFIG_SMP
global_pgid.cpu_addr = hard_smp_processor_id();
#else
global_pgid.cpu_addr = 0;
#endif
global_pgid.cpu_id = ((cpuid_t *) __LC_CPUID)->ident;
global_pgid.cpu_model = ((cpuid_t *) __LC_CPUID)->machine;
global_pgid.tod_high = (__u32) (get_clock() >> 32);
......
......@@ -21,7 +21,7 @@
extern void css_process_crw(int);
extern void chsc_process_crw(void);
extern void chp_process_crw(int);
extern void chp_process_crw(int, int);
static void
s390_handle_damage(char *msg)
......@@ -62,7 +62,17 @@ s390_collect_crw_info(void)
break;
case CRW_RSC_CPATH:
pr_debug("source is channel path %02X\n", crw.rsid);
chp_process_crw(crw.rsid);
switch (crw.erc) {
case CRW_ERC_IPARM: /* Path has come. */
chp_process_crw(crw.rsid, 1);
break;
case CRW_ERC_PERRI: /* Path has gone. */
chp_process_crw(crw.rsid, 0);
break;
default:
pr_debug("Don't know how to handle erc=%x\n",
crw.erc);
}
break;
case CRW_RSC_CONFIG:
pr_debug("source is configuration-alert facility\n");
......
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