diff options
Diffstat (limited to 'drivers/acorn/scsi')
-rw-r--r-- | drivers/acorn/scsi/Config.in | 1 | ||||
-rw-r--r-- | drivers/acorn/scsi/Makefile | 12 | ||||
-rw-r--r-- | drivers/acorn/scsi/acornscsi.c | 18 | ||||
-rw-r--r-- | drivers/acorn/scsi/cumana_1.c | 9 | ||||
-rw-r--r-- | drivers/acorn/scsi/cumana_1.h | 2 | ||||
-rw-r--r-- | drivers/acorn/scsi/cumana_2.c | 731 | ||||
-rw-r--r-- | drivers/acorn/scsi/cumana_2.h | 17 | ||||
-rw-r--r-- | drivers/acorn/scsi/ecoscsi.c | 2 | ||||
-rw-r--r-- | drivers/acorn/scsi/ecoscsi.h | 2 | ||||
-rw-r--r-- | drivers/acorn/scsi/eesox.c | 589 | ||||
-rw-r--r-- | drivers/acorn/scsi/eesox.h | 81 | ||||
-rw-r--r-- | drivers/acorn/scsi/fas216.c | 1754 | ||||
-rw-r--r-- | drivers/acorn/scsi/fas216.h | 27 | ||||
-rw-r--r-- | drivers/acorn/scsi/msgqueue.c | 72 | ||||
-rw-r--r-- | drivers/acorn/scsi/msgqueue.h | 11 | ||||
-rw-r--r-- | drivers/acorn/scsi/oak.c | 9 | ||||
-rw-r--r-- | drivers/acorn/scsi/oak.h | 2 | ||||
-rw-r--r-- | drivers/acorn/scsi/powertec.c | 351 | ||||
-rw-r--r-- | drivers/acorn/scsi/powertec.h | 16 | ||||
-rw-r--r-- | drivers/acorn/scsi/queue.c | 5 |
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 |