diff options
Diffstat (limited to 'drivers/scsi/buslogic.c')
-rw-r--r-- | drivers/scsi/buslogic.c | 690 |
1 files changed, 372 insertions, 318 deletions
diff --git a/drivers/scsi/buslogic.c b/drivers/scsi/buslogic.c index f3e49c8b9..a5824b04b 100644 --- a/drivers/scsi/buslogic.c +++ b/drivers/scsi/buslogic.c @@ -1,13 +1,17 @@ /* - * buslogic.c (C) 1993, 1994 David B. Gentzel + * buslogic.c Copyright (C) 1993, 1994 David B. Gentzel * Low-level scsi driver for BusLogic adapters * by David B. Gentzel, Whitfield Software Services, Carnegie, PA * (gentzel@nova.enet.dec.com) * Thanks to BusLogic for providing the necessary documentation * - * The original version of this driver was derived from aha1542.[ch] which - * is Copyright (C) 1992 Tommy Thorn. Much has been reworked, but most of - * basic structure and substantial chunks of code still remain. + * The original version of this driver was derived from aha1542.[ch], + * which is Copyright (C) 1992 Tommy Thorn. Much has been reworked, but + * most of basic structure and substantial chunks of code still remain. + * + * Furthermore, many subsequent fixes and improvements to the aha1542 + * driver have been folded back into this driver. These changes to + * aha1542.[ch] are Copyright (C) 1993, 1994 Eric Youngdale. * * Thanks to the following individuals who have made contributions (of * (code, information, support, or testing) to this driver: @@ -24,7 +28,6 @@ * 5. Allow multiple boards to share an IRQ if the bus allows (EISA, MCA, * and PCI). * 6. Avoid using the 445S workaround for board revs >= D. - * 7. Get cmd_per_lun put in the Scsi_Host structure. */ /* @@ -53,16 +56,18 @@ * BT-747D - 747S + differential termination. * BT-757S - 747S + WIDE SCSI. * BT-757D - 747D + WIDE SCSI. - * BT-445S - VESA bus-master FAST SCSI with active termination and floppy - * support. + * BT-445S - VESA bus-master FAST SCSI with active termination + * and floppy support. * BT-445C - 445S + enhanced BIOS & firmware options. - * BT-946C - PCI bus-master FAST SCSI. (??? Nothing else known.) + * BT-946C - PCI bus-master FAST SCSI. + * BT-956C - PCI bus-master FAST/WIDE SCSI. * * ??? I believe other boards besides the 445 now have a "C" model, but I * have no facts on them. * * This driver SHOULD support all of these boards. It has only been tested - * with a 747S and 445S. + * with a 747S, 445S, 946C, and 956C; there is no PCI-specific support as + * yet. * * Should you require further information on any of these boards, BusLogic * can be reached at (408)492-9090. Their BBS # is (408)492-1984 (maybe BBS @@ -72,6 +77,10 @@ * unfinished, questionable, or wrong. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/string.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -79,6 +88,7 @@ #include <linux/types.h> #include <linux/ioport.h> #include <linux/delay.h> +#include <linux/config.h> #include <asm/io.h> #include <asm/system.h> @@ -95,6 +105,9 @@ # define BUSLOGIC_DEBUG 0 #endif +/* ??? Until kmalloc actually implements GFP_DMA, we can't depend on it... */ +#undef GFP_DMA + /* If different port addresses are needed (e.g. to install more than two cards), you must define BUSLOGIC_PORT_OVERRIDE to be a comma-separated list of the addresses which will be checked. This can also be used to resolve a @@ -107,7 +120,7 @@ The test is believed to fail on at least some AMI BusLogic clones. */ /* #define BIOS_TRANSLATION_OVERRIDE BIOS_TRANSLATION_BIG */ -#define BUSLOGIC_VERSION "1.13" +#define BUSLOGIC_VERSION "1.15" /* Not a random value - if this is too large, the system hangs for a long time waiting for something to happen if a board is not installed. */ @@ -126,22 +139,28 @@ /* Since the SG list is malloced, we have to limit the length. */ #define BUSLOGIC_MAX_SG (BUSLOGIC_SG_MALLOC / sizeof (struct chain)) -/* ??? Arbitrary. If we can dynamically allocate the mailbox arrays, I may - bump up this number. */ -#define BUSLOGIC_MAILBOXES 16 +/* Since the host adapters have room to buffer 32 commands internally, there + is some virtue in setting BUSLOGIC_MAILBOXES to 32. The maximum value + appears to be 255, since the Count parameter to the Initialize Extended + Mailbox command is limited to one byte. */ +#define BUSLOGIC_MAILBOXES 32 -#define BUSLOGIC_NONISA_CMDLUN 4 /* ??? Arbitrary (> 1) */ +#define BUSLOGIC_CMDLUN 4 /* Arbitrary, but seems to work well. */ /* BusLogic boards can be configured for quite a number of port addresses (six to be exact), but I generally do not want the driver poking around at random. We allow two port addresses - this allows people to use a BusLogic - with a MIDI card, which frequently also uses 0x330. */ -static const unsigned int bases[] = { + with a MIDI card, which frequently also uses 0x330. + + This can also be overridden on the command line to the kernel, via LILO or + LOADLIN. */ +static unsigned short bases[7] = { #ifdef BUSLOGIC_PORT_OVERRIDE - BUSLOGIC_PORT_OVERRIDE + BUSLOGIC_PORT_OVERRIDE, #else - 0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234 */ + 0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234, */ #endif + 0 }; #define BIOS_TRANSLATION_DEFAULT 0 /* Default case */ @@ -150,8 +169,8 @@ static const unsigned int bases[] = { struct hostdata { unsigned int bus_type; unsigned int bios_translation: 1; /* BIOS mapping (for compatibility) */ - size_t last_mbi_used; - size_t last_mbo_used; + int last_mbi_used; + int last_mbo_used; char model[7]; char firmware_rev[6]; Scsi_Cmnd *sc[BUSLOGIC_MAILBOXES]; @@ -169,7 +188,14 @@ static int restart(struct Scsi_Host *shpnt); #define INTR_RESET(base) outb(RINT, CONTROL(base)) -#define buslogic_printk buslogic_prefix(),printk +#define buslogic_printk buslogic_prefix(__PRETTY_FUNCTION__),printk + +#if defined(MODULE) && !defined(GFP_DMA) +# define CHECK_DMA_ADDR(isa, addr, badstmt) \ + do { if ((isa) && (addr) > (void *)ISA_DMA_THRESHOLD) badstmt; } while (0) +#else +# define CHECK_DMA_ADDR(isa, addr, badstmt) +#endif #define CHECK(cond) if (cond) ; else goto fail @@ -204,9 +230,9 @@ static __inline__ int wait(unsigned short port, } } -static void buslogic_prefix(void) +static void buslogic_prefix(const char *func) { - printk("BusLogic SCSI: "); + printk("BusLogic SCSI: %s: ", func); } static void buslogic_stat(unsigned int base) @@ -223,29 +249,33 @@ static void buslogic_stat(unsigned int base) static int buslogic_out(unsigned int base, const unsigned char *cmdp, size_t len) { + unsigned long flags = 0; + if (len == 1) { for (;;) { WAIT_WHILE(STATUS(base), CPRBSY); + save_flags(flags); cli(); if (!(inb(STATUS(base)) & CPRBSY)) { outb(*cmdp, COMMAND_PARAMETER(base)); - sti(); + restore_flags(flags); return FALSE; } - sti(); + restore_flags(flags); } } else { + save_flags(flags); cli(); while (len--) { WAIT_WHILE(STATUS(base), CPRBSY); outb(*cmdp++, COMMAND_PARAMETER(base)); } - sti(); + restore_flags(flags); } return FALSE; fail: - sti(); - buslogic_printk("buslogic_out failed(%u): ", len + 1); + restore_flags(flags); + buslogic_printk("failed(%u): ", len + 1); buslogic_stat(base); return TRUE; } @@ -255,17 +285,20 @@ static int buslogic_out(unsigned int base, const unsigned char *cmdp, sure whether the board will respond to the command we just sent. */ static int buslogic_in(unsigned int base, unsigned char *cmdp, size_t len) { + unsigned long flags; + + save_flags(flags); cli(); while (len--) { WAIT_UNTIL_FAST(STATUS(base), DIRRDY); *cmdp++ = inb(DATA_IN(base)); } - sti(); + restore_flags(flags); return FALSE; fail: - sti(); + restore_flags(flags); #if (BUSLOGIC_DEBUG & BD_IO) - buslogic_printk("buslogic_in failed(%u): ", len + 1); + buslogic_printk("failed(%u): ", len + 1); buslogic_stat(base); #endif return TRUE; @@ -386,184 +419,151 @@ static unsigned int makecode(unsigned int haerr, unsigned int scsierr) # endif #endif if (errstr != NULL) - buslogic_printk("makecode: %s (%02X)\n", errstr, haerr); + buslogic_printk("%s (%02X)\n", errstr, haerr); return (hosterr << 16) | scsierr; } -const char *buslogic_info(void) +/* ??? this should really be "const struct Scsi_Host *" */ +const char *buslogic_info(struct Scsi_Host *shpnt) { - return "BusLogic SCSI driver version " BUSLOGIC_VERSION; + return "BusLogic SCSI driver " BUSLOGIC_VERSION; } -/* A "high" level interrupt handler. */ -static void buslogic_interrupt(int junk) +/* + This is a major rewrite of the interrupt handler to support the newer + and faster PCI cards. While the previous interrupt handler was supposed + to handle multiple incoming becoming available mailboxes during the same + interrupt, my testing showed that in practice only a single mailbox was + ever made available. With the 946C and 956C, multiple incoming mailboxes + being ready for processing during a single interrupt occurs much more + frequently, and so care must be taken to avoid race conditions managing + the Host Adapter Interrupt Register, which can lead to lost interrupts. + + Leonard N. Zubkoff, 23-Mar-95 +*/ + +static void buslogic_interrupt(int irq, struct pt_regs * regs) { - void (*my_done)(Scsi_Cmnd *) = NULL; - int errstatus, mbistatus = MBX_NOT_IN_USE, number_serviced, found; - size_t mbi, mbo = 0; + int mbi, saved_mbo[BUSLOGIC_MAILBOXES]; + int base, interrupt_flags, found, i; struct Scsi_Host *shpnt; Scsi_Cmnd *sctmp; - int irqno, base, flag; - int needs_restart; struct mailbox *mb; struct ccb *ccb; - /* Magic - this -2 is only required for slow interrupt handlers */ - irqno = ((int *)junk)[-2]; - - shpnt = host[irqno - 9]; - if (!shpnt) - panic("buslogic.c: NULL SCSI host entry"); + shpnt = host[irq - 9]; + if (shpnt == NULL) + panic("buslogic_interrupt: NULL SCSI host entry"); mb = HOSTDATA(shpnt)->mb; ccb = HOSTDATA(shpnt)->ccbs; base = shpnt->io_port; -#if (BUSLOGIC_DEBUG & BD_INTERRUPT) - flag = inb(INTERRUPT(base)); - - buslogic_printk("buslogic_interrupt: "); - if (!(flag & INTV)) - printk("no interrupt? "); - if (flag & IMBL) - printk("IMBL "); - if (flag & MBOR) - printk("MBOR "); - if (flag & CMDC) - printk("CMDC "); - if (flag & RSTS) - printk("RSTS "); - printk("status %02X\n", inb(STATUS(base))); -#endif + /* + This interrupt handler is now specified to use the SA_INTERRUPT + protocol, so interrupts are inhibited on entry until explicitly + allowed again. Read the Host Adapter Interrupt Register, and + complain if there is no pending interrupt being signaled. + */ - number_serviced = 0; - needs_restart = 0; + interrupt_flags = inb(INTERRUPT(base)); - for (;;) { - flag = inb(INTERRUPT(base)); - - /* Check for unusual interrupts. If any of these happen, we should - probably do something special, but for now just printing a message - is sufficient. A SCSI reset detected is something that we really - need to deal with in some way. */ - if (flag & (MBOR | CMDC | RSTS)) { - buslogic_printk("Unusual flag:"); - if (flag & MBOR) - printk(" MBOR"); - if (flag & CMDC) - printk(" CMDC"); - if (flag & RSTS) { - needs_restart = 1; - printk(" RSTS"); - } - printk("\n"); - } + if (!(interrupt_flags & INTV)) + buslogic_printk("interrupt received, but INTV not set\n"); - INTR_RESET(base); + /* + Reset the Host Adapter Interrupt Register. It appears to be + important that this is only done once per interrupt to avoid + losing interrupts under heavy loads. + */ - cli(); + INTR_RESET(base); - mbi = HOSTDATA(shpnt)->last_mbi_used + 1; - if (mbi >= 2 * BUSLOGIC_MAILBOXES) - mbi = BUSLOGIC_MAILBOXES; + if (interrupt_flags & RSTS) + { + restart(shpnt); + return; + } - /* I use the "found" variable as I like to keep cli/sti pairs at the - same block level. Debugging dropped sti's is no fun... */ + /* + With interrupts still inhibited, scan through the incoming mailboxes + in strict round robin fashion saving the status information and + then freeing the mailbox. A second pass over the completed commands + will be made separately to complete their processing. + */ - found = FALSE; - do { - if (mb[mbi].status != MBX_NOT_IN_USE) { - found = TRUE; - break; - } - mbi++; - if (mbi >= 2 * BUSLOGIC_MAILBOXES) - mbi = BUSLOGIC_MAILBOXES; - } while (mbi != HOSTDATA(shpnt)->last_mbi_used); - - if (found) { - mbo = (struct ccb *)mb[mbi].ccbptr - ccb; - mbistatus = mb[mbi].status; - mb[mbi].status = MBX_NOT_IN_USE; - HOSTDATA(shpnt)->last_mbi_used = mbi; - } + mbi = HOSTDATA(shpnt)->last_mbi_used + 1; + if (mbi >= 2*BUSLOGIC_MAILBOXES) + mbi = BUSLOGIC_MAILBOXES; - sti(); + found = 0; - if (!found) { - /* Hmm, no mail. Must have read it the last time around. */ - if (!number_serviced && !needs_restart) - buslogic_printk("interrupt received, but no mail.\n"); - /* We detected a reset. Restart all pending commands for devices - that use the hard reset option. */ - if (needs_restart) - restart(shpnt); - return; - } + while (mb[mbi].status != MBX_NOT_IN_USE && found < BUSLOGIC_MAILBOXES) + { + int mbo = (struct ccb *)mb[mbi].ccbptr - ccb; -#if (BUSLOGIC_DEBUG & BD_INTERRUPT) - if (ccb[mbo].tarstat || ccb[mbo].hastat) - buslogic_printk("buslogic_interrupt: returning %08X (status %d)\n", - ((int)ccb[mbo].hastat << 16) | ccb[mbo].tarstat, - mb[mbi].status); -#endif + sctmp = HOSTDATA(shpnt)->sc[mbo]; - if (mbistatus == MBX_COMPLETION_NOT_FOUND) - continue; + /* + If sctmp has become NULL, higher level code must have aborted + this operation and called the necessary completion routine. + */ -#if (BUSLOGIC_DEBUG & BD_INTERRUPT) - buslogic_printk("...done %u %u\n", mbo, mbi); -#endif + if (sctmp != NULL && mb[mbi].status != MBX_COMPLETION_NOT_FOUND) + { + int result = 0; - sctmp = HOSTDATA(shpnt)->sc[mbo]; + saved_mbo[found++] = mbo; - if (!sctmp || !sctmp->scsi_done) { - buslogic_printk("buslogic_interrupt: Unexpected interrupt\n"); - buslogic_printk("tarstat=%02X, hastat=%02X id=%d lun=%d ccb#=%u\n", - ccb[mbo].tarstat, ccb[mbo].hastat, - ccb[mbo].id, ccb[mbo].lun, mbo); - return; - } + if (mb[mbi].status != MBX_COMPLETION_OK) + result = makecode(ccb[mbo].hastat, ccb[mbo].tarstat); - my_done = sctmp->scsi_done; - if (sctmp->host_scribble) - scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC); - - /* ??? more error checking left out here */ - if (mbistatus != MBX_COMPLETION_OK) { - /* ??? This is surely wrong, but I don't know what's right. */ - errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat); - } else - errstatus = 0; - -#if (BUSLOGIC_DEBUG & BD_INTERRUPT) - if (errstatus) - buslogic_printk("error: %04X %04X\n", - ccb[mbo].hastat, ccb[mbo].tarstat); - - if (status_byte(ccb[mbo].tarstat) == CHECK_CONDITION) { - size_t i; - - buslogic_printk("buslogic_interrupt: sense:"); - for (i = 0; i < sizeof sctmp->sense_buffer; i++) - printk(" %02X", sctmp->sense_buffer[i]); - printk("\n"); - } + sctmp->result = result; - if (errstatus) - buslogic_printk("buslogic_interrupt: returning %08X\n", errstatus); -#endif + mb[mbi].status = MBX_NOT_IN_USE; + } - sctmp->result = errstatus; - HOSTDATA(shpnt)->sc[mbo] = NULL; /* This effectively frees up - the mailbox slot, as far as - queuecommand is - concerned. */ - my_done(sctmp); - number_serviced++; - } + HOSTDATA(shpnt)->last_mbi_used = mbi; + + if (++mbi >= 2*BUSLOGIC_MAILBOXES) + mbi = BUSLOGIC_MAILBOXES; + } + + /* + With interrupts no longer inhibited, iterate over the completed + commands freeing resources and calling the completion routines. + Since we exit upon completion of this loop, there is no need to + inhibit interrupts before exit, as this will be handled by the + fast interrupt assembly code we return to. + */ + + sti(); + + for (i = 0; i < found; i++) + { + int mbo = saved_mbo[i]; + sctmp = HOSTDATA(shpnt)->sc[mbo]; + if (sctmp == NULL) continue; + /* + First, free any storage allocated for a scatter/gather + data segment list. + */ + if (sctmp->host_scribble) + scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC); + /* + Next, mark the SCSI Command as completed so it may be reused + for another command by buslogic_queuecommand. This also signals + to buslogic_reset that the command is no longer active. + */ + HOSTDATA(shpnt)->sc[mbo] = NULL; + /* + Finally, call the SCSI command completion handler. + */ + sctmp->scsi_done(sctmp); + } } + /* ??? Why does queuecommand return a value? scsi.c never looks at it... */ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) { @@ -575,9 +575,12 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) void *buff = scpnt->request_buffer; int bufflen = scpnt->request_bufflen; int mbo; - struct mailbox *mb; + unsigned long flags; + struct Scsi_Host *shpnt = scpnt->host; + struct mailbox *mb = HOSTDATA(shpnt)->mb; struct ccb *ccb; + #if (BUSLOGIC_DEBUG & BD_COMMAND) if (target > 1) { scpnt->result = DID_TIME_OUT << 16; @@ -589,8 +592,8 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) if (*cmd == REQUEST_SENSE) { #if (BUSLOGIC_DEBUG & (BD_COMMAND | BD_ERRORS)) if (bufflen != sizeof scpnt->sense_buffer) { - buslogic_printk("Wrong buffer length supplied for request sense" - " (%d)\n", + buslogic_printk("wrong buffer length supplied for request sense" + " (%d).\n", bufflen); } #endif @@ -608,12 +611,11 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) i = *(int *)(cmd + 2); else i = -1; - buslogic_printk("buslogic_queuecommand:" - " dev %d cmd %02X pos %d len %d ", + buslogic_printk("dev %d cmd %02X pos %d len %d ", target, *cmd, i, bufflen); - buslogic_stat(scpnt->host->io_port); - buslogic_printk("buslogic_queuecommand: dumping scsi cmd:"); - for (i = 0; i < (COMMAND_SIZE(*cmd)); i++) + buslogic_stat(shpnt->io_port); + buslogic_printk("dumping scsi cmd:"); + for (i = 0; i < scpnt->cmd_len; i++) printk(" %02X", cmd[i]); printk("\n"); if (*cmd == WRITE_10 || *cmd == WRITE_6) @@ -621,51 +623,53 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) } #endif - mb = HOSTDATA(scpnt->host)->mb; - ccb = HOSTDATA(scpnt->host)->ccbs; - /* Use the outgoing mailboxes in a round-robin fashion, because this is how the host adapter will scan for them. */ + save_flags(flags); cli(); - mbo = HOSTDATA(scpnt->host)->last_mbo_used + 1; + mbo = HOSTDATA(shpnt)->last_mbo_used + 1; if (mbo >= BUSLOGIC_MAILBOXES) mbo = 0; do { if (mb[mbo].status == MBX_NOT_IN_USE - && HOSTDATA(scpnt->host)->sc[mbo] == NULL) + && HOSTDATA(shpnt)->sc[mbo] == NULL) break; mbo++; if (mbo >= BUSLOGIC_MAILBOXES) mbo = 0; - } while (mbo != HOSTDATA(scpnt->host)->last_mbo_used); + } while (mbo != HOSTDATA(shpnt)->last_mbo_used); - if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(scpnt->host)->sc[mbo]) { - /* ??? Instead of panicing, should we enable OMBR interrupts and - sleep until we get one? */ - panic("buslogic.c: unable to find empty mailbox"); + if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(shpnt)->sc[mbo]) { + /* ??? Instead of failing, should we enable OMBR interrupts and sleep + until we get one? */ + restore_flags(flags); + buslogic_printk("unable to find empty mailbox.\n"); + goto fail; } - HOSTDATA(scpnt->host)->sc[mbo] = scpnt; /* This will effectively + HOSTDATA(shpnt)->sc[mbo] = scpnt; /* This will effectively prevent someone else from screwing with this cdb. */ - HOSTDATA(scpnt->host)->last_mbo_used = mbo; + HOSTDATA(shpnt)->last_mbo_used = mbo; - sti(); + restore_flags(flags); #if (BUSLOGIC_DEBUG & BD_COMMAND) buslogic_printk("sending command (%d %08X)...", mbo, done); #endif + ccb = &HOSTDATA(shpnt)->ccbs[mbo]; + /* This gets trashed for some reason */ - mb[mbo].ccbptr = &ccb[mbo]; + mb[mbo].ccbptr = ccb; - memset(&ccb[mbo], 0, sizeof (struct ccb)); + memset(ccb, 0, sizeof (struct ccb)); - ccb[mbo].cdblen = COMMAND_SIZE(*cmd); /* SCSI Command Descriptor + ccb->cdblen = scpnt->cmd_len; /* SCSI Command Descriptor Block Length */ direction = 0; @@ -674,33 +678,36 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) else if (*cmd == WRITE_10 || *cmd == WRITE_6) direction = 16; - memcpy(ccb[mbo].cdb, cmd, ccb[mbo].cdblen); + memcpy(ccb->cdb, cmd, ccb->cdblen); if (scpnt->use_sg) { struct scatterlist *sgpnt; struct chain *cptr; size_t i; - ccb[mbo].op = CCB_OP_INIT_SG; /* SCSI Initiator Command + ccb->op = CCB_OP_INIT_SG; /* SCSI Initiator Command w/scatter-gather */ scpnt->host_scribble = (unsigned char *)scsi_malloc(BUSLOGIC_SG_MALLOC); - if (scpnt->host_scribble == NULL) - panic("buslogic.c: unable to allocate DMA memory"); + if (scpnt->host_scribble == NULL) { + buslogic_printk("unable to allocate DMA memory.\n"); + goto fail; + } sgpnt = (struct scatterlist *)scpnt->request_buffer; cptr = (struct chain *)scpnt->host_scribble; - if (scpnt->use_sg > scpnt->host->sg_tablesize) { - buslogic_printk("buslogic_queuecommand: bad segment list," - " %d > %d\n", - scpnt->use_sg, scpnt->host->sg_tablesize); - panic("buslogic.c: bad segment list"); + if (scpnt->use_sg > shpnt->sg_tablesize) { + buslogic_printk("bad segment list, %d > %d.\n", + scpnt->use_sg, shpnt->sg_tablesize); + goto fail; } for (i = 0; i < scpnt->use_sg; i++) { + CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, sgpnt[i].address, + goto baddma); cptr[i].dataptr = sgpnt[i].address; cptr[i].datalen = sgpnt[i].length; } - ccb[mbo].datalen = scpnt->use_sg * sizeof (struct chain); - ccb[mbo].dataptr = cptr; + ccb->datalen = scpnt->use_sg * sizeof (struct chain); + ccb->dataptr = cptr; #if (BUSLOGIC_DEBUG & BD_COMMAND) { unsigned char *ptr; @@ -713,44 +720,54 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) } #endif } else { - ccb[mbo].op = CCB_OP_INIT; /* SCSI Initiator Command */ + ccb->op = CCB_OP_INIT; /* SCSI Initiator Command */ scpnt->host_scribble = NULL; - ccb[mbo].datalen = bufflen; - ccb[mbo].dataptr = buff; + CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, buff, goto baddma); + ccb->datalen = bufflen; + ccb->dataptr = buff; } - ccb[mbo].id = target; - ccb[mbo].lun = lun; - ccb[mbo].dir = direction; - ccb[mbo].rsalen = sizeof scpnt->sense_buffer; - ccb[mbo].senseptr = scpnt->sense_buffer; - ccb[mbo].linkptr = NULL; - ccb[mbo].commlinkid = 0; + ccb->id = target; + ccb->lun = lun; + ccb->dir = direction; + ccb->rsalen = sizeof scpnt->sense_buffer; + ccb->senseptr = scpnt->sense_buffer; + /* ccbcontrol, commlinkid, and linkptr are 0 due to above memset. */ #if (BUSLOGIC_DEBUG & BD_COMMAND) { size_t i; - buslogic_printk("buslogic_queuecommand: sending..."); - for (i = 0; i < sizeof ccb[mbo] - 10; i++) - printk(" %02X", ((unsigned char *)&ccb[mbo])[i]); + buslogic_printk("sending..."); + for (i = 0; i < sizeof(struct ccb) - 10; i++) + printk(" %02X", ((unsigned char *)ccb)[i]); printk("\n"); } #endif if (done) { #if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_printk("buslogic_queuecommand: now waiting for interrupt: "); - buslogic_stat(scpnt->host->io_port); + buslogic_printk("now waiting for interrupt: "); + buslogic_stat(shpnt->io_port); #endif scpnt->scsi_done = done; mb[mbo].status = MBX_ACTION_START; /* start scsi command */ - buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd); + buslogic_out(shpnt->io_port, buscmd, sizeof buscmd); #if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_stat(scpnt->host->io_port); + buslogic_stat(shpnt->io_port); #endif } else - buslogic_printk("buslogic_queuecommand: done can't be NULL\n"); + buslogic_printk("done can't be NULL.\n"); + + while (0) { +#if defined(MODULE) && !defined(GFP_DMA) + baddma: + buslogic_printk("address > 16MB used for ISA HA.\n"); +#endif + fail: + scpnt->result = DID_ERROR << 16; + done(scpnt); + } return 0; } @@ -764,14 +781,14 @@ static void internal_done(Scsi_Cmnd *scpnt) int buslogic_command(Scsi_Cmnd *scpnt) { #if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_printk("buslogic_command: calling buslogic_queuecommand\n"); + buslogic_printk("calling buslogic_queuecommand.\n"); #endif buslogic_queuecommand(scpnt, internal_done); scpnt->SCp.Status = 0; while (!scpnt->SCp.Status) - continue; + barrier(); return scpnt->result; } #endif @@ -802,7 +819,7 @@ static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt) while (0) { fail: - buslogic_printk("buslogic_detect: failed setting up mailboxes\n"); + buslogic_printk("failed setting up mailboxes.\n"); } INTR_RESET(base); @@ -820,7 +837,7 @@ static int getconfig(unsigned int base, unsigned char *irq, int i; #if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("getconfig: called\n"); + buslogic_printk("called\n"); #endif i = inb(STATUS(base)); @@ -854,8 +871,8 @@ static int getconfig(unsigned int base, unsigned char *irq, *irq = 15; break; default: - buslogic_printk("Unable to determine BusLogic IRQ level." - " Disabling board.\n"); + buslogic_printk("unable to determine BusLogic IRQ level, " + " disabling board.\n"); goto fail; } *id = inquiry_result[2] & 0x7; @@ -898,8 +915,8 @@ static int getconfig(unsigned int base, unsigned char *irq, *dma = 7; break; default: - buslogic_printk("Unable to determine BusLogic DMA channel." - " Disabling board.\n"); + buslogic_printk("unable to determine BusLogic DMA channel," + " disabling board.\n"); goto fail; } else @@ -908,7 +925,7 @@ static int getconfig(unsigned int base, unsigned char *irq, while (0) { fail: #if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("buslogic_detect: query board settings\n"); + buslogic_printk("query board settings\n"); #endif return TRUE; } @@ -930,7 +947,7 @@ static int buslogic_query(unsigned int base, unsigned char *trans, unsigned int i; #if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("buslogic_query: called\n"); + buslogic_printk("called\n"); #endif /* Quick and dirty test for presence of the card. */ @@ -983,7 +1000,7 @@ static int buslogic_query(unsigned int base, unsigned char *trans, firmware_rev[2] = inquiry_result[3]; firmware_rev[3] = '\0'; #if 0 - buslogic_printk("Inquiry Bytes: %02X(%c) %02X(%c)\n", + buslogic_printk("inquiry bytes: %02X(%c) %02X(%c)\n", inquiry_result[0], inquiry_result[0], inquiry_result[1], inquiry_result[1]); #endif @@ -1050,13 +1067,15 @@ static int buslogic_query(unsigned int base, unsigned char *trans, /* bus_type from getconfig doesn't differentiate between EISA/VESA. We override using the model number here. */ - /* ??? What bus_type gets returned for PCI? */ switch (*bus_type) { case 'E': switch (model[0]) { case '4': *bus_type = 'V'; break; + case '9': + *bus_type = 'P'; + break; case '7': break; default: @@ -1071,7 +1090,7 @@ static int buslogic_query(unsigned int base, unsigned char *trans, while (0) { fail: #if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("buslogic_query: query board settings\n"); + buslogic_printk("query board settings\n"); #endif return TRUE; } @@ -1089,19 +1108,21 @@ int buslogic_detect(Scsi_Host_Template *tpnt) char bus_type; unsigned short max_sg; unsigned char bios_translation; + unsigned long flags; const unsigned char *bios; char *model; char *firmware_rev; struct Scsi_Host *shpnt; size_t indx; + int unchecked_isa_dma; int count = 0; #if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("buslogic_detect:\n"); + buslogic_printk("called\n"); #endif tpnt->can_queue = BUSLOGIC_MAILBOXES; - for (indx = 0; indx < ARRAY_SIZE(bases); indx++) + for (indx = 0; bases[indx] != 0; indx++) if (!check_region(bases[indx], 4)) { shpnt = scsi_register(tpnt, sizeof (struct hostdata)); @@ -1117,13 +1138,45 @@ int buslogic_detect(Scsi_Host_Template *tpnt) buslogic_stat(base); #endif + /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */ + unchecked_isa_dma = (bus_type == 'A'); +#ifndef CONFIG_NO_BUGGY_BUSLOGIC + /* There is a hardware bug in the BT-445S prior to revision D. + When the BIOS is enabled and you have more than 16MB of memory, + the card mishandles memory transfers over 16MB which (if viewed + as a 24-bit address) overlap with the BIOS address space. For + example if you have the BIOS located at physical address + 0xDC000 and a DMA transfer from the card to RAM starts at + physical address 0x10DC000 then the transfer is messed up. To + be more precise every fourth byte of the transfer is messed up. + (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP + BusLogic driver.) */ + + if (bus_type == 'V' /* 445 */ + && firmware_rev[0] <= '3' /* S */ + && bios != NULL) { /* BIOS enabled */ +#if 1 + /* Now that LNZ's forbidden_addr stuff is in the higher level + scsi code, we can use this instead. */ + /* Avoid addresses which "mirror" the BIOS for DMA. */ + shpnt->forbidden_addr = (unsigned long)bios; + shpnt->forbidden_size = 16 * 1024; +#else + /* Use double-buffering. */ + unchecked_isa_dma = TRUE; +#endif + } +#endif + + CHECK_DMA_ADDR(unchecked_isa_dma, shpnt, goto unregister); + if (setup_mailboxes(base, shpnt)) goto unregister; /* Set the Bus on/off-times as not to ruin floppy performance. CMD_BUSOFF_TIME is a noop for EISA boards (and possibly others???). */ - if (bus_type != 'E') { + if (bus_type != 'E' && bus_type != 'P') { /* The default ON/OFF times for BusLogic adapters is 7/4. */ static const unsigned char oncmd[] = { CMD_BUSON_TIME, 7 }; static const unsigned char offcmd[] = { CMD_BUSOFF_TIME, 5 }; @@ -1136,19 +1189,18 @@ int buslogic_detect(Scsi_Host_Template *tpnt) WAIT_UNTIL(INTERRUPT(base), CMDC); while (0) { fail: - buslogic_printk("buslogic_detect:" - " setting bus on/off-time failed\n"); + buslogic_printk("setting bus on/off-time failed.\n"); } INTR_RESET(base); } - buslogic_printk("Configuring %s HA at port 0x%03X, IRQ %u", + buslogic_printk("configuring %s HA at port 0x%03X, IRQ %u", (bus_type == 'A' ? "ISA" : (bus_type == 'E' ? "EISA" : (bus_type == 'M' ? "MCA" : (bus_type == 'P' ? "PCI" : (bus_type == 'V' ? "VESA" - : (bus_type == 'X' ? "EISA/VESA" + : (bus_type == 'X' ? "EISA/VESA/PCI" : "Unknown")))))), base, irq); if (bios != NULL) @@ -1161,31 +1213,32 @@ int buslogic_detect(Scsi_Host_Template *tpnt) if (model[0]) printk(" (revision %d)", model[6]); printk("\n"); - buslogic_printk("Firmware revision: %s\n", firmware_rev); + buslogic_printk("firmware revision: %s\n", firmware_rev); #if (BUSLOGIC_DEBUG & BD_DETECT) buslogic_stat(base); #endif #if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("buslogic_detect: enable interrupt channel %d\n", - irq); + buslogic_printk("enable interrupt channel %d.\n", irq); #endif + save_flags(flags); cli(); - if (request_irq(irq, buslogic_interrupt, 0, "buslogic")) { - buslogic_printk("Unable to allocate IRQ for " + if (request_irq(irq, buslogic_interrupt, + SA_INTERRUPT, "buslogic")) { + buslogic_printk("unable to allocate IRQ for " "BusLogic controller.\n"); - sti(); + restore_flags(flags); goto unregister; } if (dma) { if (request_dma(dma, "buslogic")) { - buslogic_printk("Unable to allocate DMA channel for " + buslogic_printk("unable to allocate DMA channel for " "BusLogic controller.\n"); free_irq(irq); - sti(); + restore_flags(flags); goto unregister; } @@ -1198,59 +1251,26 @@ int buslogic_detect(Scsi_Host_Template *tpnt) host[irq - 9] = shpnt; shpnt->this_id = id; - /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */ - shpnt->unchecked_isa_dma = (bus_type == 'A'); -#ifndef CONFIG_NO_BUGGY_BUSLOGIC - /* There is a hardware bug in the BT-445S prior to revision D. - When the BIOS is enabled and you have more than 16MB of memory, - the card mishandles memory transfers over 16MB which (if viewed - as a 24-bit address) overlap with the BIOS address space. For - example if you have the BIOS located at physical address - 0xDC000 and a DMA transfer from the card to RAM starts at - physical address 0x10DC000 then the transfer is messed up. To - be more precise every fourth byte of the transfer is messed up. - (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP - BusLogic driver.) */ - - if (bus_type == 'V' /* 445 */ - && firmware_rev[0] <= '3' /* S */ - && bios != NULL) { /* BIOS enabled */ -#if 0 - /* ??? Once LNZ's forbidden_addr stuff makes it into the higher - level scsi code, we can use this instead. */ - /* Avoid addresses which "mirror" the BIOS for DMA. */ - shpnt->forbidden_addr = bios; - shpnt->forbidden_size = 16 * 1024; -#else - /* Use double-buffering. */ - shpnt->unchecked_isa_dma = TRUE; -#endif - } -#endif + shpnt->unchecked_isa_dma = unchecked_isa_dma; /* Have to keep cmd_per_lun at 1 for ISA machines otherwise lots of memory gets sucked up for bounce buffers. */ - /* ??? Unfortunately, cmd_per_lun is only in the - Scsi_Host_Template structure, not the Scsi_Host structure. - Therefore, this could cause high memory consumption if a system - has multiple BusLogic adapters which are a mix of ISA and - non-ISA. */ - if (!shpnt->unchecked_isa_dma) - shpnt->hostt->cmd_per_lun = BUSLOGIC_NONISA_CMDLUN; + shpnt->cmd_per_lun = (unchecked_isa_dma ? 1 : BUSLOGIC_CMDLUN); shpnt->sg_tablesize = max_sg; if (shpnt->sg_tablesize > BUSLOGIC_MAX_SG) shpnt->sg_tablesize = BUSLOGIC_MAX_SG; /* ??? shpnt->base should really be "const unsigned char *"... */ shpnt->base = (unsigned char *)bios; shpnt->io_port = base; + shpnt->n_io_port = 4; /* Number of bytes of I/O space used */ shpnt->dma_channel = dma; shpnt->irq = irq; HOSTDATA(shpnt)->bios_translation = bios_translation; if (bios_translation == BIOS_TRANSLATION_BIG) - buslogic_printk("Using extended bios translation.\n"); + buslogic_printk("using extended bios translation.\n"); HOSTDATA(shpnt)->last_mbi_used = 2 * BUSLOGIC_MAILBOXES - 1; HOSTDATA(shpnt)->last_mbo_used = BUSLOGIC_MAILBOXES - 1; memset(HOSTDATA(shpnt)->sc, 0, sizeof HOSTDATA(shpnt)->sc); - sti(); + restore_flags(flags); #if 0 { @@ -1266,8 +1286,7 @@ int buslogic_detect(Scsi_Host_Template *tpnt) buf[i] = 0x87; for (i = 0; i < 2; i++) if (!buslogic_command(i, cmd, buf, sizeof buf)) { - buslogic_printk("buslogic_detect: LU %u " - "sector_size %d device_size %d\n", + buslogic_printk("LU %u sector_size %d device_size %d\n", i, *(int *)(buf + 4), *(int *)buf); } @@ -1289,8 +1308,8 @@ int buslogic_detect(Scsi_Host_Template *tpnt) } #endif - snarf_region(bases[indx], 4); /* Register the IO ports that - we use */ + request_region(bases[indx], 4,"buslogic"); + /* Register the IO ports that we use */ count++; continue; unregister: @@ -1317,7 +1336,7 @@ static int restart(struct Scsi_Host *shpnt) count++; } - buslogic_printk("Potential to restart %d stalled commands...\n", count); + buslogic_printk("potential to restart %d stalled commands...\n", count); #if 0 /* start scsi command */ if (count) @@ -1335,65 +1354,67 @@ int buslogic_abort(Scsi_Cmnd *scpnt) #if 1 static const unsigned char buscmd[] = { CMD_START_SCSI }; struct mailbox *mb; - size_t mbi, mbo; + int mbi, mbo, last_mbi; + unsigned long flags; unsigned int i; - buslogic_printk("buslogic_abort: %X %X\n", + buslogic_printk("%X %X\n", inb(STATUS(scpnt->host->io_port)), inb(INTERRUPT(scpnt->host->io_port))); + save_flags(flags); cli(); mb = HOSTDATA(scpnt->host)->mb; - mbi = HOSTDATA(scpnt->host)->last_mbi_used + 1; + last_mbi = HOSTDATA(scpnt->host)->last_mbi_used; + mbi = last_mbi + 1; if (mbi >= 2 * BUSLOGIC_MAILBOXES) mbi = BUSLOGIC_MAILBOXES; do { if (mb[mbi].status != MBX_NOT_IN_USE) break; + last_mbi = mbi; mbi++; if (mbi >= 2 * BUSLOGIC_MAILBOXES) mbi = BUSLOGIC_MAILBOXES; } while (mbi != HOSTDATA(scpnt->host)->last_mbi_used); - sti(); if (mb[mbi].status != MBX_NOT_IN_USE) { - buslogic_printk("Lost interrupt discovered on irq %d" - " - attempting to recover\n", + buslogic_printk("lost interrupt discovered on irq %d, " + " - attempting to recover...\n", scpnt->host->irq); - { - int intval[3]; - - intval[0] = scpnt->host->irq; - buslogic_interrupt((int)&intval[2]); - return SCSI_ABORT_SUCCESS; - } + HOSTDATA(scpnt->host)->last_mbi_used = last_mbi; + buslogic_interrupt(scpnt->host->irq, NULL); + restore_flags(flags); + return SCSI_ABORT_SUCCESS; } + restore_flags(flags); /* OK, no lost interrupt. Try looking to see how many pending commands we think we have. */ for (i = 0; i < BUSLOGIC_MAILBOXES; i++) if (HOSTDATA(scpnt->host)->sc[i]) { if (HOSTDATA(scpnt->host)->sc[i] == scpnt) { - buslogic_printk("Timed out command pending for %4.4X\n", + buslogic_printk("timed out command pending for %4.4X.\n", scpnt->request.dev); if (HOSTDATA(scpnt->host)->mb[i].status != MBX_NOT_IN_USE) { - buslogic_printk("OGMB still full - restarting\n"); + buslogic_printk("OGMB still full - restarting...\n"); buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd); } } else - buslogic_printk("Other pending command %4.4X\n", + buslogic_printk("other pending command: %4.4X\n", scpnt->request.dev); } #endif #if (BUSLOGIC_DEBUG & BD_ABORT) - buslogic_printk("buslogic_abort\n"); + buslogic_printk("called\n"); #endif #if 1 /* This section of code should be used carefully - some devices cannot abort a command, and this merely makes it worse. */ + save_flags(flags); cli(); for (mbo = 0; mbo < BUSLOGIC_MAILBOXES; mbo++) if (scpnt == HOSTDATA(scpnt->host)->sc[mbo]) { @@ -1401,7 +1422,7 @@ int buslogic_abort(Scsi_Cmnd *scpnt) buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd); break; } - sti(); + restore_flags(flags); #endif return SCSI_ABORT_SNOOZE; @@ -1418,7 +1439,7 @@ int buslogic_reset(Scsi_Cmnd *scpnt) unsigned int i; #if (BUSLOGIC_DEBUG & BD_RESET) - buslogic_printk("buslogic_reset\n"); + buslogic_printk("called\n"); #endif #if 0 /* This does a scsi reset for all devices on the bus. */ @@ -1438,7 +1459,7 @@ int buslogic_reset(Scsi_Cmnd *scpnt) interrupt for the commands that we aborted with the specified target, or do we generate this on our own? Try it without first and see what happens. */ - buslogic_printk("Sent BUS DEVICE RESET to target %d\n", + buslogic_printk("sent BUS DEVICE RESET to target %d.\n", scpnt->target); /* If the first does not work, then try the second. I think the @@ -1453,7 +1474,7 @@ int buslogic_reset(Scsi_Cmnd *scpnt) sctmp->result = DID_RESET << 16; if (sctmp->host_scribble) scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC); - buslogic_printk("Sending DID_RESET for target %d\n", + buslogic_printk("sending DID_RESET for target %d.\n", scpnt->target); sctmp->scsi_done(scpnt); @@ -1510,3 +1531,36 @@ int buslogic_biosparam(Disk *disk, int dev, int *ip) ip[2] = 1024; */ return 0; } + +/* called from init/main.c */ +void buslogic_setup(char *str, int *ints) +{ + static const unsigned short valid_bases[] + = { 0x130, 0x134, 0x230, 0x234, 0x330, 0x334 }; + static size_t setup_idx = 0; + size_t i; + + if (setup_idx >= ARRAY_SIZE(bases) - 1) { + buslogic_printk("called too many times. Bad LILO params?\n"); + return; + } + if (ints[0] != 1) { + buslogic_printk("malformed command line.\n"); + buslogic_printk("usage: buslogic=<portbase>\n"); + return; + } + for (i = 0; i < ARRAY_SIZE(valid_bases); i++) + if (valid_bases[i] == ints[1]) { + bases[setup_idx++] = ints[1]; + bases[setup_idx] = 0; + return; + } + buslogic_printk("invalid base 0x%X specified.\n", ints[1]); +} + +#ifdef MODULE +/* Eventually this will go into an include file, but that's later... */ +Scsi_Host_Template driver_template = BUSLOGIC; + +# include "scsi_module.c" +#endif |