extractedLnx/linux-2.6.9/drivers/scsi/arm/acornscsi.c_acornscsi_sbicintr.c
static
intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
{
unsigned int asr, ssr;
asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
if (!(asr & ASR_INT))
return INTR_IDLE;
ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
#if (DEBUG & DEBUG_PHASES)
print_sbic_status(asr, ssr, host->scsi.phase);
#endif
ADD_STATUS(8, ssr, host->scsi.phase, in_irq);
if (host->SCpnt && !host->scsi.disconnectable)
ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
switch (ssr) {
case 0x00: /* reset state - not advanced */
printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
host->host->host_no);
/* setup sbic - WD33C93A */
sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id);
sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET);
return INTR_IDLE;
case 0x01: /* reset state - advanced */
sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME);
sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
msgqueue_flush(&host->scsi.msgs);
return INTR_IDLE;
case 0x41: /* unexpected disconnect aborted command */
acornscsi_disconnect_unexpected(host);
return INTR_NEXT_COMMAND;
}
switch (host->scsi.phase) {
case PHASE_CONNECTING: /* STATE: command removed from issue queue */
switch (ssr) {
case 0x11: /* -> PHASE_CONNECTED */
/* BUS FREE -> SELECTION */
host->scsi.phase = PHASE_CONNECTED;
msgqueue_flush(&host->scsi.msgs);
host->dma.transferred = host->scsi.SCp.scsi_xferred;
/* 33C93 gives next interrupt indicating bus phase */
asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
if (!(asr & ASR_INT))
break;
ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
ADD_STATUS(8, ssr, host->scsi.phase, 1);
ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1);
goto connected;
case 0x42: /* select timed out */
/* -> PHASE_IDLE */
acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
return INTR_NEXT_COMMAND;
case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */
/* BUS FREE -> RESELECTION */
host->origSCpnt = host->SCpnt;
host->SCpnt = NULL;
msgqueue_flush(&host->scsi.msgs);
acornscsi_reconnect(host);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
acornscsi_abortcmd(host, host->SCpnt->tag);
}
return INTR_PROCESSING;
connected:
case PHASE_CONNECTED: /* STATE: device selected ok */
switch (ssr) {
#ifdef NONSTANDARD
case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
/* SELECTION -> COMMAND */
acornscsi_sendcommand(host);
break;
case 0x8b: /* -> PHASE_STATUS */
/* SELECTION -> STATUS */
acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
#endif
case 0x8e: /* -> PHASE_MSGOUT */
/* SELECTION ->MESSAGE OUT */
host->scsi.phase = PHASE_MSGOUT;
acornscsi_buildmessages(host);
acornscsi_sendmessage(host);
break;
/* these should not happen */
case 0x85: /* target disconnected */
acornscsi_done(host, &host->SCpnt, DID_ERROR);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
acornscsi_abortcmd(host, host->SCpnt->tag);
}
return INTR_PROCESSING;
case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */
/*
* SCSI standard says that MESSAGE OUT phases can be followed by a
* DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
*/
switch (ssr) {
case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
/* MESSAGE OUT -> COMMAND */
acornscsi_sendcommand(host);
break;
case 0x8b: /* -> PHASE_STATUS */
case 0x1b: /* -> PHASE_STATUS */
/* MESSAGE OUT -> STATUS */
acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x8e: /* -> PHASE_MSGOUT */
/* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
acornscsi_sendmessage(host);
break;
case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
/* MESSAGE OUT -> MESSAGE IN */
acornscsi_message(host);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_COMMAND: /* STATE: connected & command sent */
switch (ssr) {
case 0x18: /* -> PHASE_DATAOUT */
/* COMMAND -> DATA OUT */
if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
acornscsi_abortcmd(host, host->SCpnt->tag);
acornscsi_dma_setup(host, DMA_OUT);
if (!acornscsi_starttransfer(host))
acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAOUT;
return INTR_IDLE;
case 0x19: /* -> PHASE_DATAIN */
/* COMMAND -> DATA IN */
if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
acornscsi_abortcmd(host, host->SCpnt->tag);
acornscsi_dma_setup(host, DMA_IN);
if (!acornscsi_starttransfer(host))
acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAIN;
return INTR_IDLE;
case 0x1b: /* -> PHASE_STATUS */
/* COMMAND -> STATUS */
acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x1e: /* -> PHASE_MSGOUT */
/* COMMAND -> MESSAGE OUT */
acornscsi_sendmessage(host);
break;
case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
/* COMMAND -> MESSAGE IN */
acornscsi_message(host);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */
if (ssr == 0x85) { /* -> PHASE_IDLE */
host->scsi.disconnectable = 1;
host->scsi.reconnected.tag = 0;
host->scsi.phase = PHASE_IDLE;
host->stats.disconnects += 1;
} else {
printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_NEXT_COMMAND;
case PHASE_IDLE: /* STATE: disconnected */
if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */
acornscsi_reconnect(host);
else {
printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */
/*
* Command reconnected - if MESGIN, get message - it may be
* the tag. If not, get command out of disconnected queue
*/
/*
* If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
* reconnect I_T_L command
*/
if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
return INTR_IDLE;
ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
switch (ssr) {
case 0x88: /* data out phase */
/* -> PHASE_DATAOUT */
/* MESSAGE IN -> DATA OUT */
acornscsi_dma_setup(host, DMA_OUT);
if (!acornscsi_starttransfer(host))
acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAOUT;
return INTR_IDLE;
case 0x89: /* data in phase */
/* -> PHASE_DATAIN */
/* MESSAGE IN -> DATA IN */
acornscsi_dma_setup(host, DMA_IN);
if (!acornscsi_starttransfer(host))
acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAIN;
return INTR_IDLE;
case 0x8a: /* command out */
/* MESSAGE IN -> COMMAND */
acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
break;
case 0x8b: /* status in */
/* -> PHASE_STATUSIN */
/* MESSAGE IN -> STATUS */
acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x8e: /* message out */
/* -> PHASE_MSGOUT */
/* MESSAGE IN -> MESSAGE OUT */
acornscsi_sendmessage(host);
break;
case 0x8f: /* message in */
acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_DATAIN: /* STATE: transferred data in */
/*
* This is simple - if we disconnect then the DMA address & count is
* correct.
*/
switch (ssr) {
case 0x19: /* -> PHASE_DATAIN */
case 0x89: /* -> PHASE_DATAIN */
acornscsi_abortcmd(host, host->SCpnt->tag);
return INTR_IDLE;
case 0x1b: /* -> PHASE_STATUSIN */
case 0x4b: /* -> PHASE_STATUSIN */
case 0x8b: /* -> PHASE_STATUSIN */
/* DATA IN -> STATUS */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
acornscsi_sbic_xfcount(host);
acornscsi_dma_stop(host);
acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x1e: /* -> PHASE_MSGOUT */
case 0x4e: /* -> PHASE_MSGOUT */
case 0x8e: /* -> PHASE_MSGOUT */
/* DATA IN -> MESSAGE OUT */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
acornscsi_sbic_xfcount(host);
acornscsi_dma_stop(host);
acornscsi_sendmessage(host);
break;
case 0x1f: /* message in */
case 0x4f: /* message in */
case 0x8f: /* message in */
/* DATA IN -> MESSAGE IN */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
acornscsi_sbic_xfcount(host);
acornscsi_dma_stop(host);
acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_DATAOUT: /* STATE: transferred data out */
/*
* This is more complicated - if we disconnect, the DMA could be 12
* bytes ahead of us. We need to correct this.
*/
switch (ssr) {
case 0x18: /* -> PHASE_DATAOUT */
case 0x88: /* -> PHASE_DATAOUT */
acornscsi_abortcmd(host, host->SCpnt->tag);
return INTR_IDLE;
case 0x1b: /* -> PHASE_STATUSIN */
case 0x4b: /* -> PHASE_STATUSIN */
case 0x8b: /* -> PHASE_STATUSIN */
/* DATA OUT -> STATUS */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
acornscsi_sbic_xfcount(host);
acornscsi_dma_stop(host);
acornscsi_dma_adjust(host);
acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x1e: /* -> PHASE_MSGOUT */
case 0x4e: /* -> PHASE_MSGOUT */
case 0x8e: /* -> PHASE_MSGOUT */
/* DATA OUT -> MESSAGE OUT */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
acornscsi_sbic_xfcount(host);
acornscsi_dma_stop(host);
acornscsi_dma_adjust(host);
acornscsi_sendmessage(host);
break;
case 0x1f: /* message in */
case 0x4f: /* message in */
case 0x8f: /* message in */
/* DATA OUT -> MESSAGE IN */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
acornscsi_sbic_xfcount(host);
acornscsi_dma_stop(host);
acornscsi_dma_adjust(host);
acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_STATUSIN: /* STATE: status in complete */
switch (ssr) {
case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
/* STATUS -> MESSAGE IN */
acornscsi_message(host);
break;
case 0x1e: /* -> PHASE_MSGOUT */
case 0x8e: /* -> PHASE_MSGOUT */
/* STATUS -> MESSAGE OUT */
acornscsi_sendmessage(host);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_MSGIN: /* STATE: message in */
switch (ssr) {
case 0x1e: /* -> PHASE_MSGOUT */
case 0x4e: /* -> PHASE_MSGOUT */
case 0x8e: /* -> PHASE_MSGOUT */
/* MESSAGE IN -> MESSAGE OUT */
acornscsi_sendmessage(host);
break;
case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
case 0x2f:
case 0x4f:
case 0x8f:
acornscsi_message(host);
break;
case 0x85:
printk("scsi%d.%c: strange message in disconnection\n",
host->host->host_no, acornscsi_target(host));
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
acornscsi_done(host, &host->SCpnt, DID_ERROR);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_DONE: /* STATE: received status & message */
switch (ssr) {
case 0x85: /* -> PHASE_IDLE */
acornscsi_done(host, &host->SCpnt, DID_OK);
return INTR_NEXT_COMMAND;
case 0x1e:
case 0x8e:
acornscsi_sendmessage(host);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
case PHASE_ABORTED:
switch (ssr) {
case 0x85:
if (host->SCpnt)
acornscsi_done(host, &host->SCpnt, DID_ABORT);
else {
clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
host->busyluns);
host->scsi.phase = PHASE_IDLE;
}
return INTR_NEXT_COMMAND;
case 0x1e:
case 0x2e:
case 0x4e:
case 0x8e:
acornscsi_sendmessage(host);
break;
default:
printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
default:
printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
}
return INTR_PROCESSING;
}
Generated by GNU enscript 1.6.4.