summaryrefslogtreecommitdiffstats
path: root/drivers/acorn/scsi
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-08-25 09:12:35 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-08-25 09:12:35 +0000
commitc7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch)
tree3682407a599b8f9f03fc096298134cafba1c9b2f /drivers/acorn/scsi
parent1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff)
o Merge with Linux 2.1.116.
o New Newport console code. o New G364 console code.
Diffstat (limited to 'drivers/acorn/scsi')
-rw-r--r--drivers/acorn/scsi/Config.in1
-rw-r--r--drivers/acorn/scsi/Makefile12
-rw-r--r--drivers/acorn/scsi/acornscsi.c18
-rw-r--r--drivers/acorn/scsi/cumana_1.c9
-rw-r--r--drivers/acorn/scsi/cumana_1.h2
-rw-r--r--drivers/acorn/scsi/cumana_2.c731
-rw-r--r--drivers/acorn/scsi/cumana_2.h17
-rw-r--r--drivers/acorn/scsi/ecoscsi.c2
-rw-r--r--drivers/acorn/scsi/ecoscsi.h2
-rw-r--r--drivers/acorn/scsi/eesox.c589
-rw-r--r--drivers/acorn/scsi/eesox.h81
-rw-r--r--drivers/acorn/scsi/fas216.c1754
-rw-r--r--drivers/acorn/scsi/fas216.h27
-rw-r--r--drivers/acorn/scsi/msgqueue.c72
-rw-r--r--drivers/acorn/scsi/msgqueue.h11
-rw-r--r--drivers/acorn/scsi/oak.c9
-rw-r--r--drivers/acorn/scsi/oak.h2
-rw-r--r--drivers/acorn/scsi/powertec.c351
-rw-r--r--drivers/acorn/scsi/powertec.h16
-rw-r--r--drivers/acorn/scsi/queue.c5
20 files changed, 2626 insertions, 1085 deletions
diff --git a/drivers/acorn/scsi/Config.in b/drivers/acorn/scsi/Config.in
index 7b6411746..5b8d14429 100644
--- a/drivers/acorn/scsi/Config.in
+++ b/drivers/acorn/scsi/Config.in
@@ -8,6 +8,7 @@ if [ "$CONFIG_SCSI_ACORNSCSI_3" != "n" ]; then
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI
+ dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI
dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI
comment 'The following drives are not fully supported'
diff --git a/drivers/acorn/scsi/Makefile b/drivers/acorn/scsi/Makefile
index f810450df..f69638530 100644
--- a/drivers/acorn/scsi/Makefile
+++ b/drivers/acorn/scsi/Makefile
@@ -72,6 +72,18 @@ else
endif
endif
+ifeq ($(CONFIG_SCSI_EESOXSCSI),y)
+ L_OBJS += eesox.o
+ CONFIG_QUEUE_BUILTIN=y
+ CONFIG_FAS216_BUILTIN=y
+else
+ ifeq ($(CONFIG_SCSI_EESOXSCSI),m)
+ M_OBJS += eesox.o
+ CONFIG_QUEUE_MODULE=y
+ CONFIG_FAS216_MODULE=y
+ endif
+endif
+
ifeq ($(CONFIG_QUEUE_BUILTIN),y)
LX_OBJS += queue.o msgqueue.o
else
diff --git a/drivers/acorn/scsi/acornscsi.c b/drivers/acorn/scsi/acornscsi.c
index d2b2cff25..a203bc5c2 100644
--- a/drivers/acorn/scsi/acornscsi.c
+++ b/drivers/acorn/scsi/acornscsi.c
@@ -20,6 +20,7 @@
* reconnect race condition causing a warning message.
* 12-Oct-1997 RMK Added catch for re-entering interrupt routine.
* 15-Oct-1997 RMK Improved handling of commands.
+ * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h.
*/
#define DEBUG_NO_WRITE 1
#define DEBUG_QUEUES 2
@@ -136,8 +137,9 @@
#include <linux/stat.h>
#include <linux/ioport.h>
#include <linux/blk.h>
+#include <linux/delay.h>
+
#include <asm/bitops.h>
-#include <asm/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/ecard.h>
@@ -2726,7 +2728,7 @@ char *acornscsi_info(struct Scsi_Host *host)
p = string;
- p += sprintf (string, "%s at port %X irq %d v%d.%d.%d"
+ p += sprintf (string, "%s at port %lX irq %d v%d.%d.%d"
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
" SYNC"
#endif
@@ -2784,12 +2786,12 @@ int acornscsi_proc_info(char *buffer, char **start, off_t offset,
host->dma.io_port, host->scsi.irq);
#endif
- p += sprintf (p, "Statistics:\n",
- "Queued commands: %-10d Issued commands: %-10d\n"
- "Done commands : %-10d Reads : %-10d\n"
- "Writes : %-10d Others : %-10d\n"
- "Disconnects : %-10d Aborts : %-10d\n"
- "Resets : %-10d\n\nLast phases:",
+ p += sprintf (p, "Statistics:\n"
+ "Queued commands: %-10u Issued commands: %-10u\n"
+ "Done commands : %-10u Reads : %-10u\n"
+ "Writes : %-10u Others : %-10u\n"
+ "Disconnects : %-10u Aborts : %-10u\n"
+ "Resets : %-10u\n\nLast phases:",
host->stats.queues, host->stats.removes,
host->stats.fins, host->stats.reads,
host->stats.writes, host->stats.miscs,
diff --git a/drivers/acorn/scsi/cumana_1.c b/drivers/acorn/scsi/cumana_1.c
index 1dec51930..df00d6bff 100644
--- a/drivers/acorn/scsi/cumana_1.c
+++ b/drivers/acorn/scsi/cumana_1.c
@@ -34,6 +34,11 @@
/*
* $Log: cumana_1.c,v $
+ * Revision 1.3 1998/05/03 20:45:32 alan
+ * ARM SCSI update. This adds the eesox driver and massively updates the
+ * Cumana driver. The folks who bought cumana arent anal retentive all
+ * docs are secret weenies so now there are docs ..
+ *
* Revision 1.2 1998/03/08 05:49:46 davem
* Merge to 2.1.89
*
@@ -123,7 +128,7 @@ int cumanascsi_detect(Scsi_Host_Template * tpnt)
outb(0x00, instance->io_port - 577);
if (instance->irq != IRQ_NONE)
- if (request_irq(instance->irq, cumanascsi_intr, SA_INTERRUPT, "CumanaSCSI-1", NULL)) {
+ if (request_irq(instance->irq, do_cumanascsi_intr, SA_INTERRUPT, "CumanaSCSI-1", NULL)) {
printk("scsi%d: IRQ%d not free, interrupts disabled\n",
instance->host_no, instance->irq);
instance->irq = IRQ_NONE;
@@ -134,7 +139,7 @@ int cumanascsi_detect(Scsi_Host_Template * tpnt)
printk("scsi%d: please jumper the board for a free IRQ.\n", instance->host_no);
}
- printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+ printk("scsi%d: at port %lX irq", instance->host_no, instance->io_port);
if (instance->irq == IRQ_NONE)
printk ("s disabled");
else
diff --git a/drivers/acorn/scsi/cumana_1.h b/drivers/acorn/scsi/cumana_1.h
index 447ce3744..3767c4036 100644
--- a/drivers/acorn/scsi/cumana_1.h
+++ b/drivers/acorn/scsi/cumana_1.h
@@ -90,7 +90,7 @@ use_clustering: DISABLE_CLUSTERING \
#define NCR5380_read(reg) cumanascsi_read(_instance, reg)
#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value)
-#define NCR5380_intr cumanascsi_intr
+#define do_NCR5380_intr do_cumanascsi_intr
#define NCR5380_queue_command cumanascsi_queue_command
#define NCR5380_abort cumanascsi_abort
#define NCR5380_reset cumanascsi_reset
diff --git a/drivers/acorn/scsi/cumana_2.c b/drivers/acorn/scsi/cumana_2.c
index ca048138c..b01684684 100644
--- a/drivers/acorn/scsi/cumana_2.c
+++ b/drivers/acorn/scsi/cumana_2.c
@@ -1,15 +1,14 @@
/*
* linux/arch/arm/drivers/scsi/cumana_2.c
*
- * Copyright (C) 1997,1998 Russell King
- *
- * This driver is based on experimentation. Hence, it may have made
- * assumptions about the particular card that I have available, and
- * may not be reliable!
+ * Copyright (C) 1997-1998 Russell King
*
* Changelog:
* 30-08-1997 RMK 0.0.0 Created, READONLY version
* 22-01-1998 RMK 0.0.1 Updated to 2.1.80
+ * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
+ * 02-05-1998 RMK 0.0.2 Updated & added DMA support
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
*/
#include <linux/module.h>
@@ -21,354 +20,564 @@
#include <linux/proc_fs.h>
#include <linux/unistd.h>
#include <linux/stat.h>
+#include <linux/delay.h>
-#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/ecard.h>
+#include <asm/pgtable.h>
#include "../../scsi/sd.h"
#include "../../scsi/hosts.h"
#include "cumana_2.h"
-#include "fas216.h"
-
-/* Hmm - this should go somewhere else */
-#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
/* Configuration */
-#define XTALFREQ 40
-#define INT_POLARITY CTRL_INT_HIGH
+#define CUMANASCSI2_XTALFREQ 40
+#define CUMANASCSI2_ASYNC_PERIOD 200
+#define CUMANASCSI2_SYNC_DEPTH 8
/*
* List of devices that the driver will recognise
*/
-#define CUMANASCSI2_LIST { MANU_CUMANA, PROD_CUMANA_SCSI_2 }
+#define CUMANASCSI2_LIST { MANU_CUMANA, PROD_CUMANA_SCSI_2 }
+
+#define CUMANASCSI2_STATUS (0)
+#define STATUS_INT (1 << 0)
+#define STATUS_DRQ (1 << 1)
+#define STATUS_LATCHED (1 << 3)
+
+#define CUMANASCSI2_ALATCH (5)
+#define ALATCH_ENA_INT (3)
+#define ALATCH_DIS_INT (2)
+#define ALATCH_ENA_TERM (5)
+#define ALATCH_DIS_TERM (4)
+#define ALATCH_ENA_BIT32 (11)
+#define ALATCH_DIS_BIT32 (10)
+#define ALATCH_ENA_DMA (13)
+#define ALATCH_DIS_DMA (12)
+#define ALATCH_DMA_OUT (15)
+#define ALATCH_DMA_IN (14)
+
+#define CUMANASCSI2_PSEUDODMA (0x80)
+
+#define CUMANASCSI2_FAS216_OFFSET (0xc0)
+#define CUMANASCSI2_FAS216_SHIFT 0
/*
* Version
*/
#define VER_MAJOR 0
#define VER_MINOR 0
-#define VER_PATCH 1
+#define VER_PATCH 2
static struct expansion_card *ecs[MAX_ECARDS];
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Cumana SCSI II driver");
+MODULE_PARM(term, "1-8i");
+MODULE_PARM_DESC(term, "SCSI bus termination");
+
+/*
+ * Use term=0,1,0,0,0 to turn terminators on/off
+ */
+int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
static struct proc_dir_entry proc_scsi_cumanascsi_2 = {
PROC_SCSI_QLOGICFAS, 6, "cumanascs2",
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-/*
- * Function: void cumanascsi_2_intr (int irq, void *dev_id, struct pt_regs *regs)
- * Purpose : handle interrupts from Cumana SCSI 2 card
- * Params : irq - interrupt number
- * dev_id - user-defined (Scsi_Host structure)
- * regs - processor registers at interrupt
+/* Prototype: void cumanascsi_2_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on Cumana SCSI 2 card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
*/
-static void cumanascsi_2_intr (int irq, void *dev_id, struct pt_regs *regs)
+static void
+cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr)
{
- struct Scsi_Host *instance = (struct Scsi_Host *)dev_id;
+ unsigned int port = (unsigned int)ec->irq_data;
+ outb(ALATCH_ENA_INT, port);
+}
- fas216_intr (instance);
+/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on Cumana SCSI 2 card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr)
+{
+ unsigned int port = (unsigned int)ec->irq_data;
+ outb(ALATCH_DIS_INT, port);
}
-/*
- * Function: int cumanascsi_2_dma_setup (instance, SCpnt, direction)
- * Purpose : initialises DMA/PIO
- * Params : instance - host
- * SCpnt - command
- * direction - DMA on to/off of card
- * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
+static const expansioncard_ops_t cumanascsi_2_ops = {
+ cumanascsi_2_irqenable,
+ cumanascsi_2_irqdisable,
+ NULL,
+ NULL
+};
+
+/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)
+ * Purpose : Turn the Cumana SCSI 2 terminators on or off
+ * Params : host - card to turn on/off
+ * : on_off - !0 to turn on, 0 to turn off
*/
-static fasdmatype_t cumanascsi_2_dma_setup (struct Scsi_Host *instance, Scsi_Pointer *SCp, fasdmadir_t direction)
+static void
+cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off)
{
- /*
- * We don't do DMA
- */
- return fasdma_pseudo;
+ CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
+
+ if (on_off) {
+ info->terms = 1;
+ outb (ALATCH_ENA_TERM, info->alatch);
+ } else {
+ info->terms = 0;
+ outb (ALATCH_DIS_TERM, info->alatch);
+ }
+}
+
+/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)
+ * Purpose : handle interrupts from Cumana SCSI 2 card
+ * Params : irq - interrupt number
+ * dev_id - user-defined (Scsi_Host structure)
+ * regs - processor registers at interrupt
+ */
+static void
+cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
+
+ fas216_intr(host);
+}
+
+static void
+cumanascsi_2_invalidate(char *addr, long len, fasdmadir_t direction)
+{
+ unsigned int page;
+
+ if (direction == DMA_OUT) {
+ for (page = (unsigned int) addr; len > 0;
+ page += PAGE_SIZE, len -= PAGE_SIZE)
+ flush_page_to_ram(page);
+ } else
+ flush_cache_range(current->mm, (unsigned long)addr,
+ (unsigned long)addr + len);
+}
+
+/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : type of transfer to be performed
+ */
+static fasdmatype_t
+cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
+ int dmach = host->dma_channel;
+
+ outb(ALATCH_DIS_DMA, info->alatch);
+
+ if (dmach != NO_DMA &&
+ (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
+ int buf;
+
+ for (buf = 1; buf <= SCp->buffers_residual &&
+ buf < NR_SG; buf++) {
+ info->dmasg[buf].address = __virt_to_bus(
+ (unsigned long)SCp->buffer[buf].address);
+ info->dmasg[buf].length = SCp->buffer[buf].length;
+
+ cumanascsi_2_invalidate(SCp->buffer[buf].address,
+ SCp->buffer[buf].length,
+ direction);
+ }
+
+ info->dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
+ info->dmasg[0].length = SCp->this_residual;
+ cumanascsi_2_invalidate(SCp->ptr,
+ SCp->this_residual, direction);
+
+ disable_dma(dmach);
+ set_dma_sg(dmach, info->dmasg, buf);
+ if (direction == DMA_OUT) {
+ outb(ALATCH_DMA_OUT, info->alatch);
+ set_dma_mode(dmach, DMA_MODE_WRITE);
+ } else {
+ outb(ALATCH_DMA_IN, info->alatch);
+ set_dma_mode(dmach, DMA_MODE_READ);
+ }
+ enable_dma(dmach);
+ outb(ALATCH_ENA_DMA, info->alatch);
+ outb(ALATCH_DIS_BIT32, info->alatch);
+ return fasdma_real_all;
+ }
+
+ /*
+ * If we're not doing DMA,
+ * we'll do pseudo DMA
+ */
+ return fasdma_pio;
}
/*
- * Function: int cumanascsi_2_dma_pseudo (instance, SCpnt, direction, transfer)
- * Purpose : handles pseudo DMA
- * Params : instance - host
- * SCpnt - command
- * direction - DMA on to/off of card
- * transfer - minimum number of bytes we expect to transfer
- * Returns : bytes transfered
+ * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * transfer - minimum number of bytes we expect to transfer
*/
-static int
-cumanascsi_2_dma_pseudo (struct Scsi_Host *instance, Scsi_Pointer *SCp,
- fasdmadir_t direction, int transfer)
+static void
+cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, int transfer)
{
- CumanaScsi2_Info *info = (CumanaScsi2_Info *)instance->hostdata;
- unsigned int length;
- unsigned char *addr;
+ CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
+ unsigned int length;
+ unsigned char *addr;
- length = SCp->this_residual;
- addr = SCp->ptr;
+ length = SCp->this_residual;
+ addr = SCp->ptr;
- if (direction == DMA_OUT)
+ if (direction == DMA_OUT)
#if 0
- while (length > 1) {
- unsigned long word;
+ while (length > 1) {
+ unsigned long word;
+ unsigned int status = inb(info->status);
+ if (status & STATUS_INT)
+ goto end;
- if (inb (REG_STAT(&info->info)) & STAT_INT)
- goto end;
+ if (!(status & STATUS_DRQ))
+ continue;
- if (!(inb (info->cstatus) & CSTATUS_DRQ))
- continue;
-
- word = *addr | (*addr + 1) << 8;
- outw (info->dmaarea);
- addr += 2;
- length -= 2;
- }
+ word = *addr | *(addr + 1) << 8;
+ outw (info->dmaarea);
+ addr += 2;
+ length -= 2;
+ }
#else
- printk ("PSEUDO_OUT???\n");
+ printk ("PSEUDO_OUT???\n");
#endif
- else {
- if (transfer && (transfer & 255)) {
- while (length >= 256) {
- if (inb (REG_STAT(&info->info)) & STAT_INT)
- goto end;
-
- if (!(inb (info->cstatus) & CSTATUS_DRQ))
- continue;
-
- insw (info->dmaarea, addr, 256 >> 1);
- addr += 256;
- length -= 256;
- }
- }
-
- while (length > 0) {
- unsigned long word;
+ else {
+ if (transfer && (transfer & 255)) {
+ while (length >= 256) {
+ unsigned int status = inb(info->status);
- if (inb (REG_STAT(&info->info)) & STAT_INT)
- goto end;
+ if (status & STATUS_INT)
+ goto end;
- if (!(inb (info->cstatus) & CSTATUS_DRQ))
- continue;
-
- word = inw (info->dmaarea);
- *addr++ = word;
- if (--length > 0) {
- *addr++ = word >> 8;
- length --;
- }
+ if (!(status & STATUS_DRQ))
+ continue;
+
+ insw(info->dmaarea, addr, 256 >> 1);
+ addr += 256;
+ length -= 256;
+ }
+ }
+
+ while (length > 0) {
+ unsigned long word;
+ unsigned int status = inb(info->status);
+
+ if (status & STATUS_INT)
+ goto end;
+
+ if (!(status & STATUS_DRQ))
+ continue;
+
+ word = inw (info->dmaarea);
+ *addr++ = word;
+ if (--length > 0) {
+ *addr++ = word >> 8;
+ length --;
+ }
+ }
}
- }
end:
- return SCp->this_residual - length;
}
-/*
- * Function: int cumanascsi_2_dma_stop (instance, SCpnt)
- * Purpose : stops DMA/PIO
- * Params : instance - host
- * SCpnt - command
+/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
*/
-static void cumanascsi_2_dma_stop (struct Scsi_Host *instance, Scsi_Pointer *SCp)
+static void
+cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
{
- /*
- * no DMA to stop
- */
+ CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
+ if (host->dma_channel != NO_DMA) {
+ outb(ALATCH_DIS_DMA, info->alatch);
+ disable_dma(host->dma_channel);
+ }
}
-/*
- * Function: int cumanascsi_2_detect (Scsi_Host_Template * tpnt)
- * Purpose : initialises Cumana SCSI 2 driver
- * Params : tpnt - template for this SCSI adapter
- * Returns : >0 if host found, 0 otherwise.
+/* Prototype: int cumanascsi_2_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises Cumana SCSI 2 driver
+ * Params : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
*/
-int cumanascsi_2_detect (Scsi_Host_Template *tpnt)
+int
+cumanascsi_2_detect(Scsi_Host_Template *tpnt)
{
- static const card_ids cumanascsi_2_cids[] = { CUMANASCSI2_LIST, { 0xffff, 0xffff} };
- int count = 0;
- struct Scsi_Host *instance;
+ static const card_ids cumanascsi_2_cids[] =
+ { CUMANASCSI2_LIST, { 0xffff, 0xffff} };
+ int count = 0;
+ struct Scsi_Host *host;
- tpnt->proc_dir = &proc_scsi_cumanascsi_2;
- memset (ecs, 0, sizeof (ecs));
-
- ecard_startfind ();
-
- while (1) {
- CumanaScsi2_Info *info;
-
- ecs[count] = ecard_find (0, cumanascsi_2_cids);
- if (!ecs[count])
- break;
-
- ecard_claim (ecs[count]);
-
- instance = scsi_register (tpnt, sizeof (CumanaScsi2_Info));
- if (!instance) {
- ecard_release (ecs[count]);
- break;
+ tpnt->proc_dir = &proc_scsi_cumanascsi_2;
+ memset(ecs, 0, sizeof (ecs));
+
+ ecard_startfind();
+
+ while (1) {
+ CumanaScsi2_Info *info;
+
+ ecs[count] = ecard_find(0, cumanascsi_2_cids);
+ if (!ecs[count])
+ break;
+
+ ecard_claim(ecs[count]);
+
+ host = scsi_register(tpnt, sizeof (CumanaScsi2_Info));
+ if (!host) {
+ ecard_release(ecs[count]);
+ break;
+ }
+
+ host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0);
+ host->irq = ecs[count]->irq;
+ host->dma_channel = ecs[count]->dma;
+ info = (CumanaScsi2_Info *)host->hostdata;
+
+ info->terms = term[count] ? 1 : 0;
+ cumanascsi_2_terminator_ctl(host, info->terms);
+
+ info->info.scsi.io_port = host->io_port + CUMANASCSI2_FAS216_OFFSET;
+ info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT;
+ info->info.scsi.irq = host->irq;
+ info->info.ifcfg.clockrate = CUMANASCSI2_XTALFREQ;
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = CUMANASCSI2_ASYNC_PERIOD;
+ info->info.ifcfg.sync_max_depth = CUMANASCSI2_SYNC_DEPTH;
+ info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 1;
+ info->info.dma.setup = cumanascsi_2_dma_setup;
+ info->info.dma.pseudo = cumanascsi_2_dma_pseudo;
+ info->info.dma.stop = cumanascsi_2_dma_stop;
+ info->dmaarea = host->io_port + CUMANASCSI2_PSEUDODMA;
+ info->status = host->io_port + CUMANASCSI2_STATUS;
+ info->alatch = host->io_port + CUMANASCSI2_ALATCH;
+
+ ecs[count]->irqaddr = (unsigned char *)ioaddr(info->status);
+ ecs[count]->irqmask = STATUS_INT;
+ ecs[count]->irq_data = (void *)info->alatch;
+ ecs[count]->ops = (expansioncard_ops_t *)&cumanascsi_2_ops;
+
+ request_region(host->io_port + CUMANASCSI2_FAS216_OFFSET,
+ 16 << CUMANASCSI2_FAS216_SHIFT, "cumanascsi2-fas");
+
+ if (host->irq != NO_IRQ &&
+ request_irq(host->irq, cumanascsi_2_intr,
+ SA_INTERRUPT, "cumanascsi2", host)) {
+ printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+ host->host_no, host->irq);
+ host->irq = NO_IRQ;
+ info->info.scsi.irq = NO_IRQ;
+ }
+
+ if (host->dma_channel != NO_DMA &&
+ request_dma(host->dma_channel, "cumanascsi2")) {
+ printk("scsi%d: DMA%d not free, DMA disabled\n",
+ host->host_no, host->dma_channel);
+ host->dma_channel = NO_DMA;
+ }
+
+ fas216_init(host);
+ ++count;
}
+ return count;
+}
- instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0);
- instance->irq = ecs[count]->irq;
+/* Prototype: int cumanascsi_2_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params : host - driver host structure to return info for.
+ */
+int cumanascsi_2_release(struct Scsi_Host *host)
+{
+ int i;
- ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(instance->io_port);
- ecs[count]->irqmask = CSTATUS_IRQ;
+ fas216_release(host);
- request_region (instance->io_port , 1, "cumanascsi2-stat");
- request_region (instance->io_port + 128, 64, "cumanascsi2-dma");
- request_region (instance->io_port + 192, 16, "cumanascsi2-fas");
- if (request_irq (instance->irq, cumanascsi_2_intr, SA_INTERRUPT, "cumanascsi2", instance)) {
- printk ("scsi%d: IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- }
+ if (host->irq != NO_IRQ)
+ free_irq(host->irq, host);
+ if (host->dma_channel != NO_DMA)
+ free_dma(host->dma_channel);
+ release_region(host->io_port + CUMANASCSI2_FAS216_OFFSET,
+ 16 << CUMANASCSI2_FAS216_SHIFT);
- info = (CumanaScsi2_Info *)instance->hostdata;
- info->info.scsi.io_port = instance->io_port + 192;
- info->info.scsi.irq = instance->irq;
- info->info.ifcfg.clockrate = XTALFREQ;
- info->info.ifcfg.select_timeout = 255;
- info->info.dma.setup = cumanascsi_2_dma_setup;
- info->info.dma.pseudo = cumanascsi_2_dma_pseudo;
- info->info.dma.stop = cumanascsi_2_dma_stop;
- info->dmaarea = instance->io_port + 128;
- info->cstatus = instance->io_port;
-
- fas216_init (instance);
- ++count;
- }
- return count;
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ecs[i] && host->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
+ ecard_release (ecs[i]);
+ return 0;
}
-/*
- * Function: int cumanascsi_2_release (struct Scsi_Host * host)
- * Purpose : releases all resources used by this adapter
- * Params : host - driver host structure to return info for.
- * Returns : nothing
+/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
*/
-int cumanascsi_2_release (struct Scsi_Host *instance)
+const char *cumanascsi_2_info(struct Scsi_Host *host)
{
- int i;
+ CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
+ static char string[100], *p;
- fas216_release (instance);
+ p = string;
+ p += sprintf(string, "%s at port %lX ",
+ host->hostt->name, host->io_port);
- if (instance->irq != 255)
- free_irq (instance->irq, instance);
- release_region (instance->io_port, 1);
- release_region (instance->io_port + 128, 32);
- release_region (instance->io_port + 192, 16);
+ if (host->irq != NO_IRQ)
+ p += sprintf(p, "irq %d ", host->irq);
+ else
+ p += sprintf(p, "NO IRQ ");
- for (i = 0; i < MAX_ECARDS; i++)
- if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
- ecard_release (ecs[i]);
- return 0;
+ if (host->dma_channel != NO_DMA)
+ p += sprintf(p, "dma %d ", host->dma_channel);
+ else
+ p += sprintf(p, "NO DMA ");
+
+ p += sprintf(p, "v%d.%d.%d scsi %s",
+ VER_MAJOR, VER_MINOR, VER_PATCH,
+ info->info.scsi.type);
+
+ p += sprintf(p, " terminators %s",
+ info->terms ? "on" : "off");
+
+ return string;
}
-/*
- * Function: const char *cumanascsi_2_info (struct Scsi_Host * host)
- * Purpose : returns a descriptive string about this interface,
- * Params : host - driver host structure to return info for.
- * Returns : pointer to a static buffer containing null terminated string.
+/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+ * Purpose : Set a driver specific function
+ * Params : host - host to setup
+ * : buffer - buffer containing string describing operation
+ * : length - length of string
+ * Returns : -EINVAL, or 0
*/
-const char *cumanascsi_2_info (struct Scsi_Host *host)
+static int
+cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
{
- CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
- static char string[100], *p;
-
- p = string;
- p += sprintf (string, "%s at port %X irq %d v%d.%d.%d scsi %s",
- host->hostt->name, host->io_port, host->irq,
- VER_MAJOR, VER_MINOR, VER_PATCH,
- info->info.scsi.type);
-
- return string;
+ int ret = length;
+
+ if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) {
+ buffer += 11;
+ length -= 11;
+
+ if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
+ if (buffer[5] == '1')
+ cumanascsi_2_terminator_ctl(host, 1);
+ else if (buffer[5] == '0')
+ cumanascsi_2_terminator_ctl(host, 0);
+ else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+
+ return ret;
}
-/*
- * Function: int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset,
+/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset,
* int length, int host_no, int inout)
- * Purpose : Return information about the driver to a user process accessing
- * the /proc filesystem.
- * Params : buffer - a buffer to write information to
- * start - a pointer into this buffer set by this routine to the start
- * of the required information.
- * offset - offset into information that we have read upto.
- * length - length of buffer
- * host_no - host number to return information for
- * inout - 0 for reading, 1 for writing.
- * Returns : length of data written to buffer.
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
*/
int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset,
int length, int host_no, int inout)
{
- int pos, begin;
- struct Scsi_Host *host = scsi_hostlist;
- CumanaScsi2_Info *info;
- Scsi_Device *scd;
-
- while (host) {
- if (host->host_no == host_no)
- break;
- host = host->next;
- }
- if (!host)
- return 0;
+ int pos, begin;
+ struct Scsi_Host *host = scsi_hostlist;
+ CumanaScsi2_Info *info;
+ Scsi_Device *scd;
+
+ while (host) {
+ if (host->host_no == host_no)
+ break;
+ host = host->next;
+ }
+ if (!host)
+ return 0;
+
+ if (inout == 1)
+ return cumanascsi_2_set_proc_info(host, buffer, length);
- info = (CumanaScsi2_Info *)host->hostdata;
- if (inout == 1)
- return -EINVAL;
+ info = (CumanaScsi2_Info *)host->hostdata;
- begin = 0;
- pos = sprintf (buffer,
+ begin = 0;
+ pos = sprintf(buffer,
"Cumana SCSI II driver version %d.%d.%d\n",
VER_MAJOR, VER_MINOR, VER_PATCH);
- pos += sprintf (buffer + pos,
- "Address: %08X IRQ : %d\n"
- "FAS : %s\n\n"
+ pos += sprintf(buffer + pos,
+ "Address: %08lX IRQ : %d DMA : %d\n"
+ "FAS : %-10s TERM: %-3s\n\n"
"Statistics:\n",
- host->io_port, host->irq, info->info.scsi.type);
-
- pos += sprintf (buffer+pos,
- "Queued commands: %-10ld Issued commands: %-10ld\n"
- "Done commands : %-10ld Reads : %-10ld\n"
- "Writes : %-10ld Others : %-10ld\n"
- "Disconnects : %-10ld Aborts : %-10ld\n"
- "Resets : %-10ld\n",
- info->info.stats.queues, info->info.stats.removes,
- info->info.stats.fins, info->info.stats.reads,
- info->info.stats.writes, info->info.stats.miscs,
- info->info.stats.disconnects, info->info.stats.aborts,
- info->info.stats.resets);
-
- pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
-
- for (scd = host->host_queue; scd; scd = scd->next) {
- int len;
-
- proc_print_scsidevice (scd, buffer, &len, pos);
- pos += len;
- pos += sprintf (buffer+pos, "Extensions: ");
- if (scd->tagged_supported)
- pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
- scd->tagged_queue ? "en" : "dis",
- scd->current_tag);
- pos += sprintf (buffer+pos, "\n");
-
- if (pos + begin < offset) {
- begin += pos;
- pos = 0;
+ host->io_port, host->irq, host->dma_channel,
+ info->info.scsi.type, info->terms ? "on" : "off");
+
+ pos += sprintf(buffer+pos,
+ "Queued commands: %-10u Issued commands: %-10u\n"
+ "Done commands : %-10u Reads : %-10u\n"
+ "Writes : %-10u Others : %-10u\n"
+ "Disconnects : %-10u Aborts : %-10u\n"
+ "Resets : %-10u\n",
+ info->info.stats.queues, info->info.stats.removes,
+ info->info.stats.fins, info->info.stats.reads,
+ info->info.stats.writes, info->info.stats.miscs,
+ info->info.stats.disconnects, info->info.stats.aborts,
+ info->info.stats.resets);
+
+ pos += sprintf(buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+
+ for (scd = host->host_queue; scd; scd = scd->next) {
+ int len;
+
+ proc_print_scsidevice(scd, buffer, &len, pos);
+ pos += len;
+ pos += sprintf(buffer+pos, "Extensions: ");
+ if (scd->tagged_supported)
+ pos += sprintf(buffer+pos, "TAG %sabled [%d] ",
+ scd->tagged_queue ? "en" : "dis",
+ scd->current_tag);
+ pos += sprintf(buffer+pos, "\n");
+
+ if (pos + begin < offset) {
+ begin += pos;
+ pos = 0;
+ }
+ if (pos + begin > offset + length)
+ break;
}
- if (pos + begin > offset + length)
- break;
- }
- *start = buffer + (offset - begin);
- pos -= offset - begin;
- if (pos > length)
- pos = length;
+ *start = buffer + (offset - begin);
+ pos -= offset - begin;
+ if (pos > length)
+ pos = length;
- return pos;
+ return pos;
}
#ifdef MODULE
diff --git a/drivers/acorn/scsi/cumana_2.h b/drivers/acorn/scsi/cumana_2.h
index b2914958e..66b374019 100644
--- a/drivers/acorn/scsi/cumana_2.h
+++ b/drivers/acorn/scsi/cumana_2.h
@@ -1,7 +1,7 @@
/*
* Cumana SCSI II driver
*
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1998 Russell King
*/
#ifndef CUMANA_2_H
#define CUMANA_2_H
@@ -57,12 +57,19 @@ use_clustering: DISABLE_CLUSTERING \
#ifndef HOSTS_C
+#include <asm/dma.h>
+
+#define NR_SG 256
+
typedef struct {
- FAS216_Info info;
+ FAS216_Info info;
- /* other info... */
- unsigned int cstatus; /* card status register */
- unsigned int dmaarea; /* Pseudo DMA area */
+ /* other info... */
+ unsigned int status; /* card status register */
+ unsigned int alatch; /* Control register */
+ unsigned int terms; /* Terminator state */
+ unsigned int dmaarea; /* Pseudo DMA area */
+ dmasg_t dmasg[NR_SG]; /* Scatter DMA list */
} CumanaScsi2_Info;
#define CSTATUS_IRQ (1 << 0)
diff --git a/drivers/acorn/scsi/ecoscsi.c b/drivers/acorn/scsi/ecoscsi.c
index 9346ed4f8..1d1d0628c 100644
--- a/drivers/acorn/scsi/ecoscsi.c
+++ b/drivers/acorn/scsi/ecoscsi.c
@@ -131,7 +131,7 @@ int ecoscsi_detect(Scsi_Host_Template * tpnt)
request_region (instance->io_port, instance->n_io_port, "ecoscsi");
if (instance->irq != IRQ_NONE)
- if (request_irq(instance->irq, ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) {
+ if (request_irq(instance->irq, do_ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) {
printk("scsi%d: IRQ%d not free, interrupts disabled\n",
instance->host_no, instance->irq);
instance->irq = IRQ_NONE;
diff --git a/drivers/acorn/scsi/ecoscsi.h b/drivers/acorn/scsi/ecoscsi.h
index 17a349173..ede468f2a 100644
--- a/drivers/acorn/scsi/ecoscsi.h
+++ b/drivers/acorn/scsi/ecoscsi.h
@@ -81,7 +81,7 @@ use_clustering: DISABLE_CLUSTERING \
#define NCR5380_read(reg) ecoscsi_read(_instance, reg)
#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value)
-#define NCR5380_intr ecoscsi_intr
+#define do_NCR5380_intr do_ecoscsi_intr
#define NCR5380_queue_command ecoscsi_queue_command
#define NCR5380_abort ecoscsi_abort
#define NCR5380_reset ecoscsi_reset
diff --git a/drivers/acorn/scsi/eesox.c b/drivers/acorn/scsi/eesox.c
new file mode 100644
index 000000000..b4884de11
--- /dev/null
+++ b/drivers/acorn/scsi/eesox.c
@@ -0,0 +1,589 @@
+/*
+ * linux/arch/arm/drivers/scsi/eesox.c
+ *
+ * Copyright (C) 1997-1998 Russell King
+ *
+ * This driver is based on experimentation. Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ * 01-10-1997 RMK Created, READONLY version
+ * 15-02-1998 RMK READ/WRITE version
+ * added DMA support and hardware definitions
+ * 14-03-1998 RMK Updated DMA support
+ * Added terminator control
+ * 15-04-1998 RMK Only do PIO if FAS216 will allow it.
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/pgtable.h>
+
+#include "../../scsi/sd.h"
+#include "../../scsi/hosts.h"
+#include "eesox.h"
+
+#define NO_IRQ 255
+#define NO_DMA 255
+
+/* Configuration */
+#define EESOX_XTALFREQ 40
+#define EESOX_ASYNC_PERIOD 200
+#define EESOX_SYNC_DEPTH 7
+
+/*
+ * List of devices that the driver will recognise
+ */
+#define EESOXSCSI_LIST { MANU_EESOX, PROD_EESOX_SCSI2 }
+
+#define EESOX_FAS216_OFFSET 0xc00
+#define EESOX_FAS216_SHIFT 3
+
+#define EESOX_STATUS 0xa00
+#define EESOX_STAT_INTR 0x01
+#define EESOX_STAT_DMA 0x02
+
+#define EESOX_CONTROL 0xa00
+#define EESOX_INTR_ENABLE 0x04
+#define EESOX_TERM_ENABLE 0x02
+#define EESOX_RESET 0x01
+
+#define EESOX_DMA_OFFSET 0xe00
+
+/*
+ * Version
+ */
+#define VER_MAJOR 0
+#define VER_MINOR 0
+#define VER_PATCH 2
+
+static struct expansion_card *ecs[MAX_ECARDS];
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("EESOX SCSI driver");
+MODULE_PARM(term, "1-8i");
+MODULE_PARM_DESC(term, "SCSI bus termination");
+
+/*
+ * Use term=0,1,0,0,0 to turn terminators on/off
+ */
+int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
+static struct proc_dir_entry proc_scsi_eesox = {
+ PROC_SCSI_QLOGICISP, 5, "eesox",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* Prototype: void eesoxscsi_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on EESOX SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
+{
+ struct control *control = (struct control *)ec->irq_data;
+
+ control->control |= EESOX_INTR_ENABLE;
+
+ outb(control->control, control->io_port);
+}
+
+/* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on EESOX SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
+{
+ struct control *control = (struct control *)ec->irq_data;
+
+ control->control &= ~EESOX_INTR_ENABLE;
+
+ outb(control->control, control->io_port);
+}
+
+static const expansioncard_ops_t eesoxscsi_ops = {
+ eesoxscsi_irqenable,
+ eesoxscsi_irqdisable,
+ NULL,
+ NULL
+};
+
+/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
+ * Purpose : Turn the EESOX SCSI terminators on or off
+ * Params : host - card to turn on/off
+ * : on_off - !0 to turn on, 0 to turn off
+ */
+static void
+eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
+{
+ EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
+ unsigned long flags;
+
+ save_flags_cli(flags);
+ if (on_off)
+ info->control.control |= EESOX_TERM_ENABLE;
+ else
+ info->control.control &= ~EESOX_TERM_ENABLE;
+ restore_flags(flags);
+
+ outb(info->control.control, info->control.io_port);
+}
+
+/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
+ * Purpose : handle interrupts from EESOX SCSI card
+ * Params : irq - interrupt number
+ * dev_id - user-defined (Scsi_Host structure)
+ * regs - processor registers at interrupt
+ */
+static void
+eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
+
+ fas216_intr(host);
+}
+
+static void
+eesoxscsi_invalidate(char *addr, long len, fasdmadir_t direction)
+{
+ unsigned int page;
+
+ if (direction == DMA_OUT) {
+ for(page = (unsigned int) addr; len > 0;
+ page += PAGE_SIZE, len -= PAGE_SIZE)
+ flush_page_to_ram(page);
+ } else
+ flush_cache_range(current->mm, (unsigned long)addr,
+ (unsigned long)addr + len);
+}
+
+/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : type of transfer to be performed
+ */
+static fasdmatype_t
+eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
+ int dmach = host->dma_channel;
+
+ if (dmach != NO_DMA &&
+ (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
+ int buf;
+
+ for(buf = 1; buf <= SCp->buffers_residual &&
+ buf < NR_SG; buf++) {
+ info->dmasg[buf].address = __virt_to_bus(
+ (unsigned long)SCp->buffer[buf].address);
+ info->dmasg[buf].length = SCp->buffer[buf].length;
+
+ eesoxscsi_invalidate(SCp->buffer[buf].address,
+ SCp->buffer[buf].length,
+ direction);
+ }
+
+ info->dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
+ info->dmasg[0].length = SCp->this_residual;
+ eesoxscsi_invalidate(SCp->ptr,
+ SCp->this_residual, direction);
+
+ disable_dma(dmach);
+ set_dma_sg(dmach, info->dmasg, buf);
+ set_dma_mode(dmach,
+ direction == DMA_OUT ? DMA_MODE_WRITE :
+ DMA_MODE_READ);
+ enable_dma(dmach);
+ return fasdma_real_all;
+ }
+ /*
+ * We don't do DMA, we only do slow PIO
+ *
+ * Some day, we will do Pseudo DMA
+ */
+ return fasdma_pseudo;
+}
+
+static void
+eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t dir, int transfer_size)
+{
+ EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
+ unsigned int status;
+ unsigned int length = SCp->this_residual;
+ union {
+ unsigned char *c;
+ unsigned short *s;
+ unsigned long *l;
+ } buffer;
+
+ buffer.c = SCp->ptr;
+
+ status = inb(host->io_port + EESOX_STATUS);
+ if (dir == DMA_IN) {
+ while (length > 8) {
+ if (status & EESOX_STAT_DMA) {
+ unsigned long l1, l2;
+
+ l1 = inw(info->dmaarea);
+ l1 |= inw(info->dmaarea) << 16;
+ l2 = inw(info->dmaarea);
+ l2 |= inw(info->dmaarea) << 16;
+ *buffer.l++ = l1;
+ *buffer.l++ = l2;
+ length -= 8;
+ } else if (status & EESOX_STAT_INTR)
+ goto end;
+ status = inb(host->io_port + EESOX_STATUS);
+ }
+
+ while (length > 1) {
+ if (status & EESOX_STAT_DMA) {
+ *buffer.s++ = inw(info->dmaarea);
+ length -= 2;
+ } else if (status & EESOX_STAT_INTR)
+ goto end;
+ status = inb(host->io_port + EESOX_STATUS);
+ }
+
+ while (length > 0) {
+ if (status & EESOX_STAT_DMA) {
+ *buffer.c++ = inw(info->dmaarea);
+ length -= 1;
+ } else if (status & EESOX_STAT_INTR)
+ goto end;
+ status = inb(host->io_port + EESOX_STATUS);
+ }
+ } else {
+ while (length > 8) {
+ if (status & EESOX_STAT_DMA) {
+ unsigned long l1, l2;
+
+ l1 = *buffer.l++;
+ l2 = *buffer.l++;
+
+ outw(l1, info->dmaarea);
+ outw(l1 >> 16, info->dmaarea);
+ outw(l2, info->dmaarea);
+ outw(l2 >> 16, info->dmaarea);
+ length -= 8;
+ } else if (status & EESOX_STAT_INTR)
+ goto end;
+ status = inb(host->io_port + EESOX_STATUS);
+ }
+
+ while (length > 1) {
+ if (status & EESOX_STAT_DMA) {
+ outw(*buffer.s++, info->dmaarea);
+ length -= 2;
+ } else if (status & EESOX_STAT_INTR)
+ goto end;
+ status = inb(host->io_port + EESOX_STATUS);
+ }
+
+ while (length > 0) {
+ if (status & EESOX_STAT_DMA) {
+ outw(*buffer.c++, info->dmaarea);
+ length -= 1;
+ } else if (status & EESOX_STAT_INTR)
+ goto end;
+ status = inb(host->io_port + EESOX_STATUS);
+ }
+ }
+end:
+}
+
+/* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ */
+static void
+eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+ if (host->dma_channel != NO_DMA)
+ disable_dma(host->dma_channel);
+}
+
+/* Prototype: int eesoxscsi_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises EESOX SCSI driver
+ * Params : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
+ */
+int
+eesoxscsi_detect(Scsi_Host_Template *tpnt)
+{
+ static const card_ids eesoxscsi_cids[] =
+ { EESOXSCSI_LIST, { 0xffff, 0xffff} };
+ int count = 0;
+ struct Scsi_Host *host;
+
+ tpnt->proc_dir = &proc_scsi_eesox;
+ memset(ecs, 0, sizeof (ecs));
+
+ ecard_startfind();
+
+ while(1) {
+ EESOXScsi_Info *info;
+
+ ecs[count] = ecard_find(0, eesoxscsi_cids);
+ if (!ecs[count])
+ break;
+
+ ecard_claim(ecs[count]);
+
+ host = scsi_register(tpnt, sizeof (EESOXScsi_Info));
+ if (!host) {
+ ecard_release(ecs[count]);
+ break;
+ }
+
+ host->io_port = ecard_address(ecs[count], ECARD_IOC, ECARD_FAST);
+ host->irq = ecs[count]->irq;
+ host->dma_channel = ecs[count]->dma;
+ info = (EESOXScsi_Info *)host->hostdata;
+
+ info->control.io_port = host->io_port + EESOX_CONTROL;
+ info->control.control = term[count] ? EESOX_TERM_ENABLE : 0;
+ outb(info->control.control, info->control.io_port);
+
+ ecs[count]->irqaddr = (unsigned char *)
+ ioaddr(host->io_port + EESOX_STATUS);
+ ecs[count]->irqmask = EESOX_STAT_INTR;
+ ecs[count]->irq_data = &info->control;
+ ecs[count]->ops = (expansioncard_ops_t *)&eesoxscsi_ops;
+
+ info->info.scsi.io_port = host->io_port + EESOX_FAS216_OFFSET;
+ info->info.scsi.io_shift = EESOX_FAS216_SHIFT;
+ info->info.scsi.irq = host->irq;
+ info->info.ifcfg.clockrate = EESOX_XTALFREQ;
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = EESOX_ASYNC_PERIOD;
+ info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH;
+ info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 1;
+ info->info.dma.setup = eesoxscsi_dma_setup;
+ info->info.dma.pseudo = eesoxscsi_dma_pseudo;
+ info->info.dma.stop = eesoxscsi_dma_stop;
+ info->dmaarea = host->io_port + EESOX_DMA_OFFSET;
+
+ request_region(host->io_port + EESOX_FAS216_OFFSET,
+ 16 << EESOX_FAS216_SHIFT, "eesox2-fas");
+
+ if (request_irq(host->irq, eesoxscsi_intr,
+ SA_INTERRUPT, "eesox", host)) {
+ printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+ host->host_no, host->irq);
+ host->irq = NO_IRQ;
+ }
+
+ if (request_dma(host->dma_channel, "eesox")) {
+ printk("scsi%d: DMA%d not free, DMA disabled\n",
+ host->host_no, host->dma_channel);
+ host->dma_channel = NO_DMA;
+ }
+
+ fas216_init(host);
+ ++count;
+ }
+ return count;
+}
+
+/* Prototype: int eesoxscsi_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params : host - driver host structure to return info for.
+ */
+int eesoxscsi_release(struct Scsi_Host *host)
+{
+ int i;
+
+ fas216_release(host);
+
+ if (host->irq != NO_IRQ)
+ free_irq(host->irq, host);
+ if (host->dma_channel != NO_DMA)
+ free_dma(host->dma_channel);
+ release_region(host->io_port + EESOX_FAS216_OFFSET, 16 << EESOX_FAS216_SHIFT);
+
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ecs[i] &&
+ host->io_port == ecard_address(ecs[i], ECARD_IOC, ECARD_FAST))
+ ecard_release(ecs[i]);
+ return 0;
+}
+
+/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *eesoxscsi_info(struct Scsi_Host *host)
+{
+ EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
+ static char string[100], *p;
+
+ p = string;
+ p += sprintf(string, "%s at port %lX ",
+ host->hostt->name, host->io_port);
+
+ if (host->irq != NO_IRQ)
+ p += sprintf(p, "irq %d ", host->irq);
+ else
+ p += sprintf(p, "NO IRQ ");
+
+ if (host->dma_channel != NO_DMA)
+ p += sprintf(p, "dma %d ", host->dma_channel);
+ else
+ p += sprintf(p, "NO DMA ");
+
+ p += sprintf(p, "v%d.%d.%d scsi %s", VER_MAJOR, VER_MINOR, VER_PATCH,
+ info->info.scsi.type);
+
+ p += sprintf(p, " terminators %s",
+ info->control.control & EESOX_TERM_ENABLE ? "on" : "off");
+
+ return string;
+}
+
+/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+ * Purpose : Set a driver specific function
+ * Params : host - host to setup
+ * : buffer - buffer containing string describing operation
+ * : length - length of string
+ * Returns : -EINVAL, or 0
+ */
+static int
+eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+{
+ int ret = length;
+
+ if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
+ buffer += 9;
+ length -= 9;
+
+ if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
+ if (buffer[5] == '1')
+ eesoxscsi_terminator_ctl(host, 1);
+ else if (buffer[5] == '0')
+ eesoxscsi_terminator_ctl(host, 0);
+ else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
+ * int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
+ int length, int host_no, int inout)
+{
+ int pos, begin;
+ struct Scsi_Host *host = scsi_hostlist;
+ EESOXScsi_Info *info;
+ Scsi_Device *scd;
+
+ while (host) {
+ if (host->host_no == host_no)
+ break;
+ host = host->next;
+ }
+ if (!host)
+ return 0;
+
+ if (inout == 1)
+ return eesoxscsi_set_proc_info(host, buffer, length);
+
+ info = (EESOXScsi_Info *)host->hostdata;
+
+ begin = 0;
+ pos = sprintf(buffer,
+ "EESOX SCSI driver version %d.%d.%d\n",
+ VER_MAJOR, VER_MINOR, VER_PATCH);
+ pos += sprintf(buffer + pos,
+ "Address: %08lX IRQ : %d DMA : %d\n"
+ "FAS : %-10s TERM: %-3s\n\n"
+ "Statistics:\n",
+ host->io_port, host->irq, host->dma_channel,
+ info->info.scsi.type, info->control.control & EESOX_TERM_ENABLE ? "on" : "off");
+
+ pos += sprintf(buffer+pos,
+ "Queued commands: %-10u Issued commands: %-10u\n"
+ "Done commands : %-10u Reads : %-10u\n"
+ "Writes : %-10u Others : %-10u\n"
+ "Disconnects : %-10u Aborts : %-10u\n"
+ "Resets : %-10u\n",
+ info->info.stats.queues, info->info.stats.removes,
+ info->info.stats.fins, info->info.stats.reads,
+ info->info.stats.writes, info->info.stats.miscs,
+ info->info.stats.disconnects, info->info.stats.aborts,
+ info->info.stats.resets);
+
+ pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+
+ for (scd = host->host_queue; scd; scd = scd->next) {
+ int len;
+
+ proc_print_scsidevice (scd, buffer, &len, pos);
+ pos += len;
+ pos += sprintf (buffer+pos, "Extensions: ");
+ if (scd->tagged_supported)
+ pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
+ scd->tagged_queue ? "en" : "dis",
+ scd->current_tag);
+ pos += sprintf (buffer+pos, "\n");
+
+ if (pos + begin < offset) {
+ begin += pos;
+ pos = 0;
+ }
+ }
+ *start = buffer + (offset - begin);
+ pos -= offset - begin;
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = EESOXSCSI;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff --git a/drivers/acorn/scsi/eesox.h b/drivers/acorn/scsi/eesox.h
new file mode 100644
index 000000000..31c6c883e
--- /dev/null
+++ b/drivers/acorn/scsi/eesox.h
@@ -0,0 +1,81 @@
+/*
+ * EESOX SCSI driver
+ *
+ * Copyright (C) 1997-1998 Russell King
+ */
+#ifndef EESOXSCSI_H
+#define EESOXSCSI_H
+
+extern int eesoxscsi_detect (Scsi_Host_Template *);
+extern int eesoxscsi_release (struct Scsi_Host *);
+extern const char *eesoxscsi_info (struct Scsi_Host *);
+extern int eesoxscsi_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#ifndef CAN_QUEUE
+/*
+ * Default queue size
+ */
+#define CAN_QUEUE 1
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 1
+#endif
+
+#ifndef SCSI_ID
+/*
+ * Default SCSI host ID
+ */
+#define SCSI_ID 7
+#endif
+
+#include <scsi/scsicam.h>
+
+#include "fas216.h"
+
+#define EESOXSCSI { \
+proc_info: eesoxscsi_proc_info, \
+name: "EESOX SCSI", \
+detect: eesoxscsi_detect, /* detect */ \
+release: eesoxscsi_release, /* release */ \
+info: eesoxscsi_info, /* info */ \
+command: fas216_command, /* command */ \
+queuecommand: fas216_queue_command, /* queuecommand */ \
+abort: fas216_abort, /* abort */ \
+reset: fas216_reset, /* reset */ \
+bios_param: scsicam_bios_param, /* biosparam */ \
+can_queue: CAN_QUEUE, /* can queue */ \
+this_id: SCSI_ID, /* scsi host id */ \
+sg_tablesize: SG_ALL, /* sg_tablesize */ \
+cmd_per_lun: CAN_QUEUE, /* cmd per lun */ \
+use_clustering: DISABLE_CLUSTERING \
+ }
+
+#ifndef HOSTS_C
+
+#include <asm/dma.h>
+
+#define NR_SG 256
+
+struct control {
+ unsigned int io_port;
+ unsigned int control;
+};
+
+typedef struct {
+ FAS216_Info info;
+
+ struct control control;
+
+ unsigned int dmaarea; /* Pseudo DMA area */
+ dmasg_t dmasg[NR_SG]; /* Scatter DMA list */
+} EESOXScsi_Info;
+
+#endif /* HOSTS_C */
+
+#endif /* EESOXSCSI_H */
diff --git a/drivers/acorn/scsi/fas216.c b/drivers/acorn/scsi/fas216.c
index eb609393e..b6ffe08d9 100644
--- a/drivers/acorn/scsi/fas216.c
+++ b/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,15 @@
* 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
+ * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
+ *
+ * Todo:
+ * - tighten up the MESSAGE_REJECT support.
+ * - allow individual devices to enable sync xfers.
*/
#include <linux/module.h>
@@ -29,8 +39,9 @@
#include <linux/proc_fs.h>
#include <linux/unistd.h>
#include <linux/stat.h>
+#include <linux/delay.h>
-#include <asm/delay.h>
+#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/ecard.h>
@@ -41,19 +52,134 @@
#include "../../scsi/hosts.h"
#include "fas216.h"
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver");
+
#define VER_MAJOR 0
#define VER_MINOR 0
-#define VER_PATCH 2
+#define VER_PATCH 4
+
+#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
#undef DEBUG_BUSSERVICE
#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: 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 char *phases[] = {
+ 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\n"
+ " 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 const char *phases[] = {
"DATA OUT", "DATA IN",
"COMMAND", "STATUS",
"MISC OUT", "MISC IN",
@@ -63,7 +189,26 @@ static char *fas216_bus_phase (int stat)
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 +216,41 @@ static char fas216_target (FAS216_Info *info)
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;
+}
-/* Function: int fas216_clockrate (unsigned int clock)
+static void print_debug_list(void)
+{
+ int i;
+
+ i = ptr;
+
+ printk(KERN_ERR "SCSI IRQ trail: ");
+ do {
+ printk("%02X:%02X:%02X:%1X ",
+ 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)
* 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
@@ -98,10 +269,13 @@ static int fas216_clockrate (int clock)
* : ns - period in ns (between subsequent bytes)
* Returns : Value suitable for REG_STP
*/
-static int fas216_syncperiod(FAS216_Info *info, int ns)
+static int
+fas216_syncperiod(FAS216_Info *info, int ns)
{
int value = (info->ifcfg.clockrate * ns) / 1000;
+ fas216_checkmagic(info, "fas216_syncperiod");
+
if (value < 4)
value = 4;
else if (value > 35)
@@ -110,16 +284,40 @@ static int fas216_syncperiod(FAS216_Info *info, int ns)
return value & 31;
}
-/* Function: void fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+/* Function: void fas216_set_sync(FAS216_Info *info, int target)
+ * Purpose : Correctly setup FAS216 chip for specified transfer period.
+ * Params : info - state structure for interface
+ * : target - target
+ * Notes : we need to switch the chip out of FASTSCSI mode if we have
+ * a transfer period >= 200ns - otherwise the chip will violate
+ * the SCSI timings.
+ */
+static void
+fas216_set_sync(FAS216_Info *info, int target)
+{
+ outb(info->device[target].sof, REG_SOF(info));
+ outb(info->device[target].stp, REG_STP(info));
+ if (info->device[target].period >= (200 / 4))
+ outb(info->scsi.cfg[2] & ~CNTL3_FASTSCSI, REG_CNTL3(info));
+ else
+ outb(info->scsi.cfg[2], REG_CNTL3(info));
+}
+
+/* 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 +342,61 @@ fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
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 +406,81 @@ fas216_pio (FAS216_Info *info, fasdmadir_t direction)
* 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 +488,68 @@ fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction)
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;
}
+ if (info->scsi.phase == PHASE_DATAOUT)
+ outb(CMD_FLUSHFIFO, REG_CMD(info));
}
-/* 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 +561,36 @@ fas216_disconnect_intr (FAS216_Info *info)
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 +604,733 @@ fas216_reselected_intr (FAS216_Info *info)
#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);
+ 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;
- }
+ 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;
+ }
- target = inb(REG_FF(info));
- identify_msg = inb(REG_FF(info));
+ target = inb(REG_FF(info));
+ identify_msg = inb(REG_FF(info));
- 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;
- }
+ 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 (!(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;
- }
+ 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;
+ }
- 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;
- }
-
- 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;
+ }
- 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));
+ 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 && 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;
- message[0] = inb(REG_FF(info));
+ fas216_checkmagic(info, "fas216_message");
- if (message[0] == EXTENDED_MESSAGE) {
- message[1] = inb(REG_FF(info));
+ message[0] = inb(REG_FF(info));
- for (msglen = 2; msglen < message[1]; msglen++)
- message[msglen] = 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));
+
+ message[1] = 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;
-
- 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");
- }
+ {
+ 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;
- }
-
- 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;
+ 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;
+
+ 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;
-
- 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 */
-
- case EXTENDED_WDTR: /* Wide transfer negociation request/reply */
- /* We don't do wide transfers - reject message */
+ 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;
+
+ 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].period = message[3];
+ 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);
+ fas216_set_sync(info, info->SCpnt->target);
+ 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;
+
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;
-
- 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));
+ outb(CMD_MSGACCEPTED, 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: 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_busservice_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+static void fas216_send_command(FAS216_Info *info)
{
- 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));
+ int i;
- 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;
+ fas216_checkmagic(info, "fas216_send_command");
- case STAT_DATAIN: /* data in phase */
- fas216_starttransfer (info, DMA_IN);
- break;
+ 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: 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 int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
+{
+ fas216_checkmagic(info, "fas216_busservice_selection");
- case STAT_STATUS: /* status phase */
+ 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, 0);
+ return 1;
+
+ 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:
- 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:
+ 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");
+
+ 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;
-
- 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;
+ return 1;
- default:
- printk ("scsi%d.%c: bus phase %s after reconnect?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
+ case STAT_DATAOUT: /* data out phase */
+ fas216_finish_reconnect(info);
+ fas216_starttransfer(info, DMA_OUT, 1);
+ return 1;
- case PHASE_MSGIN:
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN:
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
+ case STAT_DATAIN: /* data in phase */
+ fas216_finish_reconnect(info);
+ fas216_starttransfer(info, DMA_IN, 0);
+ return 1;
- default:
- printk ("scsi%d.%c: bus phase %s after message in?\n",
- info->host->host_no, fas216_target (info),
- fas216_bus_phase (stat));
- }
- break;
+ default:
+ return 0;
+ }
+}
- 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;
-
- 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;
- }
+ 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;
- 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);
- }
+ 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 %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;
-
- 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);
+ 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));
+
+ 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 +1347,11 @@ static void fas216_kick (FAS216_Info *info)
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 +1408,7 @@ static void fas216_kick (FAS216_Info *info)
}
/* 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 +1419,8 @@ static void fas216_kick (FAS216_Info *info)
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,
@@ -1037,12 +1442,14 @@ static void fas216_kick (FAS216_Info *info)
outb(info->ifcfg.select_timeout, REG_STIM(info));
/* synchronous transfers */
- outb(info->device[SCpnt->target].sof, REG_SOF(info));
- outb(info->device[SCpnt->target].stp, REG_STP(info));
+ fas216_set_sync(info, SCpnt->target);
- 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 +1467,22 @@ static void fas216_kick (FAS216_Info *info)
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 +1493,95 @@ static void fas216_kick (FAS216_Info *info)
/* 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;
- SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
+ if (SCpnt) {
+ info->scsi.phase = PHASE_IDLE;
+ info->SCpnt = NULL;
+
+ 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 +1624,51 @@ int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
/* 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 +1676,13 @@ int fas216_command (Scsi_Cmnd *SCpnt)
* 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 +1705,7 @@ static void fas216_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2,
*SCpntp1 = NULL;
SCpnt->result = result;
- SCpnt->scsi_done (SCpnt);
+ SCpnt->scsi_done(SCpnt);
}
if (SCpnt == *SCpntp2)
@@ -1309,28 +1752,33 @@ int fas216_eh_host_reset(Scsi_Cmnd *SCpnt)
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_dumpstate(info);
+ fas216_dumpinfo(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 +1786,14 @@ int fas216_abort (Scsi_Cmnd *SCpnt)
/* 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 +1801,14 @@ int fas216_abort (Scsi_Cmnd *SCpnt)
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 +1819,15 @@ int fas216_abort (Scsi_Cmnd *SCpnt)
*/
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,20 +1836,23 @@ static void fas216_reset_state(FAS216_Info *info)
info->scsi.last_message = 0;
info->scsi.aborting = 0;
info->scsi.phase = PHASE_IDLE;
+ info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
- for (i = 0; i < 8; i++) {
-#ifndef NO_DISCONNECTS
- info->device[i].disconnect_ok = 1;
+ if (info->host->dma_channel == NO_DMA || !info->dma.setup)
+ negstate = syncneg_invalid;
+ else
+#ifdef SCSI2_SYNC
+ negstate = syncneg_start;
#else
- info->device[i].disconnect_ok = 0;
+ negstate = syncneg_invalid;
#endif
- info->device[i].stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+ for (i = 0; i < 8; i++) {
+ info->device[i].disconnect_ok = info->ifcfg.disconnect_ok;
+ info->device[i].negstate = negstate;
+ info->device[i].period = info->ifcfg.asyncperiod / 4;
+ 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 +1862,19 @@ static void fas216_reset_state(FAS216_Info *info)
*/
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 +1882,72 @@ static void fas216_init_chip(FAS216_Info *info)
* 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,45 +1960,50 @@ int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
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;
}
+ outb(CMD_RESETCHIP, REG_CMD(info));
+
outb(0, REG_CNTL3(info));
outb(CNTL2_S2FE, REG_CNTL2(info));
@@ -1555,8 +2037,7 @@ int fas216_init (struct Scsi_Host *instance)
break;
}
- udelay (300);
-
+ udelay(300);
/* now for the real initialisation */
fas216_init_chip(info);
@@ -1565,32 +2046,39 @@ int fas216_init (struct Scsi_Host *instance)
/* 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 +2097,12 @@ EXPORT_SYMBOL(fas216_eh_host_reset);
#ifdef MODULE
-int init_module (void)
+int init_module(void)
{
return 0;
}
-void cleanup_module (void)
+void cleanup_module(void)
{
}
#endif
diff --git a/drivers/acorn/scsi/fas216.h b/drivers/acorn/scsi/fas216.h
index f88eb83d7..6518fbb42 100644
--- a/drivers/acorn/scsi/fas216.h
+++ b/drivers/acorn/scsi/fas216.h
@@ -55,8 +55,8 @@
/* status register (read) */
#define REG_STAT(x) ((x)->scsi.io_port + (4 << (x)->scsi.io_shift))
-#define STAT_IO (1 << 0) /* IO phase */
-#define STAT_CD (1 << 1) /* CD phase */
+#define STAT_IO (1 << 0) /* IO phase */
+#define STAT_CD (1 << 1) /* CD phase */
#define STAT_MSG (1 << 2) /* MSG phase */
#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */
#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */
@@ -98,6 +98,7 @@
#define IS_NOTCOMMAND 0x02 /* Not in command state */
#define IS_EARLYPHASE 0x03 /* Early phase change */
#define IS_COMPLETE 0x04 /* Command ok */
+#define IS_SOF 0x08 /* Sync off flag */
/* Transfer period step (write) */
#define REG_STP(x) ((x)->scsi.io_port + (6 << (x)->scsi.io_shift))
@@ -170,6 +171,7 @@
typedef enum {
PHASE_IDLE, /* we're not planning on doing anything */
PHASE_SELECTION, /* selecting a device */
+ PHASE_MESSAGESENT, /* selected, and we're sending cmd */
PHASE_RECONNECTED, /* reconnected */
PHASE_DATAOUT, /* data out to device */
PHASE_DATAIN, /* data in from device */
@@ -188,6 +190,7 @@ typedef enum {
typedef enum {
fasdma_none, /* No dma */
+ fasdma_pio, /* PIO mode */
fasdma_pseudo, /* Pseudo DMA */
fasdma_real_block, /* Real DMA, on block by block basis */
fasdma_real_all /* Real DMA, on request by request */
@@ -196,10 +199,14 @@ typedef enum {
typedef enum {
syncneg_start, /* Negociate with device for Sync xfers */
syncneg_sent, /* Sync Xfer negociation sent */
- syncneg_complete /* Sync Xfer complete */
+ syncneg_complete, /* Sync Xfer complete */
+ syncneg_invalid /* Sync Xfer not supported */
} syncneg_t;
+#define MAGIC 0x441296bdUL
+
typedef struct {
+ unsigned long magic_start;
struct Scsi_Host *host; /* host */
Scsi_Cmnd *SCpnt; /* currently processing command */
Scsi_Cmnd *origSCpnt; /* original connecting command */
@@ -208,7 +215,7 @@ typedef struct {
struct {
unsigned int io_port; /* base address of FAS216 */
unsigned int io_shift; /* shift to adjust reg offsets by */
- unsigned char irq; /* interrupt */
+ unsigned int irq; /* interrupt */
unsigned char cfg[4]; /* configuration registers */
const char *type; /* chip type */
phase_t phase; /* current phase */
@@ -223,6 +230,7 @@ typedef struct {
MsgQueue_t msgs; /* message queue for connected device */
+ unsigned int async_stp; /* Async transfer STP value */
unsigned short last_message; /* last message to be sent */
unsigned char disconnectable:1; /* this command can be disconnected */
@@ -246,8 +254,10 @@ typedef struct {
struct {
unsigned char clockrate; /* clock rate of FAS device (MHz) */
unsigned char select_timeout; /* timeout (R5) */
- unsigned int asyncperiod; /* Async transfer period (ns) */
unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */
+ unsigned char cntl3; /* Control Reg 3 */
+ unsigned int asyncperiod; /* Async transfer period (ns) */
+ unsigned int disconnect_ok:1; /* Disconnects allowed? */
} ifcfg;
/* queue handling */
@@ -259,6 +269,7 @@ typedef struct {
/* per-device info */
struct {
unsigned char disconnect_ok:1; /* device can disconnect */
+ unsigned int period; /* sync xfer period (*4ns) */
unsigned char stp; /* synchronous transfer period */
unsigned char sof; /* synchronous offset register */
syncneg_t negstate; /* synchronous transfer mode */
@@ -268,13 +279,15 @@ typedef struct {
/* dma */
struct {
fasdmatype_t transfer_type; /* current type of DMA transfer */
- fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction);
- int (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer);
+ fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma);
+ void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer);
void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp);
} dma;
/* miscellaneous */
int internal_done; /* flag to indicate request done */
+
+ unsigned long magic_end;
} FAS216_Info;
/* Function: int fas216_init (struct Scsi_Host *instance)
diff --git a/drivers/acorn/scsi/msgqueue.c b/drivers/acorn/scsi/msgqueue.c
index a9b560043..7621b4d2b 100644
--- a/drivers/acorn/scsi/msgqueue.c
+++ b/drivers/acorn/scsi/msgqueue.c
@@ -1,7 +1,7 @@
/*
* drivers/acorn/scsi/msgqueue.c: message queue handling
*
- * Copyright (C) 1997,8 Russell King
+ * Copyright (C) 1997-1998 Russell King
*/
#include <linux/module.h>
@@ -10,13 +10,16 @@
#include "msgqueue.h"
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SCSI message queue handling");
+
/*
- * Function: struct msgqueue_entry *mqe_alloc (MsgQueue_t *msgq)
+ * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq)
* Purpose : Allocate a message queue entry
* Params : msgq - message queue to claim entry for
* Returns : message queue entry or NULL.
*/
-static struct msgqueue_entry *mqe_alloc (MsgQueue_t *msgq)
+static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq)
{
struct msgqueue_entry *mq;
@@ -27,12 +30,12 @@ static struct msgqueue_entry *mqe_alloc (MsgQueue_t *msgq)
}
/*
- * Function: void mqe_free (MsgQueue_t *msgq, struct msgqueue_entry *mq)
+ * Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq)
* Purpose : free a message queue entry
* Params : msgq - message queue to free entry from
* mq - message queue entry to free
*/
-static void mqe_free (MsgQueue_t *msgq, struct msgqueue_entry *mq)
+static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq)
{
if (mq) {
mq->next = msgq->free;
@@ -41,11 +44,11 @@ static void mqe_free (MsgQueue_t *msgq, struct msgqueue_entry *mq)
}
/*
- * Function: void msgqueue_initialise (MsgQueue_t *msgq)
+ * Function: void msgqueue_initialise(MsgQueue_t *msgq)
* Purpose : initialise a message queue
* Params : msgq - queue to initialise
*/
-void msgqueue_initialise (MsgQueue_t *msgq)
+void msgqueue_initialise(MsgQueue_t *msgq)
{
int i;
@@ -60,21 +63,21 @@ void msgqueue_initialise (MsgQueue_t *msgq)
/*
- * Function: void msgqueue_free (MsgQueue_t *msgq)
+ * Function: void msgqueue_free(MsgQueue_t *msgq)
* Purpose : free a queue
* Params : msgq - queue to free
*/
-void msgqueue_free (MsgQueue_t *msgq)
+void msgqueue_free(MsgQueue_t *msgq)
{
}
/*
- * Function: int msgqueue_msglength (MsgQueue_t *msgq)
+ * Function: int msgqueue_msglength(MsgQueue_t *msgq)
* Purpose : calculate the total length of all messages on the message queue
* Params : msgq - queue to examine
* Returns : number of bytes of messages in queue
*/
-int msgqueue_msglength (MsgQueue_t *msgq)
+int msgqueue_msglength(MsgQueue_t *msgq)
{
struct msgqueue_entry *mq = msgq->qe;
int length = 0;
@@ -86,19 +89,19 @@ int msgqueue_msglength (MsgQueue_t *msgq)
}
/*
- * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+ * Function: char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
* Purpose : return a message & its length
* Params : msgq - queue to obtain message from
* length - pointer to int for message length
- * Returns : pointer to message string
+ * Returns : pointer to message string, or NULL
*/
-char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
{
struct msgqueue_entry *mq;
if ((mq = msgq->qe) != NULL) {
msgq->qe = mq->next;
- mqe_free (msgq, mq);
+ mqe_free(msgq, mq);
*length = mq->length;
}
@@ -106,26 +109,42 @@ char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
}
/*
- * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+ * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
+ * Purpose : return next message & length without removing it from the list
+ * Params : msgq - queue to obtain message from
+ * : length - pointer to int for message length
+ * Returns : pointer to message string, or NULL
+ */
+char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
+{
+ struct msgqueue_entry *mq = msgq->qe;
+
+ *length = mq ? mq->length : 0;
+
+ return mq ? mq->msg : NULL;
+}
+
+/*
+ * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
* Purpose : add a message onto a message queue
* Params : msgq - queue to add message on
* length - length of message
* ... - message bytes
* Returns : != 0 if successful
*/
-int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
{
- struct msgqueue_entry *mq = mqe_alloc (msgq);
+ struct msgqueue_entry *mq = mqe_alloc(msgq);
va_list ap;
if (mq) {
struct msgqueue_entry **mqp;
int i;
- va_start (ap, length);
+ va_start(ap, length);
for (i = 0; i < length; i++)
- mq->msg[i] = va_arg (ap, unsigned char);
- va_end (ap);
+ mq->msg[i] = va_arg(ap, unsigned char);
+ va_end(ap);
mq->length = length;
mq->next = NULL;
@@ -141,17 +160,17 @@ int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
}
/*
- * Function: void msgqueue_flush (MsgQueue_t *msgq)
+ * Function: void msgqueue_flush(MsgQueue_t *msgq)
* Purpose : flush all messages from message queue
* Params : msgq - queue to flush
*/
-void msgqueue_flush (MsgQueue_t *msgq)
+void msgqueue_flush(MsgQueue_t *msgq)
{
struct msgqueue_entry *mq, *mqnext;
for (mq = msgq->qe; mq; mq = mqnext) {
mqnext = mq->next;
- mqe_free (msgq, mq);
+ mqe_free(msgq, mq);
}
msgq->qe = NULL;
}
@@ -160,16 +179,17 @@ EXPORT_SYMBOL(msgqueue_initialise);
EXPORT_SYMBOL(msgqueue_free);
EXPORT_SYMBOL(msgqueue_msglength);
EXPORT_SYMBOL(msgqueue_getnextmsg);
+EXPORT_SYMBOL(msgqueue_peeknextmsg);
EXPORT_SYMBOL(msgqueue_addmsg);
EXPORT_SYMBOL(msgqueue_flush);
#ifdef MODULE
-int init_module (void)
+int init_module(void)
{
return 0;
}
-void cleanup_module (void)
+void cleanup_module(void)
{
}
#endif
diff --git a/drivers/acorn/scsi/msgqueue.h b/drivers/acorn/scsi/msgqueue.h
index ffb381adb..cca30255c 100644
--- a/drivers/acorn/scsi/msgqueue.h
+++ b/drivers/acorn/scsi/msgqueue.h
@@ -47,11 +47,20 @@ extern int msgqueue_msglength (MsgQueue_t *msgq);
* Purpose : return a message & its length
* Params : msgq - queue to obtain message from
* length - pointer to int for message length
- * Returns : pointer to message string
+ * Returns : pointer to message string, or NULL
*/
extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length);
/*
+ * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
+ * Purpose : return next message & length without removing it from the list
+ * Params : msgq - queue to obtain message from
+ * : length - pointer to int for message length
+ * Returns : pointer to message string, or NULL
+ */
+extern char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length);
+
+/*
* Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
* Purpose : add a message onto a message queue
* Params : msgq - queue to add message on
diff --git a/drivers/acorn/scsi/oak.c b/drivers/acorn/scsi/oak.c
index 54763f9ec..44fc86eaf 100644
--- a/drivers/acorn/scsi/oak.c
+++ b/drivers/acorn/scsi/oak.c
@@ -33,6 +33,11 @@
/*
* $Log: oak.c,v $
+ * Revision 1.3 1998/05/03 20:45:37 alan
+ * ARM SCSI update. This adds the eesox driver and massively updates the
+ * Cumana driver. The folks who bought cumana arent anal retentive all
+ * docs are secret weenies so now there are docs ..
+ *
* Revision 1.2 1998/03/08 05:49:48 davem
* Merge to 2.1.89
*
@@ -111,7 +116,7 @@ int oakscsi_detect(Scsi_Host_Template * tpnt)
request_region (instance->io_port, instance->n_io_port, "Oak SCSI");
if (instance->irq != IRQ_NONE)
- if (request_irq(instance->irq, oakscsi_intr, SA_INTERRUPT, "Oak SCSI", NULL)) {
+ if (request_irq(instance->irq, do_oakscsi_intr, SA_INTERRUPT, "Oak SCSI", NULL)) {
printk("scsi%d: IRQ%d not free, interrupts disabled\n",
instance->host_no, instance->irq);
instance->irq = IRQ_NONE;
@@ -122,7 +127,7 @@ int oakscsi_detect(Scsi_Host_Template * tpnt)
printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
}
- printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+ printk("scsi%d: at port %lX irq", instance->host_no, instance->io_port);
if (instance->irq == IRQ_NONE)
printk ("s disabled");
else
diff --git a/drivers/acorn/scsi/oak.h b/drivers/acorn/scsi/oak.h
index eeea25a72..f4ead3612 100644
--- a/drivers/acorn/scsi/oak.h
+++ b/drivers/acorn/scsi/oak.h
@@ -85,7 +85,7 @@ use_clustering: DISABLE_CLUSTERING \
#define NCR5380_read(reg) oakscsi_read(_instance, reg)
#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value)
-#define NCR5380_intr oakscsi_intr
+#define do_NCR5380_intr do_oakscsi_intr
#define NCR5380_queue_command oakscsi_queue_command
#define NCR5380_abort oakscsi_abort
#define NCR5380_reset oakscsi_reset
diff --git a/drivers/acorn/scsi/powertec.c b/drivers/acorn/scsi/powertec.c
index 302357f03..7a41d7d0c 100644
--- a/drivers/acorn/scsi/powertec.c
+++ b/drivers/acorn/scsi/powertec.c
@@ -1,15 +1,18 @@
/*
* linux/arch/arm/drivers/scsi/powertec.c
*
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1998 Russell King
*
* This driver is based on experimentation. Hence, it may have made
* assumptions about the particular card that I have available, and
* may not be reliable!
*
* Changelog:
- * 01-10-1997 RMK Created, READONLY version
- * 15-02-1998 RMK Added DMA support and hardware definitions
+ * 01-10-1997 RMK Created, READONLY version.
+ * 15-02-1998 RMK Added DMA support and hardware definitions.
+ * 15-04-1998 RMK Only do PIO if FAS216 will allow it.
+ * 02-05-1998 RMK Moved DMA sg list into per-interface structure.
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
*/
#include <linux/module.h>
@@ -21,12 +24,12 @@
#include <linux/proc_fs.h>
#include <linux/unistd.h>
#include <linux/stat.h>
+#include <linux/delay.h>
-#include <asm/delay.h>
-#include <asm/io.h>
-#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/irq.h>
#include <asm/pgtable.h>
#include "../../scsi/sd.h"
@@ -36,7 +39,7 @@
/* Configuration */
#define POWERTEC_XTALFREQ 40
#define POWERTEC_ASYNC_PERIOD 200
-#define POWERTEC_SYNC_DEPTH 16
+#define POWERTEC_SYNC_DEPTH 7
/*
* List of devices that the driver will recognise
@@ -45,8 +48,16 @@
#define POWERTEC_FAS216_OFFSET 0xc00
#define POWERTEC_FAS216_SHIFT 4
+
#define POWERTEC_INTR_STATUS 0x800
#define POWERTEC_INTR_BIT 0x80
+
+#define POWERTEC_RESET_CONTROL 0x406
+#define POWERTEC_RESET_BIT 1
+
+#define POWERTEC_TERM_CONTROL 0x806
+#define POWERTEC_TERM_ENABLE 1
+
#define POWERTEC_INTR_CONTROL 0x407
#define POWERTEC_INTR_ENABLE 1
#define POWERTEC_INTR_DISABLE 0
@@ -56,19 +67,29 @@
*/
#define VER_MAJOR 0
#define VER_MINOR 0
-#define VER_PATCH 1
+#define VER_PATCH 2
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Powertec SCSI driver");
+MODULE_PARM(term, "1-8i");
+MODULE_PARM_DESC(term, "SCSI bus termination");
static struct expansion_card *ecs[MAX_ECARDS];
+/*
+ * Use term=0,1,0,0,0 to turn terminators on/off
+ */
+int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
static struct proc_dir_entry proc_scsi_powertec = {
PROC_SCSI_QLOGICISP, 8, "powertec",
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-/* Function: void powertecscsi_irqenable(ec, irqnr)
- * Purpose : Enable interrupts on powertec SCSI card
- * Params : ec - expansion card structure
- * : irqnr - interrupt number
+/* Prototype: void powertecscsi_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on Powertec SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
*/
static void
powertecscsi_irqenable(struct expansion_card *ec, int irqnr)
@@ -77,10 +98,10 @@ powertecscsi_irqenable(struct expansion_card *ec, int irqnr)
outb(POWERTEC_INTR_ENABLE, port);
}
-/* Function: void powertecscsi_irqdisable(ec, irqnr)
- * Purpose : Disable interrupts on powertec SCSI card
- * Params : ec - expansion card structure
- * : irqnr - interrupt number
+/* Prototype: void powertecscsi_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on Powertec SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
*/
static void
powertecscsi_irqdisable(struct expansion_card *ec, int irqnr)
@@ -96,19 +117,36 @@ static const expansioncard_ops_t powertecscsi_ops = {
NULL
};
-/* Function: void powertecscsi_intr(int irq, void *dev_id,
- * struct pt_regs *regs)
- * Purpose : handle interrupts from Powertec SCSI card
- * Params : irq - interrupt number
- * dev_id - user-defined (Scsi_Host structure)
- * regs - processor registers at interrupt
+/* Prototype: void powertecscsi_terminator_ctl(host, on_off)
+ * Purpose : Turn the Powertec SCSI terminators on or off
+ * Params : host - card to turn on/off
+ * : on_off - !0 to turn on, 0 to turn off
+ */
+static void
+powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
+{
+ PowerTecScsi_Info *info = (PowerTecScsi_Info *)host->hostdata;
+
+ if (on_off)
+ info->control.terms = POWERTEC_TERM_ENABLE;
+ else
+ info->control.terms = 0;
+
+ outb(info->control.terms, info->control.term_port);
+}
+
+/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs)
+ * Purpose : handle interrupts from Powertec SCSI card
+ * Params : irq - interrupt number
+ * dev_id - user-defined (Scsi_Host structure)
+ * regs - processor registers at interrupt
*/
static void
powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
{
- struct Scsi_Host *instance = (struct Scsi_Host *)dev_id;
+ struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
- fas216_intr(instance);
+ fas216_intr(host);
}
static void
@@ -125,66 +163,73 @@ powertecscsi_invalidate(char *addr, long len, fasdmadir_t direction)
(unsigned long)addr + len);
}
-/* Function: fasdmatype_t powertecscsi_dma_setup(instance, SCpnt, direction)
- * Purpose : initialises DMA/PIO
- * Params : instance - host
- * SCpnt - command
- * direction - DMA on to/off of card
- * Returns : type of transfer to be performed
+/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : type of transfer to be performed
*/
static fasdmatype_t
-powertecscsi_dma_setup(struct Scsi_Host *instance, Scsi_Pointer *SCp,
- fasdmadir_t direction)
+powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
{
- if (instance->dma_channel != NO_DMA && SCp->this_residual >= 512) {
+ PowerTecScsi_Info *info = (PowerTecScsi_Info *)host->hostdata;
+ int dmach = host->dma_channel;
+
+ if (dmach != NO_DMA &&
+ (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
int buf;
-static dmasg_t dmasg[256];
- for (buf = 1; buf <= SCp->buffers_residual; buf++) {
- dmasg[buf].address = __virt_to_bus(
+ for (buf = 1; buf <= SCp->buffers_residual &&
+ buf < NR_SG; buf++) {
+ info->dmasg[buf].address = __virt_to_bus(
(unsigned long)SCp->buffer[buf].address);
- dmasg[buf].length = SCp->buffer[buf].length;
+ info->dmasg[buf].length = SCp->buffer[buf].length;
powertecscsi_invalidate(SCp->buffer[buf].address,
SCp->buffer[buf].length,
direction);
}
- dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
- dmasg[0].length = SCp->this_residual;
+ info->dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
+ info->dmasg[0].length = SCp->this_residual;
powertecscsi_invalidate(SCp->ptr,
SCp->this_residual, direction);
- disable_dma(instance->dma_channel);
- set_dma_sg(instance->dma_channel, dmasg, buf);
- set_dma_mode(instance->dma_channel,
+ disable_dma(dmach);
+ set_dma_sg(dmach, info->dmasg, buf);
+ set_dma_mode(dmach,
direction == DMA_OUT ? DMA_MODE_WRITE :
DMA_MODE_READ);
- enable_dma(instance->dma_channel);
+ enable_dma(dmach);
return fasdma_real_all;
}
+
/*
- * We don't do DMA, we only do slow PIO
+ * If we're not doing DMA,
+ * we'll do slow PIO
*/
- return fasdma_none;
+ return fasdma_pio;
}
-/* Function: int powertecscsi_dma_stop(instance, SCpnt)
- * Purpose : stops DMA/PIO
- * Params : instance - host
- * SCpnt - command
+/* Prototype: int powertecscsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
*/
static void
-powertecscsi_dma_stop(struct Scsi_Host *instance, Scsi_Pointer *SCp)
+powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
{
- if (instance->dma_channel != NO_DMA)
- disable_dma(instance->dma_channel);
+ if (host->dma_channel != NO_DMA)
+ disable_dma(host->dma_channel);
}
-/* Function: int powertecscsi_detect(Scsi_Host_Template * tpnt)
- * Purpose : initialises PowerTec SCSI driver
- * Params : tpnt - template for this SCSI adapter
- * Returns : >0 if host found, 0 otherwise.
+/* Prototype: int powertecscsi_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises PowerTec SCSI driver
+ * Params : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
*/
int
powertecscsi_detect(Scsi_Host_Template *tpnt)
@@ -192,14 +237,14 @@ powertecscsi_detect(Scsi_Host_Template *tpnt)
static const card_ids powertecscsi_cids[] =
{ POWERTECSCSI_LIST, { 0xffff, 0xffff} };
int count = 0;
- struct Scsi_Host *instance;
+ struct Scsi_Host *host;
tpnt->proc_dir = &proc_scsi_powertec;
memset(ecs, 0, sizeof (ecs));
ecard_startfind();
- while(1) {
+ while (1) {
PowerTecScsi_Info *info;
ecs[count] = ecard_find(0, powertecscsi_cids);
@@ -208,88 +253,95 @@ powertecscsi_detect(Scsi_Host_Template *tpnt)
ecard_claim(ecs[count]);
- instance = scsi_register(tpnt, sizeof (PowerTecScsi_Info));
- if (!instance) {
+ host = scsi_register(tpnt, sizeof (PowerTecScsi_Info));
+ if (!host) {
ecard_release(ecs[count]);
break;
}
- instance->io_port = ecard_address(ecs[count], ECARD_IOC, 0);
- instance->irq = ecs[count]->irq;
+ host->io_port = ecard_address(ecs[count], ECARD_IOC, ECARD_FAST);
+ host->irq = ecs[count]->irq;
+ host->dma_channel = ecs[count]->dma;
+ info = (PowerTecScsi_Info *)host->hostdata;
- ecs[count]->irqaddr = (unsigned char *)
- ioaddr(instance->io_port + POWERTEC_INTR_STATUS);
- ecs[count]->irqmask = POWERTEC_INTR_BIT;
- ecs[count]->irq_data = (void *)
- (instance->io_port + POWERTEC_INTR_CONTROL);
- ecs[count]->ops = (expansioncard_ops_t *)&powertecscsi_ops;
+ info->control.term_port = host->io_port + POWERTEC_TERM_CONTROL;
+ info->control.terms = term[count] ? POWERTEC_TERM_ENABLE : 0;
+ powertecscsi_terminator_ctl(host, info->control.terms);
- request_region(instance->io_port + POWERTEC_FAS216_OFFSET,
- 16 << POWERTEC_FAS216_SHIFT, "powertec2-fas");
-
- if (request_irq(instance->irq, powertecscsi_intr,
- SA_INTERRUPT, "powertec", instance)) {
+ info->info.scsi.io_port =
+ host->io_port + POWERTEC_FAS216_OFFSET;
+ info->info.scsi.io_shift= POWERTEC_FAS216_SHIFT;
+ info->info.scsi.irq = host->irq;
+ info->info.ifcfg.clockrate = POWERTEC_XTALFREQ;
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD;
+ info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH;
+ info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 1;
+ info->info.dma.setup = powertecscsi_dma_setup;
+ info->info.dma.pseudo = NULL;
+ info->info.dma.stop = powertecscsi_dma_stop;
+
+ ecs[count]->irqaddr = (unsigned char *)
+ ioaddr(host->io_port + POWERTEC_INTR_STATUS);
+ ecs[count]->irqmask = POWERTEC_INTR_BIT;
+ ecs[count]->irq_data = (void *)
+ (host->io_port + POWERTEC_INTR_CONTROL);
+ ecs[count]->ops = (expansioncard_ops_t *)&powertecscsi_ops;
+
+ request_region(host->io_port + POWERTEC_FAS216_OFFSET,
+ 16 << POWERTEC_FAS216_SHIFT, "powertec2-fas");
+
+ if (host->irq != NO_IRQ &&
+ request_irq(host->irq, powertecscsi_intr,
+ SA_INTERRUPT, "powertec", host)) {
printk("scsi%d: IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- instance->irq = NO_IRQ;
+ host->host_no, host->irq);
+ host->irq = NO_IRQ;
+ info->info.scsi.irq = NO_IRQ;
}
- info = (PowerTecScsi_Info *)instance->hostdata;
-
- instance->dma_channel = 3; /* slot 1 */
- if (request_dma(instance->dma_channel, "powertec")) {
+ if (host->dma_channel != NO_DMA &&
+ request_dma(host->dma_channel, "powertec")) {
printk("scsi%d: DMA%d not free, DMA disabled\n",
- instance->host_no, instance->dma_channel);
- instance->dma_channel = NO_DMA;
+ host->host_no, host->dma_channel);
+ host->dma_channel = NO_DMA;
}
- info->info.scsi.io_port =
- instance->io_port + POWERTEC_FAS216_OFFSET;
- info->info.scsi.io_shift= POWERTEC_FAS216_SHIFT;
- info->info.scsi.irq = instance->irq;
- info->info.ifcfg.clockrate = POWERTEC_XTALFREQ;
- info->info.ifcfg.select_timeout = 255;
- info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD;
- info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH;
- info->info.dma.setup = powertecscsi_dma_setup;
- info->info.dma.pseudo = NULL;
- info->info.dma.stop = powertecscsi_dma_stop;
-
- fas216_init(instance);
+ fas216_init(host);
++count;
}
return count;
}
-/* Function: int powertecscsi_release(struct Scsi_Host * host)
- * Purpose : releases all resources used by this adapter
- * Params : host - driver host structure to return info for.
- * Returns : nothing
+/* Prototype: int powertecscsi_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params : host - driver host structure to return info for.
*/
-int powertecscsi_release(struct Scsi_Host *instance)
+int powertecscsi_release(struct Scsi_Host *host)
{
int i;
- fas216_release(instance);
+ fas216_release(host);
- if (instance->irq != NO_IRQ)
- free_irq(instance->irq, instance);
- if (instance->dma_channel != NO_DMA)
- free_dma(instance->dma_channel);
- release_region(instance->io_port + POWERTEC_FAS216_OFFSET,
+ if (host->irq != NO_IRQ)
+ free_irq(host->irq, host);
+ if (host->dma_channel != NO_DMA)
+ free_dma(host->dma_channel);
+ release_region(host->io_port + POWERTEC_FAS216_OFFSET,
16 << POWERTEC_FAS216_SHIFT);
for (i = 0; i < MAX_ECARDS; i++)
if (ecs[i] &&
- instance->io_port == ecard_address(ecs[i], ECARD_IOC, 0))
+ host->io_port == ecard_address(ecs[i], ECARD_IOC, ECARD_FAST))
ecard_release(ecs[i]);
return 0;
}
-/* Function: const char *powertecscsi_info(struct Scsi_Host * host)
- * Purpose : returns a descriptive string about this interface,
- * Params : host - driver host structure to return info for.
- * Returns : pointer to a static buffer containing null terminated string.
+/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
*/
const char *powertecscsi_info(struct Scsi_Host *host)
{
@@ -297,7 +349,7 @@ const char *powertecscsi_info(struct Scsi_Host *host)
static char string[100], *p;
p = string;
- p += sprintf(string, "%s at port %X ",
+ p += sprintf(string, "%s at port %lX ",
host->hostt->name, host->io_port);
if (host->irq != NO_IRQ)
@@ -314,21 +366,55 @@ const char *powertecscsi_info(struct Scsi_Host *host)
VER_MAJOR, VER_MINOR, VER_PATCH,
info->info.scsi.type);
+ p += sprintf(p, " terminators %s",
+ info->control.terms ? "on" : "off");
+
return string;
}
-/* Function: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
+/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+ * Purpose : Set a driver specific function
+ * Params : host - host to setup
+ * : buffer - buffer containing string describing operation
+ * : length - length of string
+ * Returns : -EINVAL, or 0
+ */
+static int
+powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+{
+ int ret = length;
+
+ if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) {
+ buffer += 12;
+ length -= 12;
+
+ if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
+ if (buffer[5] == '1')
+ powertecscsi_terminator_ctl(host, 1);
+ else if (buffer[5] == '0')
+ powertecscsi_terminator_ctl(host, 0);
+ else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
* int length, int host_no, int inout)
- * Purpose : Return information about the driver to a user process accessing
- * the /proc filesystem.
- * Params : buffer - a buffer to write information to
- * start - a pointer into this buffer set by this routine to the start
- * of the required information.
- * offset - offset into information that we have read upto.
- * length - length of buffer
- * host_no - host number to return information for
- * inout - 0 for reading, 1 for writing.
- * Returns : length of data written to buffer.
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
*/
int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
int length, int host_no, int inout)
@@ -346,27 +432,28 @@ int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
if (!host)
return 0;
- info = (PowerTecScsi_Info *)host->hostdata;
if (inout == 1)
- return -EINVAL;
+ return powertecscsi_set_proc_info(host, buffer, length);
+
+ info = (PowerTecScsi_Info *)host->hostdata;
begin = 0;
pos = sprintf(buffer,
"PowerTec SCSI driver version %d.%d.%d\n",
VER_MAJOR, VER_MINOR, VER_PATCH);
pos += sprintf(buffer + pos,
- "Address: %08X IRQ : %d DMA : %d\n"
- "FAS : %s\n\n"
+ "Address: %08lX IRQ : %d DMA : %d\n"
+ "FAS : %-10s TERM: %-3s\n\n"
"Statistics:\n",
host->io_port, host->irq, host->dma_channel,
- info->info.scsi.type);
+ info->info.scsi.type, info->control.terms ? "on" : "off");
pos += sprintf(buffer+pos,
- "Queued commands: %-10d Issued commands: %-10d\n"
- "Done commands : %-10d Reads : %-10d\n"
- "Writes : %-10d Others : %-10d\n"
- "Disconnects : %-10d Aborts : %-10d\n"
- "Resets : %-10d\n",
+ "Queued commands: %-10u Issued commands: %-10u\n"
+ "Done commands : %-10u Reads : %-10u\n"
+ "Writes : %-10u Others : %-10u\n"
+ "Disconnects : %-10u Aborts : %-10u\n"
+ "Resets : %-10u\n",
info->info.stats.queues, info->info.stats.removes,
info->info.stats.fins, info->info.stats.reads,
info->info.stats.writes, info->info.stats.miscs,
diff --git a/drivers/acorn/scsi/powertec.h b/drivers/acorn/scsi/powertec.h
index 203d81f17..bf9a8afc8 100644
--- a/drivers/acorn/scsi/powertec.h
+++ b/drivers/acorn/scsi/powertec.h
@@ -1,7 +1,7 @@
/*
* PowerTec SCSI driver
*
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1998 Russell King
*/
#ifndef POWERTECSCSI_H
#define POWERTECSCSI_H
@@ -64,10 +64,20 @@ use_new_eh_code: 0 \
#ifndef HOSTS_C
+#include <asm/dma.h>
+
+#define NR_SG 256
+
typedef struct {
- FAS216_Info info;
+ FAS216_Info info;
+
+ struct {
+ unsigned int term_port;
+ unsigned int terms;
+ } control;
- /* other info... */
+ /* other info... */
+ dmasg_t dmasg[NR_SG]; /* Scatter DMA list */
} PowerTecScsi_Info;
#endif /* HOSTS_C */
diff --git a/drivers/acorn/scsi/queue.c b/drivers/acorn/scsi/queue.c
index 823d5f1c6..b8f5ba0ad 100644
--- a/drivers/acorn/scsi/queue.c
+++ b/drivers/acorn/scsi/queue.c
@@ -20,6 +20,9 @@
#include "../../scsi/scsi.h"
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SCSI command queueing");
+
typedef struct queue_entry {
struct queue_entry *next;
struct queue_entry *prev;
@@ -298,7 +301,7 @@ Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag)
else
queue->tail = NULL;
} else {
- prev->next = q->next;
+ prev->next = q->next;
if (prev->next)
prev->next->prev = prev;
else