patch-2.1.101 linux/drivers/acorn/scsi/fas216.c
Next file: linux/drivers/acorn/scsi/fas216.h
Previous file: linux/drivers/acorn/scsi/eesox.h
Back to the patch index
Back to the overall index
- Lines: 2407
- Date:
Fri May 8 00:42:39 1998
- Orig file:
v2.1.100/linux/drivers/acorn/scsi/fas216.c
- Orig date:
Wed Apr 8 19:36:25 1998
diff -u --recursive --new-file v2.1.100/linux/drivers/acorn/scsi/fas216.c linux/drivers/acorn/scsi/fas216.c
@@ -3,8 +3,9 @@
*
* Copyright (C) 1997 Russell King
*
- * Based in information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
- * other sources.
+ * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
+ * other sources, including:
+ * the AMD Am53CF94 data sheet
*
* This is a generic driver. To use it, have a look at cumana_2.c. You
* should define your own structure that overlays FAS216_Info, eg:
@@ -18,6 +19,13 @@
* 14-09-1997 RMK Started disconnect support
* 08-02-1998 RMK Corrected real DMA support
* 15-02-1998 RMK Started sync xfer support
+ * 06-04-1998 RMK Tightened conditions for printing incomplete
+ * transfers
+ * 02-05-1998 RMK Added extra checks in fas216_reset
+ *
+ * Todo:
+ * - tighten up the MESSAGE_REJECT support.
+ * - allow individual devices to enable sync xfers.
*/
#include <linux/module.h>
@@ -31,6 +39,7 @@
#include <linux/stat.h>
#include <asm/delay.h>
+#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/ecard.h>
@@ -43,7 +52,34 @@
#define VER_MAJOR 0
#define VER_MINOR 0
-#define VER_PATCH 2
+#define VER_PATCH 3
+
+#define SCSI2_TAG
+
+/* NOTE: SCSI2 Synchronous transfers *require* DMA according to
+ * the data sheet. This restriction is crazy, especially when
+ * you only want to send 16 bytes! What were the guys who
+ * designed this chip on at that time? Did they read the SCSI2
+ * spec at all? The following sections are taken from the SCSI2
+ * standard (s2r10) concerning this:
+ *
+ * > IMPLEMENTORS NOTES:
+ * > (1) Re-negotiation at every selection is not recommended, since a
+ * > significant performance impact is likely.
+ *
+ * > The implied synchronous agreement shall remain in effect until a BUS DEVICE
+ * > RESET message is received, until a hard reset condition occurs, or until one
+ * > of the two SCSI devices elects to modify the agreement. The default data
+ * > transfer mode is asynchronous data transfer mode. The default data transfer
+ * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard
+ * > reset condition.
+ *
+ * In total, this means that once you have elected to use synchronous
+ * transfers, you must always use DMA.
+ *
+ * I was thinking that this was a good chip until I found this restriction ;(
+ */
+#define SCSI2_SYNC
#undef NO_DISCONNECTS
#undef DEBUG_CONNECT
@@ -51,9 +87,95 @@
#undef DEBUG_FUNCTIONDONE
#undef DEBUG_MESSAGES
-static char *fas216_bus_phase (int stat)
+#undef CHECK_STRUCTURE
+
+static struct { int stat, ssr, isr, ph; } list[8];
+static int ptr;
+
+static void fas216_dumpstate(FAS216_Info *info)
+{
+ printk("FAS216 registers:\n");
+ printk(" CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X"
+ " INST=%02X IS=%02X CFIS=%02X",
+ inb(REG_CTCL(info)), inb(REG_CTCM(info)),
+ inb(REG_CMD(info)), inb(REG_STAT(info)),
+ inb(REG_INST(info)), inb(REG_IS(info)),
+ inb(REG_CFIS(info)));
+ printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n",
+ inb(REG_CNTL1(info)), inb(REG_CNTL2(info)),
+ inb(REG_CNTL3(info)), inb(REG_CTCH(info)));
+}
+
+static void fas216_dumpinfo(FAS216_Info *info)
+{
+ static int used = 0;
+ int i;
+
+ if (used++)
+ return;
+
+ printk("FAS216_Info = \n");
+ printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n",
+ info->magic_start, info->host, info->SCpnt,
+ info->origSCpnt);
+ printk(" scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n",
+ info->scsi.io_port, info->scsi.io_shift, info->scsi.irq,
+ info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2],
+ info->scsi.cfg[3]);
+ printk(" type=%p phase=%X reconnected = { target=%d lun=%d tag=%d }\n",
+ info->scsi.type, info->scsi.phase,
+ info->scsi.reconnected.target,
+ info->scsi.reconnected.lun, info->scsi.reconnected.tag);
+ printk(" SCp = { ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
+ info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual);
+ printk(" msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n",
+ info->scsi.async_stp, info->scsi.last_message,
+ info->scsi.disconnectable, info->scsi.aborting);
+ printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X disconnects=%X aborts=%X resets=%X }\n",
+ info->stats.queues, info->stats.removes, info->stats.fins,
+ info->stats.reads, info->stats.writes, info->stats.miscs,
+ info->stats.disconnects, info->stats.aborts, info->stats.resets);
+ printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n",
+ info->ifcfg.clockrate, info->ifcfg.select_timeout,
+ info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth);
+ for (i = 0; i < 8; i++) {
+ printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X negstate=%X }\n",
+ i, info->busyluns[i], i,
+ info->device[i].disconnect_ok, info->device[i].stp,
+ info->device[i].sof, info->device[i].negstate);
+ }
+ printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
+ info->dma.transfer_type, info->dma.setup,
+ info->dma.pseudo, info->dma.stop);
+ printk(" internal_done=%X magic_end=%lX\n",
+ info->internal_done, info->magic_end);
+}
+
+#ifdef CHECK_STRUCTURE
+static void fas216_checkmagic(FAS216_Info *info, const char *func)
+{
+ int corruption = 0;
+ if (info->magic_start != MAGIC) {
+ printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n");
+ corruption++;
+ }
+ if (info->magic_end != MAGIC) {
+ printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n");
+ corruption++;
+ }
+ if (corruption) {
+ fas216_dumpinfo(info);
+ panic("scsi memory space corrupted in %s", func);
+ }
+}
+#else
+#define fas216_checkmagic(info,func)
+#endif
+
+static const char *fas216_bus_phase(int stat)
{
- static char *phases[] = {
+ static const char *phases[] = {
"DATA OUT", "DATA IN",
"COMMAND", "STATUS",
"MISC OUT", "MISC IN",
@@ -63,7 +185,26 @@
return phases[stat & STAT_BUSMASK];
}
-static char fas216_target (FAS216_Info *info)
+static const char *fas216_drv_phase(FAS216_Info *info)
+{
+ switch (info->scsi.phase) {
+ case PHASE_IDLE: return "idle";
+ case PHASE_SELECTION: return "selection";
+ case PHASE_MESSAGESENT: return "message sent";
+ case PHASE_RECONNECTED: return "reconnected";
+ case PHASE_DATAOUT: return "data out";
+ case PHASE_DATAIN: return "data in";
+ case PHASE_MSGOUT: return "message out";
+ case PHASE_MSGIN: return "message in";
+ case PHASE_AFTERMSGOUT: return "after message out";
+ case PHASE_STATUS: return "status";
+ case PHASE_DISCONNECT: return "disconnect";
+ case PHASE_DONE: return "done";
+ default: return "???";
+ }
+}
+
+static char fas216_target(FAS216_Info *info)
{
if (info->SCpnt)
return '0' + info->SCpnt->target;
@@ -71,15 +212,41 @@
return 'H';
}
-static void fas216_done (FAS216_Info *info, unsigned int result);
+static void add_debug_list(int stat, int ssr, int isr, int ph)
+{
+ list[ptr].stat = stat;
+ list[ptr].ssr = ssr;
+ list[ptr].isr = isr;
+ list[ptr].ph = ph;
+
+ ptr = (ptr + 1) & 7;
+}
+
+static void print_debug_list(void)
+{
+ int i;
+
+ i = ptr;
+
+ printk(KERN_ERR "SCSI state trail: ");
+ do {
+ printk("%02X:%02X:%02X:%02X ",
+ list[i].stat, list[i].ssr,
+ list[i].isr, list[i].ph);
+ i = (i + 1) & 7;
+ } while (i != ptr);
+ printk("\n");
+}
+
+static void fas216_done(FAS216_Info *info, unsigned int result);
-/* Function: int fas216_clockrate (unsigned int clock)
+/* Function: int fas216_clockrate(unsigned int clock)
* Purpose : calculate correct value to be written into clock conversion
* factor register.
* Params : clock - clock speed in MHz
* Returns : CLKF_ value
*/
-static int fas216_clockrate (int clock)
+static int fas216_clockrate(int clock)
{
if (clock <= 10 || clock > 40) {
printk(KERN_CRIT
@@ -102,6 +269,8 @@
{
int value = (info->ifcfg.clockrate * ns) / 1000;
+ fas216_checkmagic(info, "fas216_syncperiod");
+
if (value < 4)
value = 4;
else if (value > 35)
@@ -110,16 +279,21 @@
return value & 31;
}
-/* Function: void fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+/* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
* Purpose : update data pointers after transfer suspended/paused
* Params : info - interface's local pointer to update
* bytes_transferred - number of bytes transferred
*/
static void
-fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
{
- unsigned char *ptr = info->scsi.SCp.ptr;
- unsigned int residual = info->scsi.SCp.this_residual;
+ unsigned char *ptr;
+ unsigned int residual;
+
+ fas216_checkmagic(info, "fas216_updateptrs");
+
+ ptr = info->scsi.SCp.ptr;
+ residual = info->scsi.SCp.this_residual;
info->SCpnt->request_bufflen -= bytes_transferred;
@@ -144,50 +318,61 @@
info->scsi.SCp.this_residual = residual;
}
-/* Function: void fas216_pio (FAS216_Info *info, fasdmadir_t direction)
+/* Function: void fas216_pio(FAS216_Info *info, fasdmadir_t direction)
* Purpose : transfer data off of/on to card using programmed IO
* Params : info - interface to transfer data to/from
* direction - direction to transfer data (DMA_OUT/DMA_IN)
* Notes : this is incredibly slow
*/
static void
-fas216_pio (FAS216_Info *info, fasdmadir_t direction)
+fas216_pio(FAS216_Info *info, fasdmadir_t direction)
{
- unsigned int length = info->scsi.SCp.this_residual;
- char *ptr = info->scsi.SCp.ptr;
+ unsigned int residual;
+ char *ptr;
+ int correction;
+
+ fas216_checkmagic(info, "fas216_pio");
+
+ residual = info->scsi.SCp.this_residual;
+ ptr = info->scsi.SCp.ptr;
if (direction == DMA_OUT) {
- while (length > 0) {
+ while (residual > 0) {
if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
outb(*ptr++, REG_FF(info));
- length -= 1;
+ residual -= 1;
} else if (inb(REG_STAT(info)) & STAT_INT)
break;
}
+ correction = inb(REG_CFIS(info)) & CFIS_CF;
} else {
- while (length > 0) {
+ while (residual > 0) {
if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
*ptr++ = inb(REG_FF(info));
- length -= 1;
+ residual -= 1;
} else if (inb(REG_STAT(info)) & STAT_INT)
break;
}
+ correction = 0;
}
- if (length == 0) {
+ ptr -= correction;
+ residual += correction;
+
+ if (residual == 0) {
if (info->scsi.SCp.buffers_residual) {
info->scsi.SCp.buffer++;
info->scsi.SCp.buffers_residual--;
ptr = (unsigned char *)info->scsi.SCp.buffer->address;
- length = info->scsi.SCp.buffer->length;
+ residual = info->scsi.SCp.buffer->length;
} else {
ptr = NULL;
- length = 0;
+ residual = 0;
}
}
info->scsi.SCp.ptr = ptr;
- info->scsi.SCp.this_residual = length;
+ info->scsi.SCp.this_residual = residual;
}
/* Function: void fas216_starttransfer(FAS216_Info *info,
@@ -197,77 +382,81 @@
* direction - transfer direction (DMA_OUT/DMA_IN)
*/
static void
-fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction)
+fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction, int flush_fifo)
{
fasdmatype_t dmatype;
+ fas216_checkmagic(info, "fas216_starttransfer");
+
info->scsi.phase = (direction == DMA_OUT) ?
PHASE_DATAOUT : PHASE_DATAIN;
- if (info->dma.transfer_type == fasdma_real_block ||
- info->dma.transfer_type == fasdma_real_all) {
+ if (info->dma.transfer_type != fasdma_none &&
+ info->dma.transfer_type != fasdma_pio) {
unsigned long total, residual;
- if (info->dma.transfer_type == fasdma_real_block)
- total = info->scsi.SCp.this_residual;
- else
+ if (info->dma.transfer_type == fasdma_real_all)
total = info->SCpnt->request_bufflen;
+ else
+ total = info->scsi.SCp.this_residual;
residual = (inb(REG_CFIS(info)) & CFIS_CF) +
inb(REG_CTCL(info)) +
(inb(REG_CTCM(info)) << 8) +
(inb(REG_CTCH(info)) << 16);
- fas216_updateptrs (info, total - residual);
- info->dma.transfer_type = fasdma_none;
+ fas216_updateptrs(info, total - residual);
}
+ info->dma.transfer_type = fasdma_none;
if (!info->scsi.SCp.ptr) {
- printk ("scsi%d.%c: null buffer passed to "
+ printk("scsi%d.%c: null buffer passed to "
"fas216_starttransfer\n", info->host->host_no,
- fas216_target (info));
+ fas216_target(info));
return;
}
- dmatype = fasdma_none;
+ /* flush FIFO */
+ if (flush_fifo)
+ outb(CMD_FLUSHFIFO, REG_CMD(info));
+
+ /*
+ * Default to PIO mode or DMA mode if we have a synchronous
+ * transfer agreement.
+ */
+ if (info->device[info->SCpnt->target].sof && info->dma.setup)
+ dmatype = fasdma_real_all;
+ else
+ dmatype = fasdma_pio;
+
if (info->dma.setup)
dmatype = info->dma.setup(info->host, &info->scsi.SCp,
- direction);
-
+ direction, dmatype);
info->dma.transfer_type = dmatype;
switch (dmatype) {
- case fasdma_none:
+ case fasdma_pio:
+ outb(0, REG_SOF(info));
+ outb(info->scsi.async_stp, REG_STP(info));
outb(info->scsi.SCp.this_residual, REG_STCL(info));
outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
- outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
outb(CMD_TRANSFERINFO, REG_CMD(info));
- fas216_pio (info, direction);
+ fas216_pio(info, direction);
break;
- case fasdma_pseudo: {
- int transferred;
-
+ case fasdma_pseudo:
outb(info->scsi.SCp.this_residual, REG_STCL(info));
outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
- outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
-
- transferred =
- info->dma.pseudo(info->host, &info->scsi.SCp,
- direction, info->SCpnt->transfersize);
-
- fas216_updateptrs (info, transferred);
- }
+ info->dma.pseudo(info->host, &info->scsi.SCp,
+ direction, info->SCpnt->transfersize);
break;
case fasdma_real_block:
outb(info->scsi.SCp.this_residual, REG_STCL(info));
outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
- outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
-
outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
break;
@@ -275,58 +464,66 @@
outb(info->SCpnt->request_bufflen, REG_STCL(info));
outb(info->SCpnt->request_bufflen >> 8, REG_STCM(info));
outb(info->SCpnt->request_bufflen >> 16, REG_STCH(info));
- outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
-
outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%d: invalid FAS216 DMA type\n",
+ info->host->host_no, fas216_target(info));
+ break;
}
}
-/* Function: void fas216_stoptransfer (FAS216_Info *info)
+/* Function: void fas216_stoptransfer(FAS216_Info *info)
* Purpose : Stop a DMA transfer onto / off of the card
* Params : info - interface from which device disconnected from
*/
static void
-fas216_stoptransfer (FAS216_Info *info)
+fas216_stoptransfer(FAS216_Info *info)
{
- if (info->dma.transfer_type == fasdma_real_block ||
- info->dma.transfer_type == fasdma_real_all) {
+ fas216_checkmagic(info, "fas216_stoptransfer");
+
+ if (info->dma.transfer_type != fasdma_none &&
+ info->dma.transfer_type != fasdma_pio) {
unsigned long total, residual;
- if (info->dma.stop)
- info->dma.stop (info->host, &info->scsi.SCp);
+ if ((info->dma.transfer_type == fasdma_real_all ||
+ info->dma.transfer_type == fasdma_real_block) &&
+ info->dma.stop)
+ info->dma.stop(info->host, &info->scsi.SCp);
- if (info->dma.transfer_type == fasdma_real_block)
- total = info->scsi.SCp.this_residual;
- else
+ if (info->dma.transfer_type == fasdma_real_all)
total = info->SCpnt->request_bufflen;
+ else
+ total = info->scsi.SCp.this_residual;
residual = (inb(REG_CFIS(info)) & CFIS_CF) +
inb(REG_CTCL(info)) +
(inb(REG_CTCM(info)) << 8) +
(inb(REG_CTCH(info)) << 16);
- fas216_updateptrs (info, total - residual);
-
+ fas216_updateptrs(info, total - residual);
info->dma.transfer_type = fasdma_none;
}
}
-/* Function: void fas216_disconnected_intr (FAS216_Info *info)
+/* Function: void fas216_disconnected_intr(FAS216_Info *info)
* Purpose : handle device disconnection
* Params : info - interface from which device disconnected from
*/
static void
-fas216_disconnect_intr (FAS216_Info *info)
+fas216_disconnect_intr(FAS216_Info *info)
{
+ fas216_checkmagic(info, "fas216_disconnected_intr");
+
#ifdef DEBUG_CONNECT
printk("scsi%d.%c: disconnect phase=%02X\n", info->host->host_no,
- fas216_target (info), info->scsi.phase);
+ fas216_target(info), info->scsi.phase);
#endif
- msgqueue_flush (&info->scsi.msgs);
+ msgqueue_flush(&info->scsi.msgs);
switch (info->scsi.phase) {
case PHASE_SELECTION: /* while selecting - no target */
- fas216_done (info, DID_NO_CONNECT);
+ fas216_done(info, DID_NO_CONNECT);
break;
case PHASE_DISCONNECT: /* message in - disconnecting */
@@ -338,33 +535,36 @@
break;
case PHASE_DONE: /* at end of command - complete */
- fas216_done (info, DID_OK);
+ fas216_done(info, DID_OK);
break;
case PHASE_AFTERMSGOUT: /* message out - possible ABORT message */
if (info->scsi.last_message == ABORT) {
info->scsi.aborting = 0;
- fas216_done (info, DID_ABORT);
+ fas216_done(info, DID_ABORT);
break;
}
default: /* huh? */
- printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %d\n",
- info->host->host_no, fas216_target (info), info->scsi.phase);
+ printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n",
+ info->host->host_no, fas216_target(info), fas216_drv_phase(info));
+ print_debug_list();
fas216_stoptransfer(info);
- fas216_done (info, DID_ERROR);
+ fas216_done(info, DID_ERROR);
break;
}
}
-/* Function: void fas216_reselected_intr (FAS216_Info *info)
+/* Function: void fas216_reselected_intr(FAS216_Info *info)
* Purpose : Start reconnection of a device
* Params : info - interface which was reselected
*/
static void
-fas216_reselected_intr (FAS216_Info *info)
+fas216_reselected_intr(FAS216_Info *info)
{
- unsigned char target, identify_msg, ok;
+ unsigned char target, identify_msg, ok;
+
+ fas216_checkmagic(info, "fas216_reselected_intr");
if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) {
Scsi_Cmnd *SCpnt = info->SCpnt;
@@ -378,555 +578,733 @@
#ifdef DEBUG_CONNECT
printk("scsi%d.%c: reconnect phase=%02X\n", info->host->host_no,
- fas216_target (info), info->scsi.phase);
+ fas216_target(info), info->scsi.phase);
#endif
- msgqueue_flush (&info->scsi.msgs);
-
- if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
- printk (KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
- info->host->host_no);
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
- info->scsi.phase = PHASE_MSGOUT;
- outb(CMD_MSGACCEPTED, REG_CMD(info));
- return;
- }
+ msgqueue_flush(&info->scsi.msgs);
- target = inb(REG_FF(info));
- identify_msg = inb(REG_FF(info));
+ if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
+ printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
+ info->host->host_no);
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT;
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ return;
+ }
- ok = 1;
- if (!(target & (1 << info->host->this_id))) {
- printk (KERN_ERR "scsi%d.H: invalid host id on reselect\n", info->host->host_no);
- ok = 0;
- }
+ target = inb(REG_FF(info));
+ identify_msg = inb(REG_FF(info));
- if (!(identify_msg & 0x80)) {
- printk (KERN_ERR "scsi%d.H: no IDENTIFY message on reselect, got msg %02X\n",
- info->host->host_no, identify_msg);
- ok = 0;
- }
+ ok = 1;
+ if (!(target & (1 << info->host->this_id))) {
+ printk(KERN_ERR "scsi%d.H: invalid host id on reselect\n", info->host->host_no);
+ ok = 0;
+ }
- if (!ok) {
- /*
- * Something went wrong - abort the command on
- * the target. Should this be INITIATOR_ERROR ?
- */
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
- outb(CMD_MSGACCEPTED, REG_CMD(info));
- return;
- }
+ if (!(identify_msg & 0x80)) {
+ printk(KERN_ERR "scsi%d.H: no IDENTIFY message on reselect, got msg %02X\n",
+ info->host->host_no, identify_msg);
+ ok = 0;
+ }
- target &= ~(1 << info->host->this_id);
- switch (target) {
- case 1: target = 0; break;
- case 2: target = 1; break;
- case 4: target = 2; break;
- case 8: target = 3; break;
- case 16: target = 4; break;
- case 32: target = 5; break;
- case 64: target = 6; break;
- case 128: target = 7; break;
- default: target = info->host->this_id; break;
- }
-
- identify_msg &= 7;
- info->scsi.reconnected.target = target;
- info->scsi.reconnected.lun = identify_msg;
- info->scsi.reconnected.tag = 0;
-
- ok = 0;
- if (info->scsi.disconnectable && info->SCpnt &&
- info->SCpnt->target == target && info->SCpnt->lun == identify_msg)
- ok = 1;
+ if (!ok) {
+ /*
+ * Something went wrong - abort the command on
+ * the target. Should this be INITIATOR_ERROR ?
+ */
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+ info->scsi.phase = PHASE_MSGOUT;
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ return;
+ }
- if (!ok && queue_probetgtlun (&info->queues.disconnected, target, identify_msg))
- ok = 1;
+ target &= ~(1 << info->host->this_id);
+ switch (target) {
+ case 1: target = 0; break;
+ case 2: target = 1; break;
+ case 4: target = 2; break;
+ case 8: target = 3; break;
+ case 16: target = 4; break;
+ case 32: target = 5; break;
+ case 64: target = 6; break;
+ case 128: target = 7; break;
+ default: target = info->host->this_id; break;
+ }
+
+ identify_msg &= 7;
+ info->scsi.reconnected.target = target;
+ info->scsi.reconnected.lun = identify_msg;
+ info->scsi.reconnected.tag = 0;
- if (ok) {
- info->scsi.phase = PHASE_RECONNECTED;
- outb(target, REG_SDID(info));
- } else {
- /*
- * Our command structure not found - abort the command on the target
- * Should this be INITIATOR_ERROR ?
- */
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
- }
- outb(CMD_MSGACCEPTED, REG_CMD(info));
+ ok = 0;
+ if (info->scsi.disconnectable && info->SCpnt &&
+ info->SCpnt->target == target && info->SCpnt->lun == identify_msg)
+ ok = 1;
+
+ if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg))
+ ok = 1;
+
+ if (ok) {
+ info->scsi.phase = PHASE_RECONNECTED;
+ outb(target, REG_SDID(info));
+ } else {
+ /*
+ * Our command structure not found - abort the command on the target
+ * Should this be INITIATOR_ERROR ?
+ */
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+ info->scsi.phase = PHASE_MSGOUT;
+ }
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
}
-/* Function: void fas216_finish_reconnect (FAS216_Info *info)
+/* Function: void fas216_finish_reconnect(FAS216_Info *info)
* Purpose : finish reconnection sequence for device
* Params : info - interface which caused function done interrupt
*/
static void
-fas216_finish_reconnect (FAS216_Info *info)
+fas216_finish_reconnect(FAS216_Info *info)
{
+ fas216_checkmagic(info, "fas216_reconnect");
+
#ifdef DEBUG_CONNECT
-printk ("Connected: %1X %1X %02X, reconnected: %1X %1X %02X\n",
- info->SCpnt->target, info->SCpnt->lun, info->SCpnt->tag,
- info->scsi.reconnected.target, info->scsi.reconnected.lun,
- info->scsi.reconnected.tag);
+ printk("Connected: %1X %1X %02X, reconnected: %1X %1X %02X\n",
+ info->SCpnt->target, info->SCpnt->lun, info->SCpnt->tag,
+ info->scsi.reconnected.target, info->scsi.reconnected.lun,
+ info->scsi.reconnected.tag);
#endif
- if (info->scsi.disconnectable && info->SCpnt) {
- info->scsi.disconnectable = 0;
- if (info->SCpnt->target == info->scsi.reconnected.target &&
- info->SCpnt->lun == info->scsi.reconnected.lun &&
- info->SCpnt->tag == info->scsi.reconnected.tag) {
+ if (info->scsi.disconnectable && info->SCpnt) {
+ info->scsi.disconnectable = 0;
+ if (info->SCpnt->target == info->scsi.reconnected.target &&
+ info->SCpnt->lun == info->scsi.reconnected.lun &&
+ info->SCpnt->tag == info->scsi.reconnected.tag) {
#ifdef DEBUG_CONNECT
- printk ("scsi%d.%c: reconnected",
- info->host->host_no, fas216_target (info));
+ printk("scsi%d.%c: reconnected",
+ info->host->host_no, fas216_target(info));
#endif
- } else {
- queue_add_cmd_tail (&info->queues.disconnected, info->SCpnt);
+ } else {
+ queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
#ifdef DEBUG_CONNECT
- printk ("scsi%d.%c: had to move command to disconnected queue\n",
- info->host->host_no, fas216_target (info));
+ printk("scsi%d.%c: had to move command to disconnected queue\n",
+ info->host->host_no, fas216_target(info));
#endif
- info->SCpnt = NULL;
+ info->SCpnt = NULL;
+ }
}
- }
- if (!info->SCpnt) {
- info->SCpnt = queue_remove_tgtluntag (&info->queues.disconnected,
- info->scsi.reconnected.target,
- info->scsi.reconnected.lun,
- info->scsi.reconnected.tag);
+ if (!info->SCpnt) {
+ info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected,
+ info->scsi.reconnected.target,
+ info->scsi.reconnected.lun,
+ info->scsi.reconnected.tag);
#ifdef DEBUG_CONNECT
- printk ("scsi%d.%c: had to get command",
- info->host->host_no, fas216_target (info));
+ printk("scsi%d.%c: had to get command",
+ info->host->host_no, fas216_target(info));
#endif
- }
- if (!info->SCpnt) {
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
- info->scsi.aborting = 1;
- } else {
- /*
- * Restore data pointer from SAVED data pointer
- */
- info->scsi.SCp = info->SCpnt->SCp;
+ }
+ if (!info->SCpnt) {
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+ info->scsi.phase = PHASE_MSGOUT;
+ info->scsi.aborting = 1;
+ } else {
+ /*
+ * Restore data pointer from SAVED data pointer
+ */
+ info->scsi.SCp = info->SCpnt->SCp;
#ifdef DEBUG_CONNECT
- printk (", data pointers: [%p, %X]",
- info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ printk(", data pointers: [%p, %X]",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
#endif
- }
+ }
#ifdef DEBUG_CONNECT
- printk ("\n");
+ printk("\n");
#endif
}
-/* Function: void fas216_message (FAS216_Info *info)
+/* Function: void fas216_message(FAS216_Info *info)
* Purpose : handle a function done interrupt from FAS216 chip
* Params : info - interface which caused function done interrupt
*/
-static void fas216_message (FAS216_Info *info)
+static void fas216_message(FAS216_Info *info)
{
- unsigned char message[16];
- unsigned int msglen = 1;
+ unsigned char message[16];
+ unsigned int msglen = 1;
+
+ fas216_checkmagic(info, "fas216_message");
+
+ message[0] = inb(REG_FF(info));
- message[0] = inb(REG_FF(info));
+ if (message[0] == EXTENDED_MESSAGE) {
+ int tout;
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ for (tout = 1000000; tout; tout--)
+ if (inb(REG_STAT(info)) & STAT_INT)
+ break;
+ inb(REG_INST(info));
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ for (tout = 1000000; tout; tout--)
+ if (inb(REG_STAT(info)) & STAT_INT)
+ break;
+ inb(REG_INST(info));
- if (message[0] == EXTENDED_MESSAGE) {
- message[1] = inb(REG_FF(info));
+ message[1] = inb(REG_FF(info));
- for (msglen = 2; msglen < message[1]; msglen++)
- message[msglen] = inb(REG_FF(info));
- }
+ for (msglen = 2; msglen < message[1] + 2; msglen++) {
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ for (tout = 1000000; tout; tout--)
+ if (inb(REG_STAT(info)) & STAT_INT)
+ break;
+ inb(REG_INST(info));
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ for (tout = 1000000; tout; tout--)
+ if (inb(REG_STAT(info)) & STAT_INT)
+ break;
+ inb(REG_INST(info));
+
+ message[msglen] = inb(REG_FF(info));
+ }
+ }
#ifdef DEBUG_MESSAGES
- {
- int i;
+ {
+ int i;
+
+ printk("scsi%d.%c: message in: ",
+ info->host->host_no, fas216_target(info));
+ for (i = 0; i < msglen; i++)
+ printk("%02X ", message[i]);
+ printk("\n");
+ }
+#endif
+ if (info->scsi.phase == PHASE_RECONNECTED) {
+ if (message[0] == SIMPLE_QUEUE_TAG)
+ info->scsi.reconnected.tag = message[1];
+ fas216_finish_reconnect(info);
+ info->scsi.phase = PHASE_MSGIN;
+ }
- printk ("scsi%d.%c: message in: ",
- info->host->host_no, fas216_target (info));
- for (i = 0; i < msglen; i++)
- printk ("%02X ", message[i]);
- printk ("\n");
- }
-#endif
- if (info->scsi.phase == PHASE_RECONNECTED) {
- if (message[0] == SIMPLE_QUEUE_TAG)
- info->scsi.reconnected.tag = message[1];
- fas216_finish_reconnect (info);
- info->scsi.phase = PHASE_MSGIN;
- }
-
- switch (message[0]) {
- case COMMAND_COMPLETE:
- printk ("fas216: command complete with no status in MESSAGE_IN?\n");
- break;
+ switch (message[0]) {
+ case COMMAND_COMPLETE:
+ printk("fas216: command complete with no status in MESSAGE_IN?\n");
+ break;
- case SAVE_POINTERS:
- /*
- * Save current data pointer to SAVED data pointer
- */
- info->SCpnt->SCp = info->scsi.SCp;
+ case SAVE_POINTERS:
+ /*
+ * Save current data pointer to SAVED data pointer
+ */
+ info->SCpnt->SCp = info->scsi.SCp;
#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
- printk ("scsi%d.%c: save data pointers: [%p, %X]\n",
- info->host->host_no, fas216_target (info),
- info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ printk("scsi%d.%c: save data pointers: [%p, %X]\n",
+ info->host->host_no, fas216_target(info),
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
#endif
- break;
+ break;
- case RESTORE_POINTERS:
- /*
- * Restore current data pointer from SAVED data pointer
- */
- info->scsi.SCp = info->SCpnt->SCp;
+ case RESTORE_POINTERS:
+ /*
+ * Restore current data pointer from SAVED data pointer
+ */
+ info->scsi.SCp = info->SCpnt->SCp;
#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
- printk ("scsi%d.%c: restore data pointers: [%p, %X]\n",
- info->host->host_no, fas216_target (info),
- info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ printk("scsi%d.%c: restore data pointers: [%p, %X]\n",
+ info->host->host_no, fas216_target(info),
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
#endif
- break;
+ break;
+
+ case DISCONNECT:
+ info->scsi.phase = PHASE_DISCONNECT;
+ break;
+
+ case MESSAGE_REJECT:
+ printk("scsi%d.%c: reject, last message %04X\n",
+ info->host->host_no, fas216_target(info),
+ info->scsi.last_message);
+ break;
+
+ case SIMPLE_QUEUE_TAG:
+ /* handled above */
+ printk("scsi%d.%c: reconnect queue tag %02X\n",
+ info->host->host_no, fas216_target(info),
+ message[1]);
+ break;
+
+ case EXTENDED_MESSAGE:
+ switch (message[2]) {
+ case EXTENDED_SDTR: /* Sync transfer negociation request/reply */
+ switch (info->device[info->SCpnt->target].negstate) {
+ case syncneg_invalid:
+ msgqueue_flush(&info->scsi.msgs);
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT;
+ break;
- case DISCONNECT:
- info->scsi.phase = PHASE_DISCONNECT;
- break;
-
- case MESSAGE_REJECT:
- printk ("scsi%d.%c: reject, last message %04X\n",
- info->host->host_no, fas216_target (info),
- info->scsi.last_message);
- break;
-
- case SIMPLE_QUEUE_TAG:
- /* handled above */
- printk ("scsi%d.%c: reconnect queue tag %02X\n",
- info->host->host_no, fas216_target (info),
- message[1]);
- break;
-
- case EXTENDED_MESSAGE:
- switch (message[2]) {
- case EXTENDED_SDTR: /* Sync transfer negociation request/reply */
+ default:
+ if (message[4] > info->ifcfg.sync_max_depth)
+ message[4] = info->ifcfg.sync_max_depth;
+ if (message[3] < 1000 / info->ifcfg.clockrate)
+ message[3] = 1000 / info->ifcfg.clockrate;
+
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 5,
+ EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+ message[3], message[4]);
+ info->scsi.phase = PHASE_MSGOUT;
+ case syncneg_sent:
+ info->device[info->SCpnt->target].negstate = syncneg_complete;
+ info->device[info->SCpnt->target].sof = message[4];
+ info->device[info->SCpnt->target].stp =
+ fas216_syncperiod(info, message[3] * 4);
+ printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n",
+ info->host->host_no, fas216_target(info), message[4], message[3] * 4);
+ outb(info->device[info->SCpnt->target].sof, REG_SOF(info));
+ outb(info->device[info->SCpnt->target].stp, REG_STP(info));
+ break;
+ }
+ break;
+
+ case EXTENDED_WDTR: /* Wide transfer negociation request/reply */
+ /* We don't do wide transfers - reject message */
+ default:
+ printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
+ info->host->host_no, fas216_target(info),
+ message[2]);
+ msgqueue_flush(&info->scsi.msgs);
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT;
+ break;
+ }
+ break;
- case EXTENDED_WDTR: /* Wide transfer negociation request/reply */
- /* We don't do wide transfers - reject message */
default:
- printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
- info->host->host_no, fas216_target (info),
- message[2]);
- msgqueue_flush (&info->scsi.msgs);
+ printk("scsi%d.%c: unrecognised message %02X, rejecting\n",
+ info->host->host_no, fas216_target(info),
+ message[0]);
+ msgqueue_flush(&info->scsi.msgs);
outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
info->scsi.phase = PHASE_MSGOUT;
break;
}
- break;
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+}
- default:
- printk ("scsi%d.%c: unrecognised message %02X, rejecting\n",
- info->host->host_no, fas216_target (info),
- message[0]);
- msgqueue_flush (&info->scsi.msgs);
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
- info->scsi.phase = PHASE_MSGOUT;
- break;
- }
- outb(CMD_MSGACCEPTED, REG_CMD(info));
+/* Function: void fas216_send_command(FAS216_Info *info)
+ * Purpose : send a command to a target after all message bytes have been sent
+ * Params : info - interface which caused bus service
+ */
+static void fas216_send_command(FAS216_Info *info)
+{
+ int i;
+
+ fas216_checkmagic(info, "fas216_send_command");
+
+ outb(CMD_NOP|CMD_WITHDMA, REG_CMD(info));
+ outb(CMD_FLUSHFIFO, REG_CMD(info));
+
+ /* load command */
+ for (i = 0; i < info->SCpnt->cmd_len; i++)
+ outb(info->SCpnt->cmnd[i], REG_FF(info));
+
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
}
-/* Function: void fas216_busservice_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
- * Purpose : handle a bus service interrupt from FAS216 chip
- * Params : info - interface which caused bus service interrupt
- * stat - Status register contents
- * ssr - SCSI Status register contents
+/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service in selection phase
+ * Params : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
*/
-static void fas216_busservice_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
{
- int i;
-#ifdef DEBUG_BUSSERVICE
- printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
- info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
-#endif
- switch (ssr & IS_BITS) {
- case IS_COMPLETE: /* last action completed */
- outb(CMD_NOP, REG_CMD(info));
+ fas216_checkmagic(info, "fas216_busservice_selection");
- switch (info->scsi.phase) {
- case PHASE_SELECTION: /* while selecting - selected target */
- switch (stat & STAT_BUSMASK) {
- case STAT_DATAOUT: /* data out phase */
- fas216_starttransfer (info, DMA_OUT);
- break;
+ switch (stat & STAT_BUSMASK) {
+ case STAT_DATAOUT: /* data out phase */
+ fas216_starttransfer(info, DMA_OUT, 1);
+ return 1;
- case STAT_DATAIN: /* data in phase */
- fas216_starttransfer (info, DMA_IN);
- break;
+ case STAT_DATAIN: /* data in phase */
+ fas216_starttransfer(info, DMA_IN, 0);
+ return 1;
- case STAT_STATUS: /* status phase */
+ case STAT_STATUS: /* status phase */
info->scsi.phase = PHASE_STATUS;
outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- break;
+ return 1;
- case STAT_MESGIN: /* message in phase */
+ case STAT_MESGIN: /* message in phase */
info->scsi.phase = PHASE_MSGIN;
outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ return 1;
- default: /* other */
- printk ("scsi%d.%c: bus phase %s after connect?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- break;
- }
- break;
+ case STAT_MESGOUT:{ /* message out phase */
+ char *msg;
+ int start = 1, msglen;
- case PHASE_DATAIN: /* while transfering data in */
- switch (stat & STAT_BUSMASK) {
- case STAT_DATAIN: /* continue data in phase */
- fas216_starttransfer (info, DMA_IN);
- break;
+ /* load message bytes, but don't forget to miss the first
+ * byte!
+ */
+ while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+ int i;
- case STAT_STATUS:
- fas216_stoptransfer(info);
- info->scsi.phase = PHASE_STATUS;
- outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- break;
+ for (i = start; i < msglen; i++)
+ outb(msg[i], REG_FF(info));
+ start = 0;
+ }
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ info->scsi.phase = PHASE_MESSAGESENT;
+ return 1;
+ }
+ default:
+ return 0;
+ }
+}
- case STAT_MESGIN: /* message in phase */
- fas216_stoptransfer(info);
+/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service after the IDENTIFY message has been sent
+ * Params : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ */
+static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
+{
+ fas216_checkmagic(info, "fas216_busservice_messagesent");
+
+ switch (stat & STAT_BUSMASK) {
+ case STAT_MESGIN: /* message in phase */
info->scsi.phase = PHASE_MSGIN;
+ outb(CMD_FLUSHFIFO, REG_CMD(info));
outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ return 1;
- default:
- printk ("scsi%d.%c: bus phase %s after data in?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
-
- case PHASE_DATAOUT: /* while transfering data out */
- switch (stat & STAT_BUSMASK) {
- case STAT_DATAOUT:
- fas216_starttransfer (info, DMA_OUT);
- break;
+ case STAT_COMMAND: /* command phase */
+ fas216_send_command(info);
+ return 1;
+
+ default:
+ return 0;
+ }
+}
- case STAT_STATUS:
+/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service in a data in/out phase.
+ * Params : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ * Note : We do not allow the device to change the data direction!
+ */
+static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
+{
+ fas216_checkmagic(info, "fas216_busservice_dataphase");
+
+ switch (stat & STAT_BUSMASK) {
+ case STAT_DATAIN: /* continue data in phase */
+ if (info->scsi.phase == PHASE_DATAIN) {
+ fas216_starttransfer(info, DMA_IN, 0);
+ return 1;
+ } else
+ return 0;
+
+ case STAT_DATAOUT: /* continue data out phase */
+ if (info->scsi.phase == PHASE_DATAOUT) {
+ fas216_starttransfer(info, DMA_OUT, 0);
+ return 1;
+ } else
+ return 0;
+
+ case STAT_STATUS: /* status in phase */
fas216_stoptransfer(info);
info->scsi.phase = PHASE_STATUS;
- outb(CMD_FLUSHFIFO, REG_CMD(info));
outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- break;
+ return 1;
- case STAT_MESGIN: /* message in phase */
+ case STAT_MESGIN: /* message in phase */
fas216_stoptransfer(info);
info->scsi.phase = PHASE_MSGIN;
- outb(CMD_FLUSHFIFO, REG_CMD(info));
outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service in after a reconnection
+ * Params : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ * Note : We do not allow the device to change the data direction!
+ */
+static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
+{
+ fas216_checkmagic(info, "fas216_busservice_reconnected");
- default:
- printk ("scsi%d.%c: bus phase %s after data out?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
-
- case PHASE_RECONNECTED: /* newly reconnected device */
- /*
- * Command reconnected - if MESGIN, get message - it may be
- * the tag. If not, get command out of the disconnected queue
- */
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN:
+ switch (stat & STAT_BUSMASK) {
+ case STAT_MESGIN:
outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ return 1;
- case STAT_STATUS:
- fas216_finish_reconnect (info);
+ case STAT_STATUS:
+ fas216_finish_reconnect(info);
info->scsi.phase = PHASE_STATUS;
outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- break;
+ return 1;
- case STAT_DATAOUT: /* data out phase */
- fas216_finish_reconnect (info);
- fas216_starttransfer (info, DMA_OUT);
- break;
-
- case STAT_DATAIN: /* data in phase */
- fas216_finish_reconnect (info);
- fas216_starttransfer (info, DMA_IN);
- break;
-
- default:
- printk ("scsi%d.%c: bus phase %s after reconnect?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
-
- case PHASE_MSGIN:
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN:
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ case STAT_DATAOUT: /* data out phase */
+ fas216_finish_reconnect(info);
+ fas216_starttransfer(info, DMA_OUT, 1);
+ return 1;
+
+ case STAT_DATAIN: /* data in phase */
+ fas216_finish_reconnect(info);
+ fas216_starttransfer(info, DMA_IN, 0);
+ return 1;
+
+ default:
+ return 0;
+ }
+}
- default:
- printk ("scsi%d.%c: bus phase %s after message in?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
-
- case PHASE_MSGOUT:
- if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
- printk ("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
- info->host->host_no, fas216_target (info));
- } else {
- unsigned int msglen;
+/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service to send a message
+ * Params : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ * Note : We do not allow the device to change the data direction!
+ */
+static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
+{
+ fas216_checkmagic(info, "fas216_busservice_messageout");
- msglen = msgqueue_msglength (&info->scsi.msgs);
+ if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
+ printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
+ info->host->host_no, fas216_target(info));
+ return 0;
+ } else {
+ unsigned int msglen = msgqueue_msglength(&info->scsi.msgs);
outb(CMD_FLUSHFIFO, REG_CMD(info));
if (msglen == 0)
- outb(NOP, REG_FF(info));
+ outb(NOP, REG_FF(info));
else {
- char *msg;
+ char *msg;
- while ((msg = msgqueue_getnextmsg (&info->scsi.msgs, &msglen)) != NULL) {
- for (i = 0; i < msglen; i++)
- outb(msg[i], REG_FF(info));
- }
+ while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+ int i;
+
+ for (i = 0; i < msglen; i++)
+ outb(msg[i], REG_FF(info));
+ }
}
outb(CMD_TRANSFERINFO, REG_CMD(info));
info->scsi.phase = PHASE_AFTERMSGOUT;
- }
- break;
+ return 1;
+ }
+}
- case PHASE_AFTERMSGOUT:
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN:
- info->scsi.phase = PHASE_MSGIN;
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+ * Purpose : handle a bus service interrupt from FAS216 chip
+ * Params : info - interface which caused bus service interrupt
+ * stat - Status register contents
+ * ssr - SCSI Status register contents
+ */
+static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+{
+ fas216_checkmagic(info, "fas216_busservice_intr");
+
+#ifdef DEBUG_BUSSERVICE
+ printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
+ info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
+#endif
+ switch (ssr & IS_BITS) {
+ case IS_MSGBYTESENT: /* select with ATN and stop steps completed */
+ case IS_COMPLETE: /* last action completed */
+ outb(CMD_NOP, REG_CMD(info));
- default:
- printk ("scsi%d.%c: bus phase %s after message out\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
-
- case PHASE_DISCONNECT:
- printk ("scsi%d.%c: disconnect message received, but bus service %s?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
- info->scsi.aborting = 1;
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ switch (info->scsi.phase) {
+ case PHASE_SELECTION: /* while selecting - selected target */
+ if (!fas216_busservice_selection(info, stat))
+ printk("scsi%d.%c: bus phase %s after connect?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat));
+ break;
+
+ case PHASE_MESSAGESENT:
+ if (!fas216_busservice_messagesent(info, stat))
+ printk("scsi%d.%c: bus phase %s after message sent?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat));
+ break;
+
+ case PHASE_DATAIN: /* while transfering data in */
+ case PHASE_DATAOUT: /* while transfering data out */
+ if (!fas216_busservice_dataphase(info, stat))
+ printk("scsi%d.%c: bus phase %s after %s?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat), fas216_drv_phase(info));
+ break;
+
+ case PHASE_RECONNECTED: /* newly reconnected device */
+ /*
+ * Command reconnected - if MESGIN, get message - it may be
+ * the tag. If not, get command out of the disconnected queue
+ */
+ if (!fas216_busservice_reconnected(info, stat))
+ printk("scsi%d.%c: bus phase %s after reconnect?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat));
+ break;
+
+ case PHASE_MSGIN:
+ case PHASE_AFTERMSGOUT:
+ switch (stat & STAT_BUSMASK) {
+ case STAT_MESGIN:
+ info->scsi.phase = PHASE_MSGIN;
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ break;
+
+ case STAT_COMMAND: /* command phase */
+ fas216_send_command(info);
+ info->scsi.phase = PHASE_SELECTION;
+ break;
+
+ default:
+ printk("scsi%d.%c: bus phase %s after %s?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat),
+ fas216_drv_phase(info));
+ }
+ break;
+
+ case PHASE_MSGOUT:
+ if (!fas216_busservice_messageout(info, stat))
+ printk("scsi%d.%c: bus phase %s instead of message out?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat));
+ break;
+
+ case PHASE_DISCONNECT:
+ printk("scsi%d.%c: disconnect message received, but bus service %s?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat));
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT;
+ info->scsi.aborting = 1;
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ break;
+
+ default:
+ printk("scsi%d.%c: internal phase %s for bus service?"
+ " What do I do with this?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_drv_phase(info));
+ }
+ break;
default:
- printk ("scsi%d.%c: internal phase %d for bus service?"
- " What do I do with this?\n",
- info->host->host_no, fas216_target (info),
- info->scsi.phase);
+ printk("scsi%d.%c: bus service at step %d?\n",
+ info->host->host_no, fas216_target(info),
+ ssr & IS_BITS);
}
- break;
-
- default:
- printk ("scsi%d.%c: bus service at step %d?\n",
- info->host->host_no, fas216_target (info),
- ssr & IS_BITS);
- }
}
-/* Function: void fas216_funcdone_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+/* Function: void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
* Purpose : handle a function done interrupt from FAS216 chip
* Params : info - interface which caused function done interrupt
* stat - Status register contents
* ssr - SCSI Status register contents
*/
-static void fas216_funcdone_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
{
- int status, message;
+ int status, message;
+
+ fas216_checkmagic(info, "fas216_funcdone_intr");
+
#ifdef DEBUG_FUNCTIONDONE
- printk("scsi%d.%c: function done: stat=%X ssr=%X phase=%02X\n",
- info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
+ printk("scsi%d.%c: function done: stat=%X ssr=%X phase=%02X\n",
+ info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
#endif
- switch (info->scsi.phase) {
- case PHASE_STATUS: /* status phase - read status and msg */
- status = inb(REG_FF(info));
- message = inb(REG_FF(info));
- info->scsi.SCp.Message = message;
- info->scsi.SCp.Status = status;
- info->scsi.phase = PHASE_DONE;
- outb(CMD_MSGACCEPTED, REG_CMD(info));
- break;
+ switch (info->scsi.phase) {
+ case PHASE_STATUS: /* status phase - read status and msg */
+ status = inb(REG_FF(info));
+ message = inb(REG_FF(info));
+ info->scsi.SCp.Message = message;
+ info->scsi.SCp.Status = status;
+ info->scsi.phase = PHASE_DONE;
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ break;
- case PHASE_IDLE: /* reselected? */
- case PHASE_MSGIN: /* message in phase */
- case PHASE_RECONNECTED: /* reconnected command */
- if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
- fas216_message (info);
- break;
- }
+ case PHASE_IDLE: /* reselected? */
+ case PHASE_MSGIN: /* message in phase */
+ case PHASE_RECONNECTED: /* reconnected command */
+ if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+ fas216_message(info);
+ break;
+ }
- default:
- printk ("scsi%d.%c: internal phase %d for function done?"
- " What do I do with this?\n",
- info->host->host_no, fas216_target (info),
- info->scsi.phase);
- }
+ default:
+ printk("scsi%d.%c: internal phase %s for function done?"
+ " What do I do with this?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_drv_phase(info));
+ }
}
-/* Function: void fas216_intr (struct Scsi_Host *instance)
+/* Function: void fas216_intr(struct Scsi_Host *instance)
* Purpose : handle interrupts from the interface to progress a command
* Params : instance - interface to service
*/
-void fas216_intr (struct Scsi_Host *instance)
+void fas216_intr(struct Scsi_Host *instance)
{
- FAS216_Info *info = (FAS216_Info *)instance->hostdata;
- unsigned char isr, ssr, stat;
+ FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+ unsigned char isr, ssr, stat;
+
+ fas216_checkmagic(info, "fas216_intr");
- stat = inb(REG_STAT(info));
- ssr = inb(REG_IS(info));
- isr = inb(REG_INST(info));
-
- if (isr & INST_BUSRESET)
- printk ("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
- else if (isr & INST_ILLEGALCMD)
- printk (KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
- else if (isr & INST_DISCONNECT)
- fas216_disconnect_intr (info);
- else if (isr & INST_RESELECTED) /* reselected */
- fas216_reselected_intr (info);
- else if (isr & INST_BUSSERVICE) /* bus service request */
- fas216_busservice_intr (info, stat, ssr);
- else if (isr & INST_FUNCDONE) /* function done */
- fas216_funcdone_intr (info, stat, ssr);
- else
- printk ("scsi%d.%c: unknown interrupt received:"
- " phase %d isr %02X ssr %02X stat %02X\n",
- instance->host_no, fas216_target (info),
- info->scsi.phase, isr, ssr, stat);
+ stat = inb(REG_STAT(info));
+ ssr = inb(REG_IS(info));
+ isr = inb(REG_INST(info));
+
+ add_debug_list(stat, ssr, isr, info->scsi.phase);
+
+ if (stat & STAT_INT) {
+ if (isr & INST_BUSRESET)
+ printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
+ else if (isr & INST_ILLEGALCMD)
+ printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
+ else if (isr & INST_DISCONNECT)
+ fas216_disconnect_intr(info);
+ else if (isr & INST_RESELECTED) /* reselected */
+ fas216_reselected_intr(info);
+ else if (isr & INST_BUSSERVICE) /* bus service request */
+ fas216_busservice_intr(info, stat, ssr);
+ else if (isr & INST_FUNCDONE) /* function done */
+ fas216_funcdone_intr(info, stat, ssr);
+ else
+ printk("scsi%d.%c: unknown interrupt received:"
+ " phase %s isr %02X ssr %02X stat %02X\n",
+ instance->host_no, fas216_target(info),
+ fas216_drv_phase(info), isr, ssr, stat);
+ }
}
-/* Function: void fas216_kick (FAS216_Info *info)
+/* Function: void fas216_kick(FAS216_Info *info)
* Purpose : kick a command to the interface - interface should be idle
* Params : info - our host interface to kick
* Notes : Interrupts are always disabled!
*/
-static void fas216_kick (FAS216_Info *info)
+static void fas216_kick(FAS216_Info *info)
{
Scsi_Cmnd *SCpnt;
int i, msglen, from_queue = 0;
+ fas216_checkmagic(info, "fas216_kick");
+
if (info->origSCpnt) {
SCpnt = info->origSCpnt;
info->origSCpnt = NULL;
@@ -943,11 +1321,11 @@
return;
if (info->scsi.disconnectable && info->SCpnt) {
- queue_add_cmd_tail (&info->queues.disconnected, info->SCpnt);
+ queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
info->scsi.disconnectable = 0;
info->SCpnt = NULL;
printk("scsi%d.%c: moved command to disconnected queue\n",
- info->host->host_no, fas216_target (info));
+ info->host->host_no, fas216_target(info));
}
/*
@@ -1004,7 +1382,7 @@
}
/* build outgoing message bytes */
- msgqueue_flush (&info->scsi.msgs);
+ msgqueue_flush(&info->scsi.msgs);
if (info->device[SCpnt->target].disconnect_ok)
msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun));
else
@@ -1015,7 +1393,8 @@
msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
/* add synchronous negociation */
- if (info->device[SCpnt->target].negstate == syncneg_start) {
+ if (SCpnt->cmnd[0] == REQUEST_SENSE &&
+ info->device[SCpnt->target].negstate == syncneg_start) {
info->device[SCpnt->target].negstate = syncneg_sent;
msgqueue_addmsg(&info->scsi.msgs, 5,
EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
@@ -1040,9 +1419,12 @@
outb(info->device[SCpnt->target].sof, REG_SOF(info));
outb(info->device[SCpnt->target].stp, REG_STP(info));
- msglen = msgqueue_msglength (&info->scsi.msgs);
+ msglen = msgqueue_msglength(&info->scsi.msgs);
if (msglen == 1 || msglen == 3) {
+ /*
+ * We have an easy message length to send...
+ */
char *msg;
/* load message bytes */
@@ -1060,6 +1442,22 @@
else
outb(CMD_SELECTATN3, REG_CMD(info));
} else {
+ /*
+ * We have an unusual number of message bytes to send.
+ * Load first byte into fifo, and issue SELECT with ATN and
+ * stop steps.
+ * Note: we only peek at t his message - we need the rest
+ * later on!
+ */
+ int thismsg;
+ char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg);
+
+ if (!msg || thismsg < 1)
+ printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n",
+ info->host->host_no, fas216_target(info), msglen);
+ else
+ outb(msg[0], REG_FF(info));
+
outb(CMD_SELECTATNSTOP, REG_CMD(info));
}
@@ -1070,79 +1468,95 @@
/* should now get either DISCONNECT or (FUNCTION DONE with BUS SERVICE) intr */
}
-/* Function: void fas216_done (FAS216_Info *info, unsigned int result)
+/* Function: void fas216_done(FAS216_Info *info, unsigned int result)
* Purpose : complete processing for command
* Params : info - interface that completed
* result - driver byte of result
*/
-static void fas216_done (FAS216_Info *info, unsigned int result)
+static void fas216_done(FAS216_Info *info, unsigned int result)
{
- Scsi_Cmnd *SCpnt = info->SCpnt;
+ Scsi_Cmnd *SCpnt;
- if (info->scsi.aborting) {
- printk ("scsi%d.%c: uncaught abort - returning DID_ABORT\n",
- info->host->host_no, fas216_target (info));
- result = DID_ABORT;
- info->scsi.aborting = 0;
- }
+ fas216_checkmagic(info, "fas216_done");
- info->stats.fins += 1;
+ SCpnt = info->SCpnt;
- if (SCpnt) {
- info->scsi.phase = PHASE_IDLE;
- info->SCpnt = NULL;
+ if (info->scsi.aborting) {
+ printk("scsi%d.%c: uncaught abort - returning DID_ABORT\n",
+ info->host->host_no, fas216_target(info));
+ result = DID_ABORT;
+ info->scsi.aborting = 0;
+ }
+
+ info->stats.fins += 1;
+
+ if (SCpnt) {
+ info->scsi.phase = PHASE_IDLE;
+ info->SCpnt = NULL;
- SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
+ SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
info->scsi.SCp.Status;
- /*
- * In theory, this should not happen, but just in case it does.
- */
- if (info->scsi.SCp.ptr && result == DID_OK) {
- switch (status_byte (SCpnt->result)) {
- case CHECK_CONDITION:
- case COMMAND_TERMINATED:
- case BUSY:
- case QUEUE_FULL:
- case RESERVATION_CONFLICT:
- break;
+ /*
+ * In theory, this should not happen, but just in case it does.
+ */
+ if (info->scsi.SCp.ptr && result == DID_OK) {
+ switch (SCpnt->cmnd[0]) {
+ case INQUIRY:
+ case START_STOP:
+ case READ_CAPACITY:
+ break;
- default:
- printk (KERN_ERR "scsi%d.H: incomplete data transfer "
- "detected: result=%08X command=",
- info->host->host_no, SCpnt->result);
- print_command (SCpnt->cmnd);
- }
- }
+ default:
+ switch (status_byte(SCpnt->result)) {
+ case CHECK_CONDITION:
+ case COMMAND_TERMINATED:
+ case BUSY:
+ case QUEUE_FULL:
+ case RESERVATION_CONFLICT:
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.H: incomplete data transfer "
+ "detected: res=%08X ptr=%p len=%X command=",
+ info->host->host_no, SCpnt->result,
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ print_command(SCpnt->cmnd);
+ }
+ }
+ }
#ifdef DEBUG_CONNECT
- printk ("scsi%d.%c: scsi command (%p) complete, result=%08X\n",
- info->host->host_no, fas216_target (info),
- SCpnt, SCpnt->result);
+ printk("scsi%d.%c: scsi command (%p) complete, result=%08X\n",
+ info->host->host_no, fas216_target(info),
+ SCpnt, SCpnt->result);
#endif
- if (!SCpnt->scsi_done)
- panic ("scsi%d.H: null scsi_done function in fas216_done", info->host->host_no);
+ if (!SCpnt->scsi_done)
+ panic("scsi%d.H: null scsi_done function in "
+ "fas216_done", info->host->host_no);
- clear_bit (SCpnt->target * 8 + SCpnt->lun, info->busyluns);
+ clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
- SCpnt->scsi_done (SCpnt);
- } else
- panic ("scsi%d.H: null command in fas216_done", info->host->host_no);
+ SCpnt->scsi_done(SCpnt);
+ } else
+ panic("scsi%d.H: null command in fas216_done", info->host->host_no);
- if (info->scsi.irq != NO_IRQ)
- fas216_kick (info);
+ if (info->scsi.irq != NO_IRQ)
+ fas216_kick(info);
}
-/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+/* Function: int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
* Purpose : queue a command for adapter to process.
* Params : SCpnt - Command to queue
* done - done function to call once command is complete
* Returns : 0 - success, else error
*/
-int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+ fas216_checkmagic(info, "fas216_queue_command");
+
#ifdef DEBUG_CONNECT
printk("scsi%d.%c: received queuable command (%p) %02X\n",
SCpnt->host->host_no, '0' + SCpnt->target,
@@ -1185,47 +1599,51 @@
/* add command into execute queue and let it complete under
* the drivers interrupts.
*/
- if (!queue_add_cmd_ordered (&info->queues.issue, SCpnt)) {
+ if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
SCpnt->result = DID_ERROR << 16;
- done (SCpnt);
+ done(SCpnt);
}
- save_flags_cli (flags);
+ save_flags_cli(flags);
if (!info->SCpnt || info->scsi.disconnectable)
- fas216_kick (info);
- restore_flags (flags);
+ fas216_kick(info);
+ restore_flags(flags);
} else {
/* no interrupts to rely on - we'll have to handle the
* command ourselves. For now, we give up.
*/
SCpnt->result = DID_ERROR << 16;
- done (SCpnt);
+ done(SCpnt);
}
return 0;
}
-/* Function: void fas216_internal_done (Scsi_Cmnd *SCpnt)
+/* Function: void fas216_internal_done(Scsi_Cmnd *SCpnt)
* Purpose : trigger restart of a waiting thread in fas216_command
* Params : SCpnt - Command to wake
*/
-static void fas216_internal_done (Scsi_Cmnd *SCpnt)
+static void fas216_internal_done(Scsi_Cmnd *SCpnt)
{
FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+ fas216_checkmagic(info, "fas216_internal_done");
+
info->internal_done = 1;
}
-/* Function: int fas216_command (Scsi_Cmnd *SCpnt)
+/* Function: int fas216_command(Scsi_Cmnd *SCpnt)
* Purpose : queue a command for adapter to process.
* Params : SCpnt - Command to queue
* Returns : scsi result code
*/
-int fas216_command (Scsi_Cmnd *SCpnt)
+int fas216_command(Scsi_Cmnd *SCpnt)
{
FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
unsigned long flags;
+ fas216_checkmagic(info, "fas216_command");
+
info->internal_done = 0;
- fas216_queue_command (SCpnt, fas216_internal_done);
+ fas216_queue_command(SCpnt, fas216_internal_done);
/*
* This wastes time, since we can't return until the command is
@@ -1233,13 +1651,13 @@
* However, we must re-enable interrupts, or else we'll be
* waiting forever.
*/
- save_flags (flags);
- sti ();
+ save_flags(flags);
+ sti();
while (!info->internal_done)
- barrier ();
+ barrier();
- restore_flags (flags);
+ restore_flags(flags);
return SCpnt->result;
}
@@ -1262,7 +1680,7 @@
*SCpntp1 = NULL;
SCpnt->result = result;
- SCpnt->scsi_done (SCpnt);
+ SCpnt->scsi_done(SCpnt);
}
if (SCpnt == *SCpntp2)
@@ -1309,28 +1727,33 @@
return FAILED;
}
-/* Function: int fas216_abort (Scsi_Cmnd *SCpnt)
+/* Function: int fas216_abort(Scsi_Cmnd *SCpnt)
* Purpose : abort a command if something horrible happens.
* Params : SCpnt - Command that is believed to be causing a problem.
* Returns : one of SCSI_ABORT_ macros.
*/
-int fas216_abort (Scsi_Cmnd *SCpnt)
+int fas216_abort(Scsi_Cmnd *SCpnt)
{
FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
int result = SCSI_ABORT_SNOOZE;
+ fas216_checkmagic(info, "fas216_abort");
+
info->stats.aborts += 1;
+ print_debug_list();
+ fas216_dumpinfo(info);
+ fas216_dumpstate(info);
printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no);
do {
/* If command is waiting in the issue queue, then we can
* simply remove the command and return abort status
*/
- if (queue_removecmd (&info->queues.issue, SCpnt)) {
+ if (queue_removecmd(&info->queues.issue, SCpnt)) {
SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done (SCpnt);
- printk ("command on issue queue");
+ SCpnt->scsi_done(SCpnt);
+ printk("command on issue queue");
result = SCSI_ABORT_SUCCESS;
break;
}
@@ -1338,14 +1761,14 @@
/* If the command is on the disconencted queue, we need to
* reconnect to the device
*/
- if (queue_cmdonqueue (&info->queues.disconnected, SCpnt))
- printk ("command on disconnected queue");
+ if (queue_cmdonqueue(&info->queues.disconnected, SCpnt))
+ printk("command on disconnected queue");
/* If the command is connected, we need to flag that the
* command needs to be aborted
*/
if (info->SCpnt == SCpnt)
- printk ("command executing");
+ printk("command executing");
/* If the command is pending for execution, then again
* this is simple - we remove it and report abort status
@@ -1353,14 +1776,14 @@
if (info->origSCpnt == SCpnt) {
info->origSCpnt = NULL;
SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done (SCpnt);
- printk ("command waiting for execution");
+ SCpnt->scsi_done(SCpnt);
+ printk("command waiting for execution");
result = SCSI_ABORT_SUCCESS;
break;
}
} while (0);
- printk ("\n");
+ printk("\n");
return result;
}
@@ -1371,12 +1794,15 @@
*/
static void fas216_reset_state(FAS216_Info *info)
{
+ syncneg_t negstate;
int i;
+ fas216_checkmagic(info, "fas216_reset_state");
+
/*
* Clear out all stale info in our state structure
*/
- memset (info->busyluns, 0, sizeof (info->busyluns));
+ memset(info->busyluns, 0, sizeof(info->busyluns));
msgqueue_flush(&info->scsi.msgs);
info->scsi.reconnected.target = 0;
info->scsi.reconnected.lun = 0;
@@ -1385,6 +1811,16 @@
info->scsi.last_message = 0;
info->scsi.aborting = 0;
info->scsi.phase = PHASE_IDLE;
+ info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+ if (info->host->dma_channel == NO_DMA || !info->dma.setup)
+ negstate = syncneg_invalid;
+ else
+#ifdef SCSI2_SYNC
+ negstate = syncneg_start;
+#else
+ negstate = syncneg_invalid;
+#endif
for (i = 0; i < 8; i++) {
#ifndef NO_DISCONNECTS
@@ -1392,13 +1828,9 @@
#else
info->device[i].disconnect_ok = 0;
#endif
- info->device[i].stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+ info->device[i].negstate = negstate;
+ info->device[i].stp = info->scsi.async_stp;
info->device[i].sof = 0;
-#ifdef SCSI2SYNC
- info->device[i].negstate = syncneg_start;
-#else
- info->device[i].negstate = syncneg_complete;
-#endif
}
}
@@ -1408,17 +1840,19 @@
*/
static void fas216_init_chip(FAS216_Info *info)
{
+ fas216_checkmagic(info, "fas216_init_chip");
+
outb(fas216_clockrate(info->ifcfg.clockrate), REG_CLKF(info));
outb(info->scsi.cfg[0], REG_CNTL1(info));
outb(info->scsi.cfg[1], REG_CNTL2(info));
outb(info->scsi.cfg[2], REG_CNTL3(info));
outb(info->ifcfg.select_timeout, REG_STIM(info));
outb(0, REG_SOF(info));
- outb(fas216_syncperiod(info, info->ifcfg.asyncperiod), REG_STP(info));
+ outb(info->scsi.async_stp, REG_STP(info));
outb(info->scsi.cfg[0], REG_CNTL1(info));
}
-/* Function: int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+/* Function: int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
* Purpose : resets the adapter if something horrible happens.
* Params : SCpnt - Command that is believed to be causing a problem.
* reset_flags - flags indicating reset type that is believed
@@ -1426,51 +1860,72 @@
* Returns : one of SCSI_RESET_ macros, or'd with the SCSI_RESET_*_RESET
* macros.
*/
-int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
{
FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
Scsi_Cmnd *SCptr;
int result = 0;
+ fas216_checkmagic(info, "fas216_reset");
+
+ /*
+ * Validate that command is actually on one of our queues if we're doing
+ * an asynchronous reset
+ */
+ if (reset_flags & SCSI_RESET_ASYNCHRONOUS &&
+ SCpnt &&
+ info->SCpnt != SCpnt &&
+ info->origSCpnt != SCpnt &&
+ !queue_cmdonqueue(&info->queues.disconnected, SCpnt) &&
+ !queue_cmdonqueue(&info->queues.issue, SCpnt)) {
+ printk("scsi%d: fas216_reset: asynchronous reset for unknown command\n",
+ info->host->host_no);
+ return SCSI_RESET_NOT_RUNNING;
+ }
+
info->stats.resets += 1;
+ print_debug_list();
printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no);
+ if (SCpnt)
+ printk(" for target %d ", SCpnt->target);
outb(info->scsi.cfg[3], REG_CNTL3(info));
fas216_stoptransfer(info);
- fas216_reset_state(info);
- if (reset_flags & SCSI_RESET_SUGGEST_HOST_RESET) {
+ switch (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET)) {
+ case SCSI_RESET_SUGGEST_BUS_RESET:
+ outb(CMD_RESETSCSI, REG_CMD(info));
+ outb(CMD_NOP, REG_CMD(info));
+ result |= SCSI_RESET_BUS_RESET;
+ break;
+
+ case SCSI_RESET_SUGGEST_HOST_RESET:
outb(CMD_RESETCHIP, REG_CMD(info));
outb(CMD_NOP, REG_CMD(info));
result |= SCSI_RESET_HOST_RESET;
- }
-
- if (reset_flags & SCSI_RESET_SUGGEST_BUS_RESET) {
- outb(CMD_RESETSCSI, REG_CMD(info));
- outb(CMD_NOP, REG_CMD(info));
- result |= SCSI_RESET_BUS_RESET;
- }
+ break;
- if (!(reset_flags &
- (SCSI_RESET_SUGGEST_BUS_RESET|SCSI_RESET_SUGGEST_HOST_RESET))) {
+ default:
outb(CMD_RESETCHIP, REG_CMD(info));
outb(CMD_NOP, REG_CMD(info));
outb(CMD_RESETSCSI, REG_CMD(info));
result |= SCSI_RESET_HOST_RESET | SCSI_RESET_BUS_RESET;
+ break;
}
- if (result & SCSI_RESET_HOST_RESET)
- fas216_init_chip(info);
+ udelay(300);
+ fas216_reset_state(info);
+ fas216_init_chip(info);
/*
* Signal all commands in progress have been reset
*/
- fas216_reportstatus (&info->SCpnt, &SCpnt, DID_RESET << 16);
+ fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16);
- while ((SCptr = queue_remove (&info->queues.disconnected)) != NULL)
- fas216_reportstatus (&SCptr, &SCpnt, DID_RESET << 16);
+ while ((SCptr = queue_remove(&info->queues.disconnected)) != NULL)
+ fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16);
if (SCpnt) {
/*
@@ -1483,42 +1938,45 @@
queue_removecmd(&info->queues.issue, SCpnt);
SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done (SCpnt);
+ SCpnt->scsi_done(SCpnt);
}
- printk ("\n");
+ printk("\n");
return result | SCSI_RESET_SUCCESS;
}
-/* Function: int fas216_init (struct Scsi_Host *instance)
+/* Function: int fas216_init(struct Scsi_Host *instance)
* Purpose : initialise FAS/NCR/AMD SCSI ic.
* Params : instance - a driver-specific filled-out structure
* Returns : 0 on success
*/
-int fas216_init (struct Scsi_Host *instance)
+int fas216_init(struct Scsi_Host *instance)
{
FAS216_Info *info = (FAS216_Info *)instance->hostdata;
unsigned long flags;
int target_jiffies;
+ info->magic_start = MAGIC;
+ info->magic_end = MAGIC;
+
info->host = instance;
info->scsi.cfg[0] = instance->this_id;
info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE;
- info->scsi.cfg[2] = CNTL3_ADIDCHK | CNTL3_G2CB | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->scsi.cfg[2] = info->ifcfg.cntl3 | CNTL3_ADIDCHK | CNTL3_G2CB;
info->scsi.type = "unknown";
info->SCpnt = NULL;
fas216_reset_state(info);
- memset (&info->stats, 0, sizeof (info->stats));
+ memset(&info->stats, 0, sizeof(info->stats));
- msgqueue_initialise (&info->scsi.msgs);
+ msgqueue_initialise(&info->scsi.msgs);
- if (!queue_initialise (&info->queues.issue))
+ if (!queue_initialise(&info->queues.issue))
return 1;
- if (!queue_initialise (&info->queues.disconnected)) {
- queue_free (&info->queues.issue);
+ if (!queue_initialise(&info->queues.disconnected)) {
+ queue_free(&info->queues.issue);
return 1;
}
@@ -1555,8 +2013,7 @@
break;
}
- udelay (300);
-
+ udelay(300);
/* now for the real initialisation */
fas216_init_chip(info);
@@ -1565,32 +2022,39 @@
/* scsi standard says 250ms */
target_jiffies = jiffies + (25 * HZ) / 100;
- save_flags (flags);
- sti ();
+ save_flags(flags);
+ sti();
- while (jiffies < target_jiffies) barrier ();
+ while (jiffies < target_jiffies) barrier();
- restore_flags (flags);
+ restore_flags(flags);
outb(info->scsi.cfg[0], REG_CNTL1(info));
inb(REG_INST(info));
+ /* now for the real initialisation */
+ fas216_init_chip(info);
+
+ fas216_checkmagic(info, "fas216_init");
+
return 0;
}
-/* Function: int fas216_release (struct Scsi_Host *instance)
+/* Function: int fas216_release(struct Scsi_Host *instance)
* Purpose : release all resources and put everything to bed for
* FAS/NCR/AMD SCSI ic.
* Params : instance - a driver-specific filled-out structure
* Returns : 0 on success
*/
-int fas216_release (struct Scsi_Host *instance)
+int fas216_release(struct Scsi_Host *instance)
{
FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+ fas216_checkmagic(info, "fas216_release");
+
outb(CMD_RESETCHIP, REG_CMD(info));
- queue_free (&info->queues.disconnected);
- queue_free (&info->queues.issue);
+ queue_free(&info->queues.disconnected);
+ queue_free(&info->queues.issue);
return 0;
}
@@ -1609,12 +2073,12 @@
#ifdef MODULE
-int init_module (void)
+int init_module(void)
{
return 0;
}
-void cleanup_module (void)
+void cleanup_module(void)
{
}
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov