diff -urN lx-1.2.8/Makefile linux/Makefile --- lx-1.2.8/Makefile Wed May 3 05:56:28 1995 +++ linux/Makefile Fri May 12 10:04:53 1995 @@ -232,6 +232,7 @@ mrproper: clean rm -f include/linux/autoconf.h include/linux/version.h rm -f drivers/sound/local.h + rm -f drivers/scsi/aic7770 drivers/scsi/aic7xxx_seq.h rm -f .version .config* config.in config.old rm -f include/asm rm -f .depend `find . -name .depend -print` diff -urN lx-1.2.8/arch/i386/config.in linux/arch/i386/config.in --- lx-1.2.8/arch/i386/config.in Sun Apr 30 12:10:44 1995 +++ linux/arch/i386/config.in Fri May 12 10:04:53 1995 @@ -92,7 +92,7 @@ bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y -bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n +bool 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX n bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n bool 'EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support' CONFIG_SCSI_EATA_DMA n bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n diff -urN lx-1.2.8/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- lx-1.2.8/drivers/scsi/Makefile Mon Apr 24 18:11:31 1995 +++ linux/drivers/scsi/Makefile Fri May 12 10:04:53 1995 @@ -25,6 +25,7 @@ SCSI_OBJS = SCSI_SRCS = +SCSI_DEP = SCSI_MODULE_OBJS = ifdef CONFIG_SCSI @@ -76,9 +77,10 @@ SCSI_SRCS := $(SCSI_SRCS) aha1740.c endif -ifdef CONFIG_SCSI_AHA274X -SCSI_OBJS := $(SCSI_OBJS) aha274x.o -SCSI_SRCS := $(SCSI_SRCS) aha274x.c +ifdef CONFIG_SCSI_AIC7XXX +SCSI_OBJS := $(SCSI_OBJS) aic7xxx.o +SCSI_SRCS := $(SCSI_SRCS) aic7xxx.c +SCSI_DEP := $(SCSI_DEP) aic7xxx_seq.h endif ifdef CONFIG_SCSI_BUSLOGIC @@ -180,8 +182,8 @@ aic7770: aic7770.c $(CC) $(CFLAGS) -o $@ aic7770.c -aha274x_seq.h: aic7770 aha274x.seq - ./aic7770 -o $@ aha274x.seq +aic7xxx_seq.h: aic7770 aic7xxx.seq + ./aic7770 -o $@ aic7xxx.seq seagate.o: seagate.c $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c @@ -201,7 +203,7 @@ echo $(SCSI_MODULE_OBJS) > ../../modules/SCSI_MODULES (cd ../../modules;for i in $(SCSI_MODULE_OBJS); do ln -sf ../drivers/scsi/$$i .; done) -dep: +dep: $(SCSI_DEP) $(CPP) -M $(AHA152X) $(SCSI_SRCS) > .depend $(CPP) -M -DMODULE $(SCSI_MODULE_OBJS:.o=.c) >> .depend diff -urN lx-1.2.8/drivers/scsi/README.aha274x linux/drivers/scsi/README.aha274x --- lx-1.2.8/drivers/scsi/README.aha274x Sun Nov 20 14:50:47 1994 +++ linux/drivers/scsi/README.aha274x Fri May 12 10:04:53 1995 @@ -1,94 +0,0 @@ -@(#)README 1.15 94/10/29 jda - -AHA274x/284x DRIVER - -*** THIS SHOULD BE CONSIDERED BETA SOFTWARE *** - -BACKGROUND & LIMITATIONS - -For various reasons, we ended up with one of these cards under the -impression that support was soon forthcoming. In mid-May, I asked -Scott Ferris (the official person who's supposed to be writing this -driver) what documentation he used, _finally_ got it from Adaptec, -and started writing this driver. It is now at what I would consider -a stable state - it runs our news server and is battered by SCSI -requests 24 hours a day without dying. There are a few devices it -reportedly doesn't like working with - those are being sorted out. Due -to some unexpected equipment loans, I am able to support this at least -for the time being. - -YOU MUST HAVE THE BIOS ENABLED OR THIS WILL NOT WORK. The BIOS extracts -some configuration information that I cannot get to portably yet, as -well as provides some self-tests which this driver does not attempt to -duplicate. - -Scott's driver development is stalled for now, and after discussions -with him, this is now officially out of "pre-alpha" status and into -beta until the remaining device problems can be resolved. The latest -patches can be obtained via anonymous ftp from ftp.cpsc.ucalgary.ca in -/pub/systems/linux/aha274x. - -It supports both EISA 274x and VL-bus 284x, either single or twin-bus cards -(but not the second SCSI bus of twin cards - see aha274x.c), and supports -disconnection, synchronous SCSI, and scatter-gather. Unlike previous -versions, abort() and reset() are now implemented, and both hosts.c and -aha274x.c should give a clean compile. Code is now present to detect parity -errors, but has not been tested. - -I wrote this using a 1.0.9 kernel. Unfortunately, I'm getting tired of -#ifdef'ing everything to handle two or three different evolutionary steps -in the SCSI kernel code, so I've upgraded my system to 1.1.49, and will -only leave in code to support versions from about 1.1.45 onward. - -Thanks to patches supplied by Mark Olson , this driver -will now work with the 284x series (the VL-bus version of this card). The -294x (PCI-bus) is being worked on, and initial support for it will be ready -soon. - -Under protest, this driver is subject to the GPL - see the file -COPYING for details. - -Thanks to the following people for bug fixes/code improvements (also -thanks to the people who have sent me feedback): - - "David F. Carlson" - Jimen Ching - mday@artisoft.com (Matt Day) - "Dean W. Gehnert" - Darcy Grant - Alan Hourihane - isely@fncrd8.fnal.gov (Mike Isely) - Mike Jerger - tm@netcom.com (Toshiyasu Morita) - neal@interact.org (Neal Norwitz) - Mark Olson - map@europa.ecn.uoknor.edu (Michael A. Parker) - Thomas Scheunemann - -Special thanks to Drew Eckhardt for -fielding my questions about synchronous negotiation. Steffen Moeller - sent me installation instructions which -were previously included in this README. - -David Pirie was nice enough to loan me his -2842 card for a week so I could track down one bug, as well as his -CD-ROM drive later, and also thanks to Doug Fortune at Riley's Data Share -in Calgary, who arranged a long-term loan of a 2842 board for further work. - -Many thanks to the fearless prerelease testers! Dean Gehnert has been -building Slackware boot disks for the driver, which are available from -ftp.cpsc.ucalgary.ca in /pub/systems/linux/aha274x/slackware_boot. - -Carl Riches has set up a mailing list -for aic7xxx driver development. To subscribe, send a message to -aic7770-list@poplar1.cfr.washington.edu with a message body of: - - subscribe AIC7770-LIST - -Please direct questions and discussions to that list instead of me. When -sending bug reports, please include a description of your hardware, the -release numbers displayed by the driver at boot time, and as accurate a -facsimile of any error message you're mailing about. - -John Aycock -aycock@cpsc.ucalgary.ca diff -urN lx-1.2.8/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx --- lx-1.2.8/drivers/scsi/README.aic7xxx Wed Dec 31 19:00:00 1969 +++ linux/drivers/scsi/README.aic7xxx Fri May 12 10:04:53 1995 @@ -0,0 +1,94 @@ +@(#)README 1.15 94/10/29 jda + +AHA274x/284x DRIVER + +*** THIS SHOULD BE CONSIDERED BETA SOFTWARE *** + +BACKGROUND & LIMITATIONS + +For various reasons, we ended up with one of these cards under the +impression that support was soon forthcoming. In mid-May, I asked +Scott Ferris (the official person who's supposed to be writing this +driver) what documentation he used, _finally_ got it from Adaptec, +and started writing this driver. It is now at what I would consider +a stable state - it runs our news server and is battered by SCSI +requests 24 hours a day without dying. There are a few devices it +reportedly doesn't like working with - those are being sorted out. Due +to some unexpected equipment loans, I am able to support this at least +for the time being. + +YOU MUST HAVE THE BIOS ENABLED OR THIS WILL NOT WORK. The BIOS extracts +some configuration information that I cannot get to portably yet, as +well as provides some self-tests which this driver does not attempt to +duplicate. + +Scott's driver development is stalled for now, and after discussions +with him, this is now officially out of "pre-alpha" status and into +beta until the remaining device problems can be resolved. The latest +patches can be obtained via anonymous ftp from ftp.cpsc.ucalgary.ca in +/pub/systems/linux/aha274x. + +It supports both EISA 274x and VL-bus 284x, either single or twin-bus cards +(but not the second SCSI bus of twin cards - see aha274x.c), and supports +disconnection, synchronous SCSI, and scatter-gather. Unlike previous +versions, abort() and reset() are now implemented, and both hosts.c and +aha274x.c should give a clean compile. Code is now present to detect parity +errors, but has not been tested. + +I wrote this using a 1.0.9 kernel. Unfortunately, I'm getting tired of +#ifdef'ing everything to handle two or three different evolutionary steps +in the SCSI kernel code, so I've upgraded my system to 1.1.49, and will +only leave in code to support versions from about 1.1.45 onward. + +Thanks to patches supplied by Mark Olson , this driver +will now work with the 284x series (the VL-bus version of this card). The +294x (PCI-bus) is being worked on, and initial support for it will be ready +soon. + +Under protest, this driver is subject to the GPL - see the file +COPYING for details. + +Thanks to the following people for bug fixes/code improvements (also +thanks to the people who have sent me feedback): + + "David F. Carlson" + Jimen Ching + mday@artisoft.com (Matt Day) + "Dean W. Gehnert" + Darcy Grant + Alan Hourihane + isely@fncrd8.fnal.gov (Mike Isely) + Mike Jerger + tm@netcom.com (Toshiyasu Morita) + neal@interact.org (Neal Norwitz) + Mark Olson + map@europa.ecn.uoknor.edu (Michael A. Parker) + Thomas Scheunemann + +Special thanks to Drew Eckhardt for +fielding my questions about synchronous negotiation. Steffen Moeller + sent me installation instructions which +were previously included in this README. + +David Pirie was nice enough to loan me his +2842 card for a week so I could track down one bug, as well as his +CD-ROM drive later, and also thanks to Doug Fortune at Riley's Data Share +in Calgary, who arranged a long-term loan of a 2842 board for further work. + +Many thanks to the fearless prerelease testers! Dean Gehnert has been +building Slackware boot disks for the driver, which are available from +ftp.cpsc.ucalgary.ca in /pub/systems/linux/aha274x/slackware_boot. + +Carl Riches has set up a mailing list +for aic7xxx driver development. To subscribe, send a message to +aic7770-list@poplar1.cfr.washington.edu with a message body of: + + subscribe AIC7770-LIST + +Please direct questions and discussions to that list instead of me. When +sending bug reports, please include a description of your hardware, the +release numbers displayed by the driver at boot time, and as accurate a +facsimile of any error message you're mailing about. + +John Aycock +aycock@cpsc.ucalgary.ca diff -urN lx-1.2.8/drivers/scsi/aha274x.c linux/drivers/scsi/aha274x.c --- lx-1.2.8/drivers/scsi/aha274x.c Mon Jan 16 00:17:37 1995 +++ linux/drivers/scsi/aha274x.c Fri May 12 10:04:53 1995 @@ -1,1490 +0,0 @@ -/* - * @(#)aha274x.c 1.29 94/10/29 jda - * - * Adaptec 274x device driver for Linux. - * Copyright (c) 1994 The University of Calgary Department of Computer Science. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Sources include the Adaptec 1740 driver (aha1740.c), the - * Ultrastor 24F driver (ultrastor.c), various Linux kernel - * source, the Adaptec EISA config file (!adp7771.cfg), the - * Adaptec AHA-2740A Series User's Guide, the Linux Kernel - * Hacker's Guide, Writing a SCSI Device Driver for Linux, - * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA - * overlay file (adp7770.ovl), the Adaptec AHA-2740 Series - * Technical Reference Manual, the Adaptec AIC-7770 Data - * Book, the ANSI SCSI specification, the ANSI SCSI-2 - * specification (draft 10c), ... - * - * On a twin-bus adapter card, channel B is ignored. Rationale: - * it would greatly complicate the sequencer and host driver code, - * and both busses are multiplexed on to the EISA bus anyway. So - * I don't really see any technical advantage to supporting both. - * - * As well, multiple adapter card using the same IRQ level are - * not supported. It doesn't make sense to configure the cards - * this way from a performance standpoint. Not to mention that - * the kernel would have to support two devices per registered IRQ. - */ - -#include -#include -#include -#include -#include -#include - -#include "../block/blk.h" -#include "sd.h" -#include "scsi.h" -#include "hosts.h" -#include "aha274x.h" - -/* - * There should be a specific return value for this in scsi.h, but - * it seems that most drivers ignore it. - */ -#define DID_UNDERFLOW DID_ERROR - -/* EISA stuff */ - -#define MINEISA 1 -#define MAXEISA 15 -#define SLOTBASE(x) ((x) << 12) - -#define MAXIRQ 15 - -/* AIC-7770 offset definitions */ - -#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */ -#define O_MAXREG(x) ((x) + 0xcbf) - -#define O_SCSISEQ(x) ((x) + 0xc00) /* scsi sequence control */ -#define O_SCSISIGI(x) ((x) + 0xc03) /* scsi control signal read */ -#define O_SCSISIGO(x) ((x) + 0xc03) /* scsi control signal write */ -#define O_SCSIID(x) ((x) + 0xc05) /* scsi id */ -#define O_SSTAT0(x) ((x) + 0xc0b) /* scsi status register 0 */ -#define O_CLRSINT1(x) ((x) + 0xc0c) /* clear scsi interrupt 1 */ -#define O_SSTAT1(x) ((x) + 0xc0c) /* scsi status register 1 */ -#define O_SELID(x) ((x) + 0xc19) /* [re]selection id */ -#define O_SBLKCTL(x) ((x) + 0xc1f) /* scsi block control */ -#define O_SEQCTL(x) ((x) + 0xc60) /* sequencer control */ -#define O_SEQRAM(x) ((x) + 0xc61) /* sequencer ram data */ -#define O_SEQADDR(x) ((x) + 0xc62) /* sequencer address (W) */ -#define O_BIDx(x) ((x) + 0xc80) /* board id */ -#define O_BCTL(x) ((x) + 0xc84) /* board control */ -#define O_HCNTRL(x) ((x) + 0xc87) /* host control */ -#define O_SCBPTR(x) ((x) + 0xc90) /* scb pointer */ -#define O_INTSTAT(x) ((x) + 0xc91) /* interrupt status */ -#define O_ERROR(x) ((x) + 0xc92) /* hard error */ -#define O_CLRINT(x) ((x) + 0xc92) /* clear interrupt status */ -#define O_SCBCNT(x) ((x) + 0xc9a) /* scb auto increment */ -#define O_QINFIFO(x) ((x) + 0xc9b) /* queue in fifo */ -#define O_QINCNT(x) ((x) + 0xc9c) /* queue in count */ -#define O_QOUTFIFO(x) ((x) + 0xc9d) /* queue out fifo */ -#define O_QOUTCNT(x) ((x) + 0xc9e) /* queue out count */ -#define O_SCBARRAY(x) ((x) + 0xca0) /* scb array start */ - -/* host adapter offset definitions */ - -#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */ -#define HA_MSG_FLAGS(x) ((x) + 0xc35) /* outgoing message flag */ -#define HA_MSG_LEN(x) ((x) + 0xc36) /* outgoing message length */ -#define HA_MSG_START(x) ((x) + 0xc37) /* outgoing message body */ -#define HA_ARG_1(x) ((x) + 0xc4c) /* sdtr <-> rate parameters */ -#define HA_ARG_2(x) ((x) + 0xc4d) -#define HA_RETURN_1(x) ((x) + 0xc4c) -#define HA_RETURN_2(x) ((x) + 0xc4d) -#define HA_SIGSTATE(x) ((x) + 0xc4e) /* value in SCSISIGO */ -#define HA_NEEDSDTR(x) ((x) + 0xc4f) /* synchronous negotiation? */ - -#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */ -#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */ -#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */ - -/* debugging code */ - -#define AHA274X_DEBUG - -/* - * If a parity error occurs during a data transfer phase, run the - * command to completion - it's easier that way - making a note - * of the error condition in this location. This then will modify - * a DID_OK status into a DID_PARITY one for the higher-level SCSI - * code. - */ -#define aha274x_parity(cmd) ((cmd)->SCp.Status) - -/* - * Since the sequencer code DMAs the scatter-gather structures - * directly from memory, we use this macro to assert that the - * kernel structure hasn't changed. - */ -#define SG_STRUCT_CHECK(sg) \ - ((char *)&(sg).address - (char *)&(sg) != 0 || \ - (char *)&(sg).length - (char *)&(sg) != 8 || \ - sizeof((sg).address) != 4 || \ - sizeof((sg).length) != 4 || \ - sizeof(sg) != 12) - -/* - * "Static" structures. Note that these are NOT initialized - * to zero inside the kernel - we have to initialize them all - * explicitly. - * - * We support a maximum of one adapter card per IRQ level (see the - * rationale for this above). On an interrupt, use the IRQ as an - * index into aha274x_boards[] to locate the card information. - */ -static struct Scsi_Host *aha274x_boards[MAXIRQ + 1]; - -struct aha274x_host { - int base; /* card base address */ - int startup; /* intr type check */ - volatile int unpause; /* value for HCNTRL */ - volatile Scsi_Cmnd *SCB_array[AHA274X_MAXSCB]; /* active commands */ -}; - -struct aha274x_scb { - unsigned char control; - unsigned char target_channel_lun; /* 4/1/3 bits */ - unsigned char SG_segment_count; - unsigned char SG_list_pointer[4]; - unsigned char SCSI_cmd_pointer[4]; - unsigned char SCSI_cmd_length; - unsigned char RESERVED[2]; /* must be zero */ - unsigned char target_status; - unsigned char residual_data_count[3]; - unsigned char residual_SG_segment_count; - unsigned char data_pointer[4]; - unsigned char data_count[3]; -#if 0 - /* - * No real point in transferring this to the - * SCB registers. - */ - unsigned char RESERVED[6]; -#endif -}; - -/* - * NB. This table MUST be ordered shortest period first. - */ -static struct { - short period; - short rate; - char *english; -} aha274x_synctab[] = { - {100, 0, "10.0"}, - {125, 1, "8.0"}, - {150, 2, "6.67"}, - {175, 3, "5.7"}, - {200, 4, "5.0"}, - {225, 5, "4.4"}, - {250, 6, "4.0"}, - {275, 7, "3.6"} -}; - -static int aha274x_synctab_max = - sizeof(aha274x_synctab) / sizeof(aha274x_synctab[0]); - -enum aha_type { - T_NONE, - T_274X, - T_284X, - T_MAX -}; - -#ifdef AHA274X_DEBUG - - extern int vsprintf(char *, const char *, va_list); - - static - void debug(const char *fmt, ...) - { - va_list ap; - char buf[256]; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - printk(buf); - va_end(ap); - } - - static - void debug_config(enum aha_type type, int base) - { - int ioport2, ioport3, ioport4; - - static char *BRT[T_MAX][16] = { - { }, /* T_NONE */ - { - "2", "???", "???", "12", /* T_274X */ - "???", "???", "???", "28", - "???", "???", "???", "44", - "???", "???", "???", "60" - }, - { - "2", "4", "8", "12", /* T_284X */ - "16", "20", "24", "28", - "32", "36", "40", "44", - "48", "52", "56", "60" - } - }; - static int DFT[4] = { - 0, 50, 75, 100 - }; - static int SST[4] = { - 256, 128, 64, 32 - }; - - ioport2 = inb(HA_HOSTCONF(base)); - ioport3 = inb(HA_SCSICONF(base)); - ioport4 = inb(HA_INTDEF(base)); - - if (type == T_284X) - printk("AHA284X AT SLOT %d:\n", base >> 12); - else - printk("AHA274X AT EISA SLOT %d:\n", base >> 12); - - printk(" irq %d\n" - " bus release time %s bclks\n" - " data fifo threshold %d%%\n", - ioport4 & 0xf, - BRT[type][(ioport2 >> 2) & 0xf], - DFT[(ioport2 >> 6) & 0x3]); - - printk(" SCSI CHANNEL A:\n" - " scsi id %d\n" - " scsi bus parity check %sabled\n" - " scsi selection timeout %d ms\n" - " scsi bus reset at power-on %sabled\n", - ioport3 & 0x7, - (ioport3 & 0x20) ? "en" : "dis", - SST[(ioport3 >> 3) & 0x3], - (ioport3 & 0x40) ? "en" : "dis"); - - if (type == T_274X) { - printk(" scsi bus termination %sabled\n", - (ioport3 & 0x80) ? "en" : "dis"); - } - } - - static - void debug_rate(int base, int rate) - { - int target = inb(O_SCSIID(base)) >> 4; - - if (rate) { - printk("aha274x: target %d now synchronous at %sMb/s\n", - target, - aha274x_synctab[(rate >> 4) & 0x7].english); - } else { - printk("aha274x: target %d using asynchronous mode\n", - target); - } - } - -#else - -# define debug(fmt, args...) -# define debug_config(x) -# define debug_rate(x,y) - -#endif AHA274X_DEBUG - -/* - * XXX - these options apply unilaterally to _all_ 274x/284x - * cards in the system. This should be fixed, but then, - * does anyone really have more than one in a machine? - */ -static int aha274x_extended = 0; /* extended translation on? */ - -void aha274x_setup(char *s, int *dummy) -{ - int i; - char *p; - - static struct { - char *name; - int *flag; - } options[] = { - {"extended", &aha274x_extended}, - {NULL, NULL } - }; - - for (p = strtok(s, ","); p; p = strtok(NULL, ",")) { - for (i = 0; options[i].name; i++) - if (!strcmp(options[i].name, p)) - *(options[i].flag) = !0; - } -} - -static -void aha274x_getscb(int base, struct aha274x_scb *scb) -{ - /* - * This is almost identical to aha274x_putscb(). - */ - outb(0x80, O_SCBCNT(base)); /* SCBAUTO */ - - asm volatile("cld\n\t" - "rep\n\t" - "insb" - : /* no output */ - :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base)) - :"di", "cx", "dx"); - - outb(0, O_SCBCNT(base)); -} - -/* - * How much data should be transferred for this SCSI command? Stop - * at segment sg_last if it's a scatter-gather command so we can - * compute underflow easily. - */ -static -unsigned aha274x_length(Scsi_Cmnd *cmd, int sg_last) -{ - int i, segments; - unsigned length; - struct scatterlist *sg; - - segments = cmd->use_sg - sg_last; - sg = (struct scatterlist *)cmd->buffer; - - if (cmd->use_sg) { - for (i = length = 0; - i < cmd->use_sg && i < segments; - i++) - { - length += sg[i].length; - } - } else - length = cmd->request_bufflen; - - return(length); -} - -static -void aha274x_sg_check(Scsi_Cmnd *cmd) -{ - int i; - struct scatterlist *sg = (struct scatterlist *)cmd->buffer; - - if (cmd->use_sg) { - for (i = 0; i < cmd->use_sg; i++) - if ((unsigned)sg[i].length > 0xffff) - panic("aha274x_sg_check: s/g segment > 64k\n"); - } -} - -static -void aha274x_to_scsirate(unsigned char *rate, - unsigned char transfer, - unsigned char offset) -{ - int i; - - transfer *= 4; - - for (i = 0; i < aha274x_synctab_max-1; i++) { - - if (transfer == aha274x_synctab[i].period) { - *rate = (aha274x_synctab[i].rate << 4) | (offset & 0xf); - return; - } - - if (transfer > aha274x_synctab[i].period && - transfer < aha274x_synctab[i+1].period) - { - *rate = (aha274x_synctab[i+1].rate << 4) | - (offset & 0xf); - return; - } - } - *rate = 0; -} - -/* - * Pause the sequencer and wait for it to actually stop - this - * is important since the sequencer can disable pausing for critical - * sections. - */ -#define PAUSE_SEQUENCER(p) \ - do { \ - outb(0xe, O_HCNTRL(p->base)); /* IRQMS|PAUSE|INTEN */ \ - \ - while ((inb(O_HCNTRL(p->base)) & 0x4) == 0) \ - ; \ - } while (0) - -/* - * Unpause the sequencer. Unremarkable, yet done often enough to - * warrant an easy way to do it. - */ -#define UNPAUSE_SEQUENCER(p) \ - outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */ - -/* - * See comments in aha274x_loadram() wrt this. - */ -#define RESTART_SEQUENCER(p) \ - do { \ - do { \ - outb(0x2, O_SEQCTL(p->base)); \ - } while (inw(O_SEQADDR(p->base)) != 0); \ - \ - UNPAUSE_SEQUENCER(p); \ - } while (0) - -/* - * Since we declared this using SA_INTERRUPT, interrupts should - * be disabled all through this function unless we say otherwise. - */ -static -void aha274x_isr(int irq, struct pt_regs * regs) -{ - int base, intstat; - struct aha274x_host *p; - - p = (struct aha274x_host *)aha274x_boards[irq]->hostdata; - base = p->base; - - /* - * Check the startup flag - if no commands have been queued, - * we probably have the interrupt type set wrong. Reverse - * the stored value and the active one in the host control - * register. - */ - if (p->startup) { - p->unpause ^= 0x8; - outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base)); - return; - } - - /* - * Handle all the interrupt sources - especially for SCSI - * interrupts, we won't get a second chance at them. - */ - intstat = inb(O_INTSTAT(base)); - - if (intstat & 0x8) { /* BRKADRINT */ - - panic("aha274x_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n", - inb(O_ERROR(base)), inw(O_SEQADDR(base))); - } - - if (intstat & 0x4) { /* SCSIINT */ - - int scbptr = inb(O_SCBPTR(base)); - int status = inb(O_SSTAT1(base)); - Scsi_Cmnd *cmd; - - cmd = (Scsi_Cmnd *)p->SCB_array[scbptr]; - if (!cmd) { - printk("aha274x_isr: no command for scb (scsiint)\n"); - /* - * Turn off the interrupt and set status - * to zero, so that it falls through the - * reset of the SCSIINT code. - */ - outb(status, O_CLRSINT1(base)); - UNPAUSE_SEQUENCER(p); - outb(0x4, O_CLRINT(base)); /* undocumented */ - status = 0; - } - p->SCB_array[scbptr] = NULL; - - /* - * Only the SCSI Status 1 register has information - * about exceptional conditions that we'd have a - * SCSIINT about; anything in SSTAT0 will be handled - * by the sequencer. Note that there can be multiple - * bits set. - */ - if (status & 0x80) { /* SELTO */ - /* - * Hardware selection timer has expired. Turn - * off SCSI selection sequence. - */ - outb(0, O_SCSISEQ(base)); - cmd->result = DID_TIME_OUT << 16; - - /* - * If there's an active message, it belongs to the - * command that is getting punted - remove it. - */ - outb(0, HA_MSG_FLAGS(base)); - - /* - * Shut off the offending interrupt sources, reset - * the sequencer address to zero and unpause it, - * then call the high-level SCSI completion routine. - * - * WARNING! This is a magic sequence! After many - * hours of guesswork, turning off the SCSI interrupts - * in CLRSINT? does NOT clear the SCSIINT bit in - * INTSTAT. By writing to the (undocumented, unused - * according to the AIC-7770 manual) third bit of - * CLRINT, you can clear INTSTAT. But, if you do it - * while the sequencer is paused, you get a BRKADRINT - * with an Illegal Host Address status, so the - * sequencer has to be restarted first. - */ - outb(0x80, O_CLRSINT1(base)); /* CLRSELTIMO */ - RESTART_SEQUENCER(p); - - outb(0x4, O_CLRINT(base)); /* undocumented */ - cmd->scsi_done(cmd); - } - - if (status & 0x4) { /* SCSIPERR */ - /* - * A parity error has occurred during a data - * transfer phase. Flag it and continue. - */ - printk("aha274x: parity error on target %d, lun %d\n", - cmd->target, - cmd->lun); - aha274x_parity(cmd) = DID_PARITY; - - /* - * Clear interrupt and resume as above. - */ - outb(0x4, O_CLRSINT1(base)); /* CLRSCSIPERR */ - UNPAUSE_SEQUENCER(p); - - outb(0x4, O_CLRINT(base)); /* undocumented */ - } - - if ((status & (0x8|0x4)) == 0 && status) { - /* - * We don't know what's going on. Turn off the - * interrupt source and try to continue. - */ - printk("aha274x_isr: sstat1 = 0x%x\n", status); - outb(status, O_CLRSINT1(base)); - UNPAUSE_SEQUENCER(p); - outb(0x4, O_CLRINT(base)); /* undocumented */ - } - } - - if (intstat & 0x2) { /* CMDCMPLT */ - - int complete, old_scbptr; - struct aha274x_scb scb; - unsigned actual; - Scsi_Cmnd *cmd; - - /* - * The sequencer will continue running when it - * issues this interrupt. There may be >1 commands - * finished, so loop until we've processed them all. - */ - do { - complete = inb(O_QOUTFIFO(base)); - - cmd = (Scsi_Cmnd *)p->SCB_array[complete]; - if (!cmd) { - printk("aha274x warning: " - "no command for scb (cmdcmplt)\n"); - continue; - } - p->SCB_array[complete] = NULL; - - PAUSE_SEQUENCER(p); - - /* - * After pausing the sequencer (and waiting - * for it to stop), save its SCB pointer, then - * write in our completed one and read the SCB - * registers. Afterwards, restore the saved - * pointer, unpause the sequencer and call the - * higher-level completion function - unpause - * first since we have no idea how long done() - * will take. - */ - old_scbptr = inb(O_SCBPTR(base)); - outb(complete, O_SCBPTR(base)); - - aha274x_getscb(base, &scb); - outb(old_scbptr, O_SCBPTR(base)); - - UNPAUSE_SEQUENCER(p); - - cmd->result = scb.target_status | - (aha274x_parity(cmd) << 16); - - /* - * Did we underflow? At this time, there's only - * one other driver that bothers to check for this, - * and cmd->underflow seems to be set rather half- - * heartedly in the higher-level SCSI code. - */ - actual = aha274x_length(cmd, - scb.residual_SG_segment_count); - - actual -= ((scb.residual_data_count[2] << 16) | - (scb.residual_data_count[1] << 8) | - (scb.residual_data_count[0])); - - if (actual < cmd->underflow) { - printk("aha274x: target %d underflow - " - "wanted (at least) %u, got %u\n", - cmd->target, cmd->underflow, actual); - - cmd->result = scb.target_status | - (DID_UNDERFLOW << 16); - } - - cmd->scsi_done(cmd); - - /* - * Clear interrupt status before checking - * the output queue again. This eliminates - * a race condition whereby a command could - * complete between the queue poll and the - * interrupt clearing, so notification of the - * command being complete never made it back - * up to the kernel. - */ - outb(0x2, O_CLRINT(base)); /* CLRCMDINT */ - - } while (inb(O_QOUTCNT(base))); - } - - if (intstat & 0x1) { /* SEQINT */ - - unsigned char transfer, offset, rate; - - /* - * Although the sequencer is paused immediately on - * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT - * condition will have unpaused the sequencer before - * this point. - */ - PAUSE_SEQUENCER(p); - - switch (intstat & 0xf0) { - case 0x00: - panic("aha274x_isr: unknown scsi bus phase\n"); - case 0x10: - debug("aha274x_isr warning: " - "issuing message reject, 1st byte 0x%x\n", - inb(HA_REJBYTE(base))); - break; - case 0x20: - panic("aha274x_isr: reconnecting target %d " - "didn't issue IDENTIFY message\n", - (inb(O_SELID(base)) >> 4) & 0xf); - case 0x30: - debug("aha274x_isr: sequencer couldn't find match " - "for reconnecting target %d - issuing ABORT\n", - (inb(O_SELID(base)) >> 4) & 0xf); - break; - case 0x40: - transfer = inb(HA_ARG_1(base)); - offset = inb(HA_ARG_2(base)); - aha274x_to_scsirate(&rate, transfer, offset); - outb(rate, HA_RETURN_1(base)); - debug_rate(base, rate); - break; - default: - debug("aha274x_isr: seqint, " - "intstat = 0x%x, scsisigi = 0x%x\n", - intstat, inb(O_SCSISIGI(base))); - break; - } - - outb(0x1, O_CLRINT(base)); /* CLRSEQINT */ - UNPAUSE_SEQUENCER(p); - } -} - -/* - * Probing for EISA boards: it looks like the first two bytes - * are a manufacturer code - three characters, five bits each: - * - * BYTE 0 BYTE 1 BYTE 2 BYTE 3 - * ?1111122 22233333 PPPPPPPP RRRRRRRR - * - * The characters are baselined off ASCII '@', so add that value - * to each to get the real ASCII code for it. The next two bytes - * appear to be a product and revision number, probably vendor- - * specific. This is what is being searched for at each port, - * and what should probably correspond to the ID= field in the - * ECU's .cfg file for the card - if your card is not detected, - * make sure your signature is listed in the array. - * - * The fourth byte's lowest bit seems to be an enabled/disabled - * flag (rest of the bits are reserved?). - */ - -static -enum aha_type aha274x_probe(int slot, int s_base) -{ - int i; - unsigned char buf[4]; - - static struct { - int n; - unsigned char signature[sizeof(buf)]; - enum aha_type type; - } S[] = { - {4, { 0x04, 0x90, 0x77, 0x71 }, T_274X}, /* host adapter 274x */ - {4, { 0x04, 0x90, 0x77, 0x70 }, T_274X}, /* motherboard 274x */ - {4, { 0x04, 0x90, 0x77, 0x56 }, T_284X}, /* 284x, BIOS enabled */ - }; - - for (i = 0; i < sizeof(buf); i++) { - /* - * The VL-bus cards need to be primed by - * writing before a signature check. - */ - outb(0x80 + i, s_base); - buf[i] = inb(s_base + i); - } - - for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) { - if (!memcmp(buf, S[i].signature, S[i].n)) { - /* - * Signature match on enabled card? - */ - if (inb(s_base + 4) & 1) - return(S[i].type); - printk("aha274x disabled at slot %d, ignored\n", slot); - } - } - return(T_NONE); -} - -/* - * Return ' ' for plain 274x, 'T' for twin-channel, 'W' for - * wide channel, '?' for anything else. - */ - -static -char aha274x_type(int base) -{ - /* - * The AIC-7770 can be wired so that, on chip reset, - * the SCSI Block Control register indicates how many - * busses the chip is configured for. - */ - switch (inb(O_SBLKCTL(base))) { - case 0: - return(' '); - case 2: - return('W'); - case 8: - return('T'); - default: - printk("aha274x has unknown bus configuration\n"); - return('?'); - } -} - -static -void aha274x_loadram(int base) -{ - static unsigned char seqprog[] = { - /* - * Each sequencer instruction is 29 bits - * long (fill in the excess with zeroes) - * and has to be loaded from least -> most - * significant byte, so this table has the - * byte ordering reversed. - */ -# include "aha274x_seq.h" - }; - - /* - * When the AIC-7770 is paused (as on chip reset), the - * sequencer address can be altered and a sequencer - * program can be loaded by writing it, byte by byte, to - * the sequencer RAM port - the Adaptec documentation - * recommends using REP OUTSB to do this, hence the inline - * assembly. Since the address autoincrements as we load - * the program, reset it back to zero afterward. Disable - * sequencer RAM parity error detection while loading, and - * make sure the LOADRAM bit is enabled for loading. - */ - outb(0x83, O_SEQCTL(base)); /* PERRORDIS|SEQRESET|LOADRAM */ - - asm volatile("cld\n\t" - "rep\n\t" - "outsb" - : /* no output */ - :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base)) - :"si", "cx", "dx"); - - /* - * WARNING! This is a magic sequence! After extensive - * experimentation, it seems that you MUST turn off the - * LOADRAM bit before you play with SEQADDR again, else - * you will end up with parity errors being flagged on - * your sequencer program. (You would also think that - * turning off LOADRAM and setting SEQRESET to reset the - * address to zero would work, but you need to do it twice - * for it to take effect on the address. Timing problem?) - */ - outb(0, O_SEQCTL(base)); - do { - /* - * Actually, reset it until - * the address shows up as - * zero just to be safe.. - */ - outb(0x2, O_SEQCTL(base)); /* SEQRESET */ - - } while (inw(O_SEQADDR(base)) != 0); -} - -static -int aha274x_register(Scsi_Host_Template *template, - enum aha_type type, - int base) -{ - int i, irq, scsi_id; - struct Scsi_Host *host; - struct aha274x_host *p; - - /* - * Give the AIC-7770 a reset - reading the 274x's registers - * returns zeroes unless you do. This forces a pause of the - * Sequencer. - */ - outb(1, O_HCNTRL(base)); /* CHIPRST */ - - /* - * The IRQ level in i/o port 4 maps directly onto the real - * IRQ number. If it's ok, register it with the kernel. - * - * NB. the Adaptec documentation says the IRQ number is only - * in the lower four bits; the ECU information shows the - * high bit being used as well. Which is correct? - */ - irq = inb(HA_INTDEF(base)) & 0xf; - if (irq < 9 || irq > 15) { - printk("aha274x uses unsupported IRQ level, ignoring\n"); - return(0); - } - - /* - * Lock out other contenders for our i/o space. - */ - request_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base), "aha27x"); - - /* - * Any card-type-specific adjustments before we register - * the scsi host(s). - */ - - scsi_id = inb(HA_SCSICONF(base)) & 0x7; - - switch (aha274x_type(base)) { - case 'T': - printk("aha274x warning: ignoring channel B of 274x-twin\n"); - break; - case ' ': - break; - default: - printk("aha274x is an unsupported type, ignoring\n"); - return(0); - } - - /* - * Before registry, make sure that the offsets of the - * struct scatterlist are what the sequencer will expect, - * otherwise disable scatter-gather altogether until someone - * can fix it. This is important since the sequencer will - * DMA elements of the SG array in while executing commands. - */ - if (template->sg_tablesize != SG_NONE) { - struct scatterlist sg; - - if (SG_STRUCT_CHECK(sg)) { - printk("aha274x warning: kernel scatter-gather " - "structures changed, disabling it\n"); - template->sg_tablesize = SG_NONE; - } - } - - /* - * Register each "host" and fill in the returned Scsi_Host - * structure as best we can. Some of the parameters aren't - * really relevant for EISA, and none of the high-level SCSI - * code looks at it anyway.. why are the fields there? Also - * save the pointer so that we can find the information when - * an IRQ is triggered. - */ - host = scsi_register(template, sizeof(struct aha274x_host)); - host->this_id = scsi_id; - host->irq = irq; - - aha274x_boards[irq] = host; - - p = (struct aha274x_host *)host->hostdata; - for (i = 0; i < AHA274X_MAXSCB; i++) - p->SCB_array[i] = NULL; - p->base = base; - - /* - * The interrupt trigger is different depending - * on whether the card is EISA or VL-bus - sometimes. - * The startup variable will be cleared once the first - * command is queued, and is checked in the isr to - * try and detect when the interrupt type is set - * incorrectly, triggering an interrupt immediately. - */ - p->unpause = (type != T_274X ? 0x2 : 0xa); - p->startup = !0; - - /* - * Register IRQ with the kernel _after_ the host information - * is set up, in case we take an interrupt right away, due to - * the interrupt type being set wrong. - */ - if (request_irq(irq, aha274x_isr, SA_INTERRUPT, "AHA274x/284x")) { - printk("aha274x couldn't register irq %d, ignoring\n", irq); - return(0); - } - - /* - * A reminder until this can be detected automatically. - */ - printk("aha274x: extended translation %sabled\n", - aha274x_extended ? "en" : "dis"); - - /* - * Print out debugging information before re-enabling - * the card - a lot of registers on it can't be read - * when the sequencer is active. - */ - debug_config(type, base); - - /* - * Load the sequencer program, then re-enable the board - - * resetting the AIC-7770 disables it, leaving the lights - * on with nobody home. - */ - aha274x_loadram(base); - outb(1, O_BCTL(base)); /* ENABLE */ - - /* - * Set the host adapter registers to indicate that synchronous - * negotiation should be attempted the first time the targets - * are communicated with. Also initialize the active message - * flag to indicate that there is no message. - */ - outb(0xff, HA_NEEDSDTR(base)); - outb(0, HA_MSG_FLAGS(base)); - - /* - * Unpause the sequencer before returning and enable - * interrupts - we shouldn't get any until the first - * command is sent to us by the high-level SCSI code. - */ - UNPAUSE_SEQUENCER(p); - return(1); -} - -int aha274x_detect(Scsi_Host_Template *template) -{ - enum aha_type type; - int found = 0, slot, base; - - for (slot = MINEISA; slot <= MAXEISA; slot++) { - - base = SLOTBASE(slot); - - if (check_region(O_MINREG(base), - O_MAXREG(base)-O_MINREG(base))) - { - /* - * Some other driver has staked a - * claim to this i/o region already. - */ - continue; - } - - type = aha274x_probe(slot, O_BIDx(base)); - - if (type != T_NONE) { - /* - * We "find" a 274x if we locate the card - * signature and we can set it up and register - * it with the kernel without incident. - */ - found += aha274x_register(template, type, base); - } - } - template->name = (char *)aha274x_info(NULL); - return(found); -} - -const char *aha274x_info(struct Scsi_Host * shost) -{ - return("Adaptec AHA274x/284x (EISA/VL-bus -> Fast SCSI) " - AHA274X_SEQ_VERSION "/" - AHA274X_H_VERSION "/" - "1.29"); -} - -int aha274x_command(Scsi_Cmnd *cmd) -{ - /* - * This is a relic of non-interrupt-driven SCSI - * drivers. With the can_queue variable set, this - * should never be called. - */ - panic("aha274x_command was called\n"); -} - -static -void aha274x_buildscb(struct aha274x_host *p, - Scsi_Cmnd *cmd, - struct aha274x_scb *scb) -{ - void *addr; - unsigned length; - - memset(scb, 0, sizeof(*scb)); - - /* - * NB. channel selection (bit 3) is always zero. - */ - scb->target_channel_lun = ((cmd->target << 4) & 0xf0) | - (cmd->lun & 0x7); - - /* - * The interpretation of request_buffer and request_bufflen - * changes depending on whether or not use_sg is zero; a - * non-zero use_sg indicates the number of elements in the - * scatter-gather array. - * - * The AIC-7770 can't support transfers of any sort larger - * than 2^24 (three-byte count) without backflips. For what - * the kernel is doing, this shouldn't occur. I hope. - */ - length = aha274x_length(cmd, 0); - - /* - * The sequencer code cannot yet handle scatter-gather segments - * larger than 64k (two-byte length). The 1.1.x kernels, however, - * have a four-byte length field in the struct scatterlist, so - * make sure we don't exceed 64k on these kernels for now. - */ - aha274x_sg_check(cmd); - - if (length > 0xffffff) { - panic("aha274x_buildscb: can't transfer > 2^24 - 1 bytes\n"); - } - - /* - * XXX - this relies on the host data being stored in a - * little-endian format. - */ - addr = cmd->cmnd; - scb->SCSI_cmd_length = cmd->cmd_len; - memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); - - if (cmd->use_sg) { -#if 0 - debug("aha274x_buildscb: SG used, %d segments, length %u\n", - cmd->use_sg, - length); -#endif - scb->SG_segment_count = cmd->use_sg; - memcpy(scb->SG_list_pointer, - &cmd->request_buffer, - sizeof(scb->SG_list_pointer)); - } else { - scb->SG_segment_count = 0; - memcpy(scb->data_pointer, - &cmd->request_buffer, - sizeof(scb->data_pointer)); - memcpy(scb->data_count, - &cmd->request_bufflen, - sizeof(scb->data_count)); - } -} - -static -void aha274x_putscb(int base, struct aha274x_scb *scb) -{ - /* - * By turning on the SCB auto increment, any reference - * to the SCB I/O space postincrements the SCB address - * we're looking at. So turn this on and dump the relevant - * portion of the SCB to the card. - */ - outb(0x80, O_SCBCNT(base)); /* SCBAUTO */ - - asm volatile("cld\n\t" - "rep\n\t" - "outsb" - : /* no output */ - :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base)) - :"si", "cx", "dx"); - - outb(0, O_SCBCNT(base)); -} - -int aha274x_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) -{ - long flags; - int empty, old_scbptr; - struct aha274x_host *p; - struct aha274x_scb scb; - -#if 0 - debug("aha274x_queue: cmd 0x%x (size %u), target %d, lun %d\n", - cmd->cmnd[0], - cmd->cmd_len, - cmd->target, - cmd->lun); -#endif - - p = (struct aha274x_host *)cmd->host->hostdata; - - /* - * Construct the SCB beforehand, so the sequencer is - * paused a minimal amount of time. - */ - aha274x_buildscb(p, cmd, &scb); - - /* - * Clear the startup flag - we can now legitimately - * expect interrupts. - */ - p->startup = 0; - - /* - * This is a critical section, since we don't want the - * interrupt routine mucking with the host data or the - * card. Since the kernel documentation is vague on - * whether or not we are in a cli/sti pair already, save - * the flags to be on the safe side. - */ - save_flags(flags); - cli(); - - /* - * Find a free slot in the SCB array to load this command - * into. Since can_queue is set to AHA274X_MAXSCB, we - * should always find one. - */ - for (empty = 0; empty < AHA274X_MAXSCB; empty++) - if (!p->SCB_array[empty]) - break; - if (empty == AHA274X_MAXSCB) - panic("aha274x_queue: couldn't find a free scb\n"); - - /* - * Pause the sequencer so we can play with its registers - - * wait for it to acknowledge the pause. - * - * XXX - should the interrupts be left on while doing this? - */ - PAUSE_SEQUENCER(p); - - /* - * Save the SCB pointer and put our own pointer in - this - * selects one of the four banks of SCB registers. Load - * the SCB, then write its pointer into the queue in FIFO - * and restore the saved SCB pointer. - */ - old_scbptr = inb(O_SCBPTR(p->base)); - outb(empty, O_SCBPTR(p->base)); - - aha274x_putscb(p->base, &scb); - - outb(empty, O_QINFIFO(p->base)); - outb(old_scbptr, O_SCBPTR(p->base)); - - /* - * Make sure the Scsi_Cmnd pointer is saved, the struct it - * points to is set up properly, and the parity error flag - * is reset, then unpause the sequencer and watch the fun - * begin. - */ - cmd->scsi_done = fn; - p->SCB_array[empty] = cmd; - aha274x_parity(cmd) = DID_OK; - - UNPAUSE_SEQUENCER(p); - - restore_flags(flags); - return(0); -} - -/* return values from aha274x_kill */ - -enum k_state { - k_ok, /* scb found and message sent */ - k_busy, /* message already present */ - k_absent, /* couldn't locate scb */ - k_disconnect, /* scb found, but disconnected */ -}; - -/* - * This must be called with interrupts disabled - it's going to - * be messing around with the host data, and an interrupt being - * fielded in the middle could get ugly. - * - * Since so much of the abort and reset code is shared, this - * function performs more magic than it really should. If the - * command completes ok, then it will call scsi_done with the - * result code passed in. The unpause parameter controls whether - * or not the sequencer gets unpaused - the reset function, for - * instance, may want to do something more aggressive. - * - * Note that the command is checked for in our SCB_array first - * before the sequencer is paused, so if k_absent is returned, - * then the sequencer is NOT paused. - */ - -static -enum k_state aha274x_kill(Scsi_Cmnd *cmd, unsigned char message, - unsigned int result, int unpause) -{ - struct aha274x_host *p; - int i, scb, found, queued; - unsigned char scbsave[AHA274X_MAXSCB]; - - p = (struct aha274x_host *)cmd->host->hostdata; - - /* - * If we can't find the command, assume it just completed - * and shrug it away. - */ - for (scb = 0; scb < AHA274X_MAXSCB; scb++) - if (p->SCB_array[scb] == cmd) - break; - - if (scb == AHA274X_MAXSCB) - return(k_absent); - - PAUSE_SEQUENCER(p); - - /* - * This is the best case, really. Check to see if the - * command is still in the sequencer's input queue. If - * so, simply remove it. Reload the queue afterward. - */ - queued = inb(O_QINCNT(p->base)); - - for (i = found = 0; i < queued; i++) { - scbsave[i] = inb(O_QINFIFO(p->base)); - - if (scbsave[i] == scb) { - found = 1; - i -= 1; - } - } - - queued -= found; - for (i = 0; i < queued; i++) - outb(scbsave[i], O_QINFIFO(p->base)); - - if (found) - goto complete; - - /* - * Check the current SCB bank. If it's not the one belonging - * to the command we want to kill, assume that the command - * is disconnected. It's rather a pain to force a reconnect - * and send a message to the target, so we abdicate responsibility - * in this case. - */ - if (inb(O_SCBPTR(p->base)) != scb) { - if (unpause) - UNPAUSE_SEQUENCER(p); - return(k_disconnect); - } - - /* - * Presumably at this point our target command is active. Check - * to see if there's a message already in effect. If not, place - * our message in and assert ATN so the target goes into MESSAGE - * OUT phase. - */ - if (inb(HA_MSG_FLAGS(p->base)) & 0x80) { - if (unpause) - UNPAUSE_SEQUENCER(p); - return(k_busy); - } - - outb(0x80, HA_MSG_FLAGS(p->base)); /* active message */ - outb(1, HA_MSG_LEN(p->base)); /* length = 1 */ - outb(message, HA_MSG_START(p->base)); /* message body */ - - /* - * Assert ATN. Use the value of SCSISIGO saved by the - * sequencer code so we don't alter its contents radically - * in the middle of something critical. - */ - outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base)); - - /* - * The command has been killed. Do the bookkeeping, unpause - * the sequencer, and notify the higher-level SCSI code. - */ -complete: - p->SCB_array[scb] = NULL; - if (unpause) - UNPAUSE_SEQUENCER(p); - - cmd->result = result << 16; - cmd->scsi_done(cmd); - return(k_ok); -} - -int aha274x_abort(Scsi_Cmnd *cmd) -{ - int rv; - long flags; - - save_flags(flags); - cli(); - - switch (aha274x_kill(cmd, ABORT, DID_ABORT, !0)) { - case k_ok: rv = SCSI_ABORT_SUCCESS; break; - case k_busy: rv = SCSI_ABORT_BUSY; break; - case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break; - case k_disconnect: rv = SCSI_ABORT_SNOOZE; break; - default: - panic("aha274x_do_abort: internal error\n"); - } - - restore_flags(flags); - return(rv); -} - -/* - * Resetting the bus always succeeds - is has to, otherwise the - * kernel will panic! Try a surgical technique - sending a BUS - * DEVICE RESET message - on the offending target before pulling - * the SCSI bus reset line. - */ - -int aha274x_reset(Scsi_Cmnd *cmd) -{ - int i; - long flags; - Scsi_Cmnd *reset; - struct aha274x_host *p; - - p = (struct aha274x_host *)cmd->host->hostdata; - save_flags(flags); - cli(); - - switch (aha274x_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) { - - case k_ok: - /* - * The RESET message was sent to the target - * with no problems. Flag that target as - * needing a SDTR negotiation on the next - * connection and restart the sequencer. - */ - outb((1 << cmd->target), HA_NEEDSDTR(p->base)); - UNPAUSE_SEQUENCER(p); - break; - - case k_absent: - /* - * The sequencer will not be paused if aha274x_kill() - * couldn't find the command. - */ - PAUSE_SEQUENCER(p); - /* falls through */ - - case k_busy: - case k_disconnect: - /* - * Do a hard reset of the SCSI bus. According to the - * SCSI-2 draft specification, reset has to be asserted - * for at least 25us. I'm invoking the kernel delay - * function for 30us since I'm not totally trusting of - * the busy loop timing. - * - * XXX - I'm not convinced this works. I tried resetting - * the bus before, trying to get the devices on the - * bus to revert to asynchronous transfer, and it - * never seemed to work. - */ - debug("aha274x: attempting to reset scsi bus and card\n"); - - outb(1, O_SCSISEQ(p->base)); /* SCSIRSTO */ - udelay(30); - outb(0, O_SCSISEQ(p->base)); /* !SCSIRSTO */ - - outb(0xff, HA_NEEDSDTR(p->base)); - UNPAUSE_SEQUENCER(p); - - /* - * Locate the command and return a "reset" status - * for it. This is not completely correct and will - * probably return to haunt me later. - */ - for (i = 0; i < AHA274X_MAXSCB; i++) { - if (cmd == p->SCB_array[i]) { - reset = (Scsi_Cmnd *)p->SCB_array[i]; - p->SCB_array[i] = NULL; - reset->result = DID_RESET << 16; - reset->scsi_done(reset); - break; - } - } - break; - - default: - panic("aha274x_reset: internal error\n"); - } - - restore_flags(flags); - return(SCSI_RESET_SUCCESS); -} - -int aha274x_biosparam(Disk *disk, int devno, int geom[]) -{ - int heads, sectors, cylinders; - - /* - * XXX - if I could portably find the card's configuration - * information, then this could be autodetected instead - * of left to a boot-time switch. - */ - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); - - if (aha274x_extended && cylinders > 1024) { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (255 * 63); - } - - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - - return(0); -} - diff -urN lx-1.2.8/drivers/scsi/aha274x.h linux/drivers/scsi/aha274x.h --- lx-1.2.8/drivers/scsi/aha274x.h Tue Nov 29 03:07:14 1994 +++ linux/drivers/scsi/aha274x.h Fri May 12 10:04:53 1995 @@ -1,63 +0,0 @@ -/* @(#)aha274x.h 1.11 94/09/06 jda */ - -/* - * Adaptec 274x device driver for Linux. - * Copyright (c) 1994 The University of Calgary Department of Computer Science. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef aha274x_h -#define aha274x_h - -#define AHA274X_MAXSCB 4 -#define AHA274X_H_VERSION "1.11" - -/* - * Scsi_Host_Template (see hosts.h) for 274x - some fields - * to do with card config are filled in after the card is - * detected. - */ -#define AHA274X { \ - NULL, \ - NULL, \ - NULL, \ - aha274x_detect, \ - NULL, \ - aha274x_info, \ - aha274x_command, \ - aha274x_queue, \ - aha274x_abort, \ - aha274x_reset, \ - NULL, \ - aha274x_biosparam, \ - AHA274X_MAXSCB, /* max simultaneous cmds */\ - -1, /* scsi id of host adapter */\ - SG_ALL, /* max scatter-gather cmds */\ - 1, /* cmds per lun (linked cmds) */\ - 0, /* number of 274x's present */\ - 0, /* no memory DMA restrictions */\ - DISABLE_CLUSTERING \ -} - -extern int aha274x_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); -extern int aha274x_biosparam(Disk *, int, int[]); -extern int aha274x_detect(Scsi_Host_Template *); -extern int aha274x_command(Scsi_Cmnd *); -extern int aha274x_abort(Scsi_Cmnd *); -extern int aha274x_reset(Scsi_Cmnd *); -extern const char *aha274x_info(struct Scsi_Host *); - -#endif diff -urN lx-1.2.8/drivers/scsi/aha274x.seq linux/drivers/scsi/aha274x.seq --- lx-1.2.8/drivers/scsi/aha274x.seq Sun Nov 20 14:50:47 1994 +++ linux/drivers/scsi/aha274x.seq Fri May 12 10:04:53 1995 @@ -1,1021 +0,0 @@ -# @(#)aha274x.seq 1.28 94/10/04 jda -# -# Adaptec 274x device driver for Linux. -# Copyright (c) 1994 The University of Calgary Department of Computer Science. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -VERSION AHA274X_SEQ_VERSION 1.28 - -MAXSCB = 4 - -SCSISEQ = 0x00 -SXFRCTL0 = 0x01 -SXFRCTL1 = 0x02 -SCSISIGI = 0x03 -SCSISIGO = 0x03 -SCSIRATE = 0x04 -SCSIID = 0x05 -SCSIDATL = 0x06 -STCNT = 0x08 -STCNT+0 = 0x08 -STCNT+1 = 0x09 -STCNT+2 = 0x0a -SSTAT0 = 0x0b -CLRSINT1 = 0x0c -SSTAT1 = 0x0c -SIMODE1 = 0x11 -SCSIBUSL = 0x12 -SHADDR = 0x14 -SELID = 0x19 -SBLKCTL = 0x1f -SEQCTL = 0x60 -A = 0x64 # == ACCUM -SINDEX = 0x65 -DINDEX = 0x66 -ALLZEROS = 0x6a -NONE = 0x6a -SINDIR = 0x6c -DINDIR = 0x6d -FUNCTION1 = 0x6e -HADDR = 0x88 -HCNT = 0x8c -HCNT+0 = 0x8c -HCNT+1 = 0x8d -HCNT+2 = 0x8e -SCBPTR = 0x90 -INTSTAT = 0x91 -DFCNTRL = 0x93 -DFSTATUS = 0x94 -DFDAT = 0x99 -QINFIFO = 0x9b -QINCNT = 0x9c -QOUTFIFO = 0x9d - -SCSICONF = 0x5a - -# The two reserved bytes at SCBARRAY+1[23] are expected to be set to -# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag -# to indicate whether or not to reload scatter-gather parameters after -# a disconnect. -# -SCBARRAY+0 = 0xa0 -SCBARRAY+1 = 0xa1 -SCBARRAY+2 = 0xa2 -SCBARRAY+3 = 0xa3 -SCBARRAY+7 = 0xa7 -SCBARRAY+11 = 0xab -SCBARRAY+14 = 0xae -SCBARRAY+15 = 0xaf -SCBARRAY+16 = 0xb0 -SCBARRAY+17 = 0xb1 -SCBARRAY+18 = 0xb2 -SCBARRAY+19 = 0xb3 -SCBARRAY+20 = 0xb4 -SCBARRAY+21 = 0xb5 -SCBARRAY+22 = 0xb6 -SCBARRAY+23 = 0xb7 -SCBARRAY+24 = 0xb8 -SCBARRAY+25 = 0xb9 - -SIGNAL_0 = 0x01 # unknown scsi bus phase -SIGNAL_1 = 0x11 # message reject -SIGNAL_2 = 0x21 # no IDENTIFY after reconnect -SIGNAL_3 = 0x31 # no cmd match for reconnect -SIGNAL_4 = 0x41 # SDTR -> SCSIRATE conversion - -# The host adapter card (at least the BIOS) uses 20-2f for SCSI -# device information, 32-33 and 5a-5f as well. Since we don't support -# wide or twin-bus SCSI, 28-2f can be reclaimed. As it turns out, the -# BIOS trashes 20-27 anyway, writing the synchronous negotiation results -# on top of the BIOS values, so we re-use those for our per-target -# scratchspace (actually a value that can be copied directly into -# SCSIRATE). This implies, since we can't get the BIOS config values, -# that all targets will be negotiated with for synchronous transfer. -# NEEDSDTR has one bit per target indicating if an SDTR message is -# needed for that device - this will be set initially, as well as -# after a bus reset condition. -# -# The high bit of DROPATN is set if ATN should be dropped before the ACK -# when outb is called. REJBYTE contains the first byte of a MESSAGE IN -# message, so the driver can report an intelligible error if a message is -# rejected. -# -# RESELECT's high bit is true if we are currently handling a reselect; -# its next-highest bit is true ONLY IF we've seen an IDENTIFY message -# from the reselecting target. If we haven't had IDENTIFY, then we have -# no idea what the lun is, and we can't select the right SCB register -# bank, so force a kernel panic if the target attempts a data in/out or -# command phase instead of corrupting something. -# -# Note that SG_NEXT occupies four bytes. -# -SYNCNEG = 0x20 -DISC_DSB_A = 0x32 - -DROPATN = 0x30 -REJBYTE = 0x31 -RESELECT = 0x34 - -MSG_FLAGS = 0x35 -MSG_LEN = 0x36 -MSG_START+0 = 0x37 -MSG_START+1 = 0x38 -MSG_START+2 = 0x39 -MSG_START+3 = 0x3a -MSG_START+4 = 0x3b -MSG_START+5 = 0x3c --MSG_START+0 = 0xc9 # 2's complement of MSG_START+0 - -ARG_1 = 0x4c # sdtr conversion args & return -ARG_2 = 0x4d -RETURN_1 = 0x4c - -SIGSTATE = 0x4e # value written to SCSISIGO -NEEDSDTR = 0x4f # send SDTR message, 1 bit/trgt - -SG_SIZEOF = 12 # sizeof(struct scatterlist) -SG_NOLOAD = 0x50 # load SG pointer/length? -SG_COUNT = 0x51 # working value of SG count -SG_NEXT = 0x52 # working value of SG pointer -SG_NEXT+0 = 0x52 -SG_NEXT+1 = 0x53 -SG_NEXT+2 = 0x54 -SG_NEXT+3 = 0x55 - -# Poll QINCNT for work - the lower three bits contain -# the number of entries in the Queue In FIFO. -# -start: - test SCSISIGI,0x4 jnz reselect # BSYI - test QINCNT,0x7 jz start - -# We have at least one queued SCB now. Set the SCB pointer -# from the FIFO so we see the right bank of SCB registers, -# then set SCSI options and set the initiator and target -# SCSI IDs. -# - mov SCBPTR,QINFIFO - mov SCBARRAY+1 call initialize - clr SG_NOLOAD - clr RESELECT - -# As soon as we get a successful selection, the target should go -# into the message out phase since we have ATN asserted. Prepare -# the message to send, locking out the device driver. If the device -# driver hasn't beaten us with an ABORT or RESET message, then tack -# on a SDTR negotiation if required. -# -# Messages are stored in scratch RAM starting with a flag byte (high bit -# set means active message), one length byte, and then the message itself. -# - mov SCBARRAY+1 call disconnect # disconnect ok? - - and SINDEX,0x7,SCBARRAY+1 # lun - or SINDEX,A # return value from disconnect - or SINDEX,0x80 call mk_mesg # IDENTIFY message - - mov A,SINDEX - cmp MSG_START+0,A jne !message # did driver beat us? - mvi MSG_START+1 call mk_sdtr # build SDTR message if needed - -!message: - -# Enable selection phase as an initiator, and do automatic ATN -# after the selection. -# - mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO - -# Wait for successful arbitration. The AIC-7770 documentation says -# that SELINGO indicates successful arbitration, and that it should -# be used to look for SELDO. However, if the sequencer is paused at -# just the right time - a parallel fsck(8) on two drives did it for -# me - then SELINGO can flip back to false before we've seen it. This -# makes the sequencer sit in the arbitration loop forever. This is -# Not Good. -# -# Therefore, I've added a check in the arbitration loop for SELDO -# too. This could arguably be made a critical section by disabling -# pauses, but I don't want to make a potentially infinite loop a CS. -# I suppose you could fold it into the select loop, too, but since -# I've been hunting this bug for four days it's kinda like a trophy. -# -arbitrate: - test SSTAT0,0x40 jnz *select # SELDO - test SSTAT0,0x10 jz arbitrate # SELINGO - -# Wait for a successful selection. If the hardware selection -# timer goes off, then the driver gets the interrupt, so we don't -# need to worry about it. -# -select: - test SSTAT0,0x40 jz select # SELDO - jmp *select - -# Reselection is being initiated by a target - we've seen the BSY -# line driven active, and we didn't do it! Enable the reselection -# hardware, and wait for it to finish. Make a note that we've been -# reselected, but haven't seen an IDENTIFY message from the target -# yet. -# -reselect: - mvi SCSISEQ,0x10 # ENRSELI - -reselect1: - test SSTAT0,0x20 jz reselect1 # SELDI - mov SELID call initialize - - mvi RESELECT,0x80 # reselected, no IDENTIFY - -# After the [re]selection, make sure that the [re]selection enable -# bit is off. This chip is flaky enough without extra things -# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be -# using it shortly. -# -*select: - clr SCSISEQ - mvi CLRSINT1,0x8 # CLRBUSFREE - -# Main loop for information transfer phases. If BSY is false, then -# we have a bus free condition, expected or not. Otherwise, wait -# for the target to assert REQ before checking MSG, C/D and I/O -# for the bus phase. -# -# We can't simply look at the values of SCSISIGI here (if we want -# to do synchronous data transfer), because the target won't assert -# REQ if it's already sent us some data that we haven't acknowledged -# yet. -# -ITloop: - test SSTAT1,0x8 jnz p_busfree # BUSFREE - test SSTAT1,0x1 jz ITloop # REQINIT - - and A,0xe0,SCSISIGI # CDI|IOI|MSGI - - cmp ALLZEROS,A je p_dataout - cmp A,0x40 je p_datain - cmp A,0x80 je p_command - cmp A,0xc0 je p_status - cmp A,0xa0 je p_mesgout - cmp A,0xe0 je p_mesgin - - mvi INTSTAT,SIGNAL_0 # unknown - signal driver - -p_dataout: - mvi 0 call scsisig # !CDO|!IOO|!MSGO - call assert - call sg_load - - mvi A,3 - mvi DINDEX,HCNT - mvi SCBARRAY+23 call bcopy - - mvi A,3 - mvi DINDEX,STCNT - mvi SCBARRAY+23 call bcopy - - mvi A,4 - mvi DINDEX,HADDR - mvi SCBARRAY+19 call bcopy - - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - -# After a DMA finishes, save the final transfer pointer and count -# back into the SCB, in case a device disconnects in the middle of -# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since -# it's a reflection of how many bytes were transferred on the SCSI -# (as opposed to the host) bus. -# - mvi A,3 - mvi DINDEX,SCBARRAY+23 - mvi STCNT call bcopy - - mvi A,4 - mvi DINDEX,SCBARRAY+19 - mvi SHADDR call bcopy - - call sg_advance - mov SCBARRAY+18,SG_COUNT # residual S/G count - - jmp ITloop - -p_datain: - mvi 0x40 call scsisig # !CDO|IOO|!MSGO - call assert - call sg_load - - mvi A,3 - mvi DINDEX,HCNT - mvi SCBARRAY+23 call bcopy - - mvi A,3 - mvi DINDEX,STCNT - mvi SCBARRAY+23 call bcopy - - mvi A,4 - mvi DINDEX,HADDR - mvi SCBARRAY+19 call bcopy - - mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN| - # !DIRECTION|FIFORESET - mvi A,3 - mvi DINDEX,SCBARRAY+23 - mvi STCNT call bcopy - - mvi A,4 - mvi DINDEX,SCBARRAY+19 - mvi SHADDR call bcopy - - call sg_advance - mov SCBARRAY+18,SG_COUNT # residual S/G count - - jmp ITloop - -# Command phase. Set up the DMA registers and let 'er rip - the -# two bytes after the SCB SCSI_cmd_length are zeroed by the driver, -# so we can copy those three bytes directly into HCNT. -# -p_command: - mvi 0x80 call scsisig # CDO|!IOO|!MSGO - call assert - - mvi A,3 - mvi DINDEX,HCNT - mvi SCBARRAY+11 call bcopy - - mvi A,3 - mvi DINDEX,STCNT - mvi SCBARRAY+11 call bcopy - - mvi A,4 - mvi DINDEX,HADDR - mvi SCBARRAY+7 call bcopy - - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - jmp ITloop - -# Status phase. Wait for the data byte to appear, then read it -# and store it into the SCB. -# -p_status: - mvi 0xc0 call scsisig # CDO|IOO|!MSGO - - mvi SCBARRAY+14 call inb - jmp ITloop - -# Message out phase. If there is no active message, but the target -# took us into this phase anyway, build a no-op message and send it. -# -p_mesgout: - mvi 0xa0 call scsisig # CDO|!IOO|MSGO - mvi 0x8 call mk_mesg # build NOP message - -# Set up automatic PIO transfer from MSG_START. Bit 3 in -# SXFRCTL0 (SPIOEN) is already on. -# - mvi SINDEX,MSG_START+0 - mov DINDEX,MSG_LEN - clr A - -# When target asks for a byte, drop ATN if it's the last one in -# the message. Otherwise, keep going until the message is exhausted. -# (We can't use outb for this since it wants the input in SINDEX.) -# -# Keep an eye out for a phase change, in case the target issues -# a MESSAGE REJECT. -# -p_mesgout2: - test SSTAT0,0x2 jz p_mesgout2 # SPIORDY - test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS - - cmp DINDEX,1 jne p_mesgout3 # last byte? - mvi CLRSINT1,0x40 # CLRATNO - drop ATN - -# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically -# send ACKs in automatic PIO or DMA mode unless you make sure that the -# "expected" bus phase in SCSISIGO matches the actual bus phase. This -# behaviour is completely undocumented and caused me several days of -# grief. -# -# After plugging in different drives to test with and using a longer -# SCSI cable, I found that I/O in Automatic PIO mode ceased to function, -# especially when transferring >1 byte. It seems to be much more stable -# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is -# polled for transfer completion - for both output _and_ input. The -# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL -# is accessed (like the documentation says it does), and that on a longer -# cable run, the sequencer code was fast enough to loop back and see -# an SPIORDY that hadn't dropped yet. -# -p_mesgout3: - call one_stcnt - mov SCSIDATL,SINDIR - -p_mesgout4: - test SSTAT0,0x4 jz p_mesgout4 # SDONE - dec DINDEX - inc A - cmp MSG_LEN,A jne p_mesgout2 - -# If the next bus phase after ATN drops is a message out, it means -# that the target is requesting that the last message(s) be resent. -# -p_mesgout5: - test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE - test SSTAT1,0x1 jz p_mesgout5 # REQINIT - - and A,0xe0,SCSISIGI # CDI|IOI|MSGI - cmp A,0xa0 jne p_mesgout6 - mvi 0x10 call scsisig # ATNO - re-assert ATN - - jmp ITloop - -p_mesgout6: - mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS - clr MSG_FLAGS # no active msg - jmp ITloop - -# Message in phase. Bytes are read using Automatic PIO mode, but not -# using inb. This alleviates a race condition, namely that if ATN had -# to be asserted under Automatic PIO mode, it had to beat the SCSI -# circuitry sending an ACK to the target. This showed up under heavy -# loads and really confused things, since ABORT commands wouldn't be -# seen by the drive after an IDENTIFY message in until it had changed -# to a data I/O phase. -# -p_mesgin: - mvi 0xe0 call scsisig # CDO|IOO|MSGO - mvi A call inb_first # read the 1st message byte - mvi REJBYTE,A # save it for the driver - - cmp ALLZEROS,A jne p_mesgin1 - -# We got a "command complete" message, so put the SCB pointer -# into the Queue Out, and trigger a completion interrupt. -# - mov QOUTFIFO,SCBPTR - mvi INTSTAT,0x2 # CMDCMPLT - jmp p_mesgin_done - -# Is it an extended message? We only support the synchronous data -# transfer request message, which will probably be in response to -# an SDTR message out from us. If it's not an SDTR, reject it - -# apparently this can be done after any message in byte, according -# to the SCSI-2 spec. -# -# XXX - we should really reject this if we didn't initiate the SDTR -# negotiation; this may cause problems with unusual devices. -# -p_mesgin1: - cmp A,1 jne p_mesgin2 # extended message code? - - mvi A call inb_next - cmp A,3 jne p_mesginN # extended mesg length = 3 - mvi A call inb_next - cmp A,1 jne p_mesginN # SDTR code - - mvi ARG_1 call inb_next # xfer period - mvi ARG_2 call inb_next # REQ/ACK offset - mvi INTSTAT,SIGNAL_4 # call driver to convert - - call ndx_sdtr # index sync config for target - mov DINDEX,SINDEX - mov DINDIR,RETURN_1 # save returned value - - not A # turn off "need sdtr" flag - and NEEDSDTR,A - -# Even though the SCSI-2 specification says that a device responding -# to our SDTR message should honor our parameters for transmitting -# to us, it doesn't seem to work too well in real life. In particular, -# a lot of CD-ROM and tape units don't function: try using the SDTR -# parameters the device sent us for both transmitting and receiving. -# - mov SCSIRATE,RETURN_1 - jmp p_mesgin_done - -# Is it a disconnect message? Set a flag in the SCB to remind us -# and await the bus going free. -# -p_mesgin2: - cmp A,4 jne p_mesgin3 # disconnect code? - - or SCBARRAY+0,0x4 # set "disconnected" bit - jmp p_mesgin_done - -# Save data pointers message? Copy working values into the SCB, -# usually in preparation for a disconnect. -# -p_mesgin3: - cmp A,2 jne p_mesgin4 # save data pointers code? - - call sg_ram2scb - jmp p_mesgin_done - -# Restore pointers message? Data pointers are recopied from the -# SCB anyway at the start of any DMA operation, so the only thing -# to copy is the scatter-gather values. -# -p_mesgin4: - cmp A,3 jne p_mesgin5 # restore pointers code? - - call sg_scb2ram - jmp p_mesgin_done - -# Identify message? For a reconnecting target, this tells us the lun -# that the reconnection is for - find the correct SCB and switch to it, -# clearing the "disconnected" bit so we don't "find" it by accident later. -# -p_mesgin5: - test A,0x80 jz p_mesgin6 # identify message? - - test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved - - mov A call findSCB # switch to correct SCB - -# If a active message is present after calling findSCB, then either it -# or the driver is trying to abort the command. Either way, something -# untoward has happened and we should just leave it alone. -# - test MSG_FLAGS,0x80 jnz p_mesgin_done - - xor SCBARRAY+0,0x4 # clear disconnect bit in SCB - mvi RESELECT,0xc0 # make note of IDENTIFY - - call sg_scb2ram # implied restore pointers - # required on reselect - jmp p_mesgin_done - -# Message reject? If we have an outstanding SDTR negotiation, assume -# that it's a response from the target selecting asynchronous transfer, -# otherwise just ignore it since we have no clue what it pertains to. -# -# XXX - I don't have a device that responds this way. Does this code -# actually work? -# -p_mesgin6: - cmp A,7 jne p_mesgin7 # message reject code? - - and FUNCTION1,0x70,SCSIID # outstanding SDTR message? - mov A,FUNCTION1 - test NEEDSDTR,A jz p_mesgin_done # no - ignore rejection - - call ndx_sdtr # note use of asynch xfer - mov DINDEX,SINDEX - clr DINDIR - - not A # turn off "active sdtr" flag - and NEEDSDTR,A - - clr SCSIRATE # select asynch xfer - jmp p_mesgin_done - -# [ ADD MORE MESSAGE HANDLING HERE ] -# -p_mesgin7: - -# We have no idea what this message in is, and there's no way -# to pass it up to the kernel, so we issue a message reject and -# hope for the best. Since we're now using manual PIO mode to -# read in the message, there should no longer be a race condition -# present when we assert ATN. In any case, rejection should be a -# rare occurrence - signal the driver when it happens. -# -p_mesginN: - or SINDEX,0x10,SIGSTATE # turn on ATNO - call scsisig - mvi INTSTAT,SIGNAL_1 # let driver know - - mvi 0x7 call mk_mesg # MESSAGE REJECT message - -p_mesgin_done: - call inb_last # ack & turn auto PIO back on - jmp ITloop - -# Bus free phase. It might be useful to interrupt the device -# driver if we aren't expecting this. For now, make sure that -# ATN isn't being asserted and look for a new command. -# -p_busfree: - mvi CLRSINT1,0x40 # CLRATNO - clr SIGSTATE - jmp start - -# Bcopy: number of bytes to transfer should be in A, DINDEX should -# contain the destination address, and SINDEX should contain the -# source address. All input parameters are trashed on return. -# -bcopy: - mov DINDIR,SINDIR - dec A - cmp ALLZEROS,A jne bcopy - ret - -# Locking the driver out, build a one-byte message passed in SINDEX -# if there is no active message already. SINDEX is returned intact. -# -mk_mesg: - mvi SEQCTL,0x40 # PAUSEDIS - test MSG_FLAGS,0x80 jnz mk_mesg1 # active message? - - mvi MSG_FLAGS,0x80 # if not, there is now - mvi MSG_LEN,1 # length = 1 - mov MSG_START+0,SINDEX # 1-byte message - -mk_mesg1: - clr SEQCTL # !PAUSEDIS - ret - -# Input byte in Automatic PIO mode. The address to store the byte -# in should be in SINDEX. DINDEX will be used by this routine. -# -inb: - test SSTAT0,0x2 jz inb # SPIORDY - mov DINDEX,SINDEX - call one_stcnt # xfer one byte - mov DINDIR,SCSIDATL -inb1: - test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish" - ret - -# Carefully read data in Automatic PIO mode. I first tried this using -# Manual PIO mode, but it gave me continual underrun errors, probably -# indicating that I did something wrong, but I feel more secure leaving -# Automatic PIO on all the time. -# -# According to Adaptec's documentation, an ACK is not sent on input from -# the target until SCSIDATL is read from. So we wait until SCSIDATL is -# latched (the usual way), then read the data byte directly off the bus -# using SCSIBUSL. When we have pulled the ATN line, or we just want to -# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI -# spec guarantees that the target will hold the data byte on the bus until -# we send our ACK. -# -# The assumption here is that these are called in a particular sequence, -# and that REQ is already set when inb_first is called. inb_{first,next} -# use the same calling convention as inb. -# -inb_first: - mov DINDEX,SINDEX - mov DINDIR,SCSIBUSL ret # read byte directly from bus - -inb_next: - mov DINDEX,SINDEX # save SINDEX - - call one_stcnt # xfer one byte - mov NONE,SCSIDATL # dummy read from latch to ACK -inb_next1: - test SSTAT0,0x4 jz inb_next1 # SDONE -inb_next2: - test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte - mov DINDIR,SCSIBUSL ret # read byte directly from bus - -inb_last: - call one_stcnt # ACK with dummy read - mov NONE,SCSIDATL -inb_last1: - test SSTAT0,0x4 jz inb_last1 # wait for completion - ret - -# Output byte in Automatic PIO mode. The byte to output should be -# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped -# before the byte is output. -# -outb: - test SSTAT0,0x2 jz outb # SPIORDY - call one_stcnt # xfer one byte - - test DROPATN,0x80 jz outb1 - mvi CLRSINT1,0x40 # CLRATNO - clr DROPATN -outb1: - mov SCSIDATL,SINDEX -outb2: - test SSTAT0,0x4 jz outb2 # SDONE - ret - -# Write the value "1" into the STCNT registers, for Automatic PIO -# transfers. -# -one_stcnt: - clr STCNT+2 - clr STCNT+1 - mvi STCNT+0,1 ret - -# DMA data transfer. HADDR and HCNT must be loaded first, and -# SINDEX should contain the value to load DFCNTRL with - 0x3d for -# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared -# during initialization. -# -dma: - mov DFCNTRL,SINDEX -dma1: -dma2: - test SSTAT0,0x1 jnz dma3 # DMADONE - test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun - -# We will be "done" DMAing when the transfer count goes to zero, or -# the target changes the phase (in light of this, it makes sense that -# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are -# doing a SCSI->Host transfer, flush the data FIFO. -# -dma3: - test SINDEX,0x4 jnz dma5 # DIRECTION - and SINDEX,0xfe # mask out FIFORESET - or DFCNTRL,0x2,SINDEX # FIFOFLUSH -dma4: - test DFCNTRL,0x2 jnz dma4 # FIFOFLUSHACK - -# Now shut the DMA enables off, and copy STCNT (ie. the underrun -# amount, if any) to the SCB registers; SG_COUNT will get copied to -# the SCB's residual S/G count field after sg_advance is called. Make -# sure that the DMA enables are actually off first lest we get an ILLSADDR. -# -dma5: - clr DFCNTRL # disable DMA -dma6: - test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK - - mvi A,3 - mvi DINDEX,SCBARRAY+15 - mvi STCNT call bcopy - - ret - -# Common SCSI initialization for selection and reselection. Expects -# the target SCSI ID to be in the upper four bits of SINDEX, and A's -# contents are stomped on return. -# -initialize: - clr SBLKCTL # channel A, !wide - and SCSIID,0xf0,SINDEX # target ID - and A,0x7,SCSICONF # SCSI_ID_A[210] - or SCSIID,A - -# Esundry initialization. -# - clr DROPATN - clr SIGSTATE - -# Turn on Automatic PIO mode now, before we expect to see an REQ -# from the target. It shouldn't hurt anything to leave it on. Set -# CLRCHN here before the target has entered a data transfer mode - -# with synchronous SCSI, if you do it later, you blow away some -# data in the SCSI FIFO that the target has already sent to you. -# - mvi SXFRCTL0,0xa # SPIOEN|CLRCHN - -# Set SCSI bus parity checking and the selection timeout value, -# and enable the hardware selection timer. Set the SELTO interrupt -# to signal the driver. -# - and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10] - or SXFRCTL1,0x4,A # ENSTIMER - mvi SIMODE1,0x84 # ENSELTIMO|ENSCSIPERR - -# Initialize scatter-gather pointers by setting up the working copy -# in scratch RAM. -# - call sg_scb2ram - -# Initialize SCSIRATE with the appropriate value for this target. -# - call ndx_sdtr - mov SCSIRATE,SINDIR - ret - -# Assert that if we've been reselected, then we've seen an IDENTIFY -# message. -# -assert: - test RESELECT,0x80 jz assert1 # reselected? - test RESELECT,0x40 jnz assert1 # seen IDENTIFY? - - mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic - -assert1: - ret - -# Find out if disconnection is ok from the information the BIOS has left -# us. The target ID should be in the upper four bits of SINDEX; A will -# contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok) -# on exit. -# -# This is the only place the target ID is limited to three bits, so we -# can use the FUNCTION1 register. -# -disconnect: - and FUNCTION1,0x70,SINDEX # strip off extra just in case - mov A,FUNCTION1 - test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled - - clr A ret -disconnect1: - mvi A,0x40 ret - -# Locate the SCB matching the target ID in SELID and the lun in the lower -# three bits of SINDEX, and switch the SCB to it. Have the kernel print -# a warning message if it can't be found - this seems to happen occasionally -# under high loads. Also, if not found, generate an ABORT message to the -# target. -# -findSCB: - and A,0x7,SINDEX # lun in lower three bits - or A,A,SELID # can I do this? - and A,0xf7 # only channel A implemented - - clr SINDEX - -findSCB1: - mov SCBPTR,SINDEX # switch to new SCB - cmp SCBARRAY+1,A jne findSCB2 # target ID/channel/lun match? - test SCBARRAY+0,0x4 jz findSCB2 # should be disconnected - - ret - -findSCB2: - inc SINDEX - cmp SINDEX,MAXSCB jne findSCB1 - - mvi INTSTAT,SIGNAL_3 # not found - signal kernel - mvi 0x6 call mk_mesg # ABORT message - - or SINDEX,0x10,SIGSTATE # assert ATNO - call scsisig - ret - -# Make a working copy of the scatter-gather parameters in the SCB. -# -sg_scb2ram: - mov SG_COUNT,SCBARRAY+2 - - mvi A,4 - mvi DINDEX,SG_NEXT - mvi SCBARRAY+3 call bcopy - - mvi SG_NOLOAD,0x80 - test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g? - clr SG_NOLOAD - -sg_scb2ram1: - ret - -# Copying RAM values back to SCB, for Save Data Pointers message. -# -sg_ram2scb: - mov SCBARRAY+2,SG_COUNT - - mvi A,4 - mvi DINDEX,SCBARRAY+3 - mvi SG_NEXT call bcopy - - and SCBARRAY+0,0xef,SCBARRAY+0 - test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g? - or SCBARRAY+0,0x10 - -sg_ram2scb1: - ret - -# Load a struct scatter if needed and set up the data address and -# length. If the working value of the SG count is nonzero, then -# we need to load a new set of values. -# -# This, like the above DMA, assumes a little-endian host data storage. -# -sg_load: - test SG_COUNT,0xff jz sg_load3 # SG being used? - test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g? - - clr HCNT+2 - clr HCNT+1 - mvi HCNT+0,SG_SIZEOF - - mvi A,4 - mvi DINDEX,HADDR - mvi SG_NEXT call bcopy - - mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET - -# Wait for DMA from host memory to data FIFO to complete, then disable -# DMA and wait for it to acknowledge that it's off. -# -sg_load1: - test DFSTATUS,0x8 jz sg_load1 # HDONE - - clr DFCNTRL # disable DMA -sg_load2: - test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK - -# Copy data from FIFO into SCB data pointer and data count. This assumes -# that the struct scatterlist has this structure (this and sizeof(struct -# scatterlist) == 12 are asserted in aha274x.c): -# -# struct scatterlist { -# char *address; /* four bytes, little-endian order */ -# ... /* four bytes, ignored */ -# unsigned short length; /* two bytes, little-endian order */ -# } -# - mov SCBARRAY+19,DFDAT # new data address - mov SCBARRAY+20,DFDAT - mov SCBARRAY+21,DFDAT - mov SCBARRAY+22,DFDAT - - mov NONE,DFDAT # throw away four bytes - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT - - mov SCBARRAY+23,DFDAT - mov SCBARRAY+24,DFDAT - clr SCBARRAY+25 - -sg_load3: - ret - -# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled, -# and the SCSI transfer count is zero (note that this should be called -# right after a DMA finishes), then move the working copies of the SG -# pointer/length along. If the SCSI transfer count is not zero, then -# presumably the target is disconnecting - do not reload the SG values -# next time. -# -sg_advance: - test SG_COUNT,0xff jz sg_advance2 # s/g enabled? - - test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero? - test STCNT+1,0xff jnz sg_advance1 - test STCNT+2,0xff jnz sg_advance1 - - clr SG_NOLOAD # reload s/g next time - dec SG_COUNT # one less segment to go - - clr A # add sizeof(struct scatter) - add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 - adc SG_NEXT+1,A,SG_NEXT+1 - adc SG_NEXT+2,A,SG_NEXT+2 - adc SG_NEXT+3,A,SG_NEXT+3 - - ret - -sg_advance1: - mvi SG_NOLOAD,0x80 # don't reload s/g next time -sg_advance2: - ret - -# Add the array base SYNCNEG to the target offset (the target address -# is in SCSIID), and return the result in SINDEX. The accumulator -# contains the 3->8 decoding of the target ID on return. -# -ndx_sdtr: - shr A,SCSIID,4 - and A,0x7 - add SINDEX,SYNCNEG,A - - and FUNCTION1,0x70,SCSIID # 3-bit target address decode - mov A,FUNCTION1 ret - -# If we need to negotiate transfer parameters, build the SDTR message -# starting at the address passed in SINDEX. DINDEX is modified on return. -# -mk_sdtr: - mov DINDEX,SINDEX # save SINDEX - - call ndx_sdtr - test NEEDSDTR,A jnz mk_sdtr1 # do we need negotiation? - ret - -mk_sdtr1: - mvi DINDIR,1 # extended message - mvi DINDIR,3 # extended message length = 3 - mvi DINDIR,1 # SDTR code - mvi DINDIR,25 # REQ/ACK transfer period - mvi DINDIR,15 # REQ/ACK offset - - add MSG_LEN,-MSG_START+0,DINDEX # update message length - ret - -# Set SCSI bus control signal state. This also saves the last-written -# value into a location where the higher-level driver can read it - if -# it has to send an ABORT or RESET message, then it needs to know this -# so it can assert ATN without upsetting SCSISIGO. The new value is -# expected in SINDEX. Change the actual state last to avoid contention -# from the driver. -# -scsisig: - mov SIGSTATE,SINDEX - mov SCSISIGO,SINDEX ret diff -urN lx-1.2.8/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- lx-1.2.8/drivers/scsi/aic7xxx.c Wed Dec 31 19:00:00 1969 +++ linux/drivers/scsi/aic7xxx.c Fri May 12 10:04:54 1995 @@ -0,0 +1,2875 @@ +/* + * @(#)aic7xxx.c 1.34 94/11/30 jda + * + * Adaptec 274x/284x/294x device driver for Linux. + * Copyright (c) 1994 The University of Calgary Department of Computer Science. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Sources include the Adaptec 1740 driver (aha1740.c), the + * Ultrastor 24F driver (ultrastor.c), various Linux kernel + * source, the Adaptec EISA config file (!adp7771.cfg), the + * Adaptec AHA-2740A Series User's Guide, the Linux Kernel + * Hacker's Guide, Writing a SCSI Device Driver for Linux, + * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA + * overlay file (adp7770.ovl), the Adaptec AHA-2740 Series + * Technical Reference Manual, the Adaptec AIC-7770 Data + * Book, the ANSI SCSI specification, the ANSI SCSI-2 + * specification (draft 10c), ... + * + * Multiple adapter card using the same IRQ level are not + * supported. It doesn't make sense to configure the cards this + * this way from a performance standpoint. Not to mention that + * the kernel would have to support two devices per registered IRQ. + * + * ---------------------------------------------------------------- + * + * Modified to include support for wide and twin bus adapters, + * DMAing of SCBs, tagged queueing, bug fixes, and other rework + * of the code. + * + * Boot time options were also added for ignoring the reversing + * of interrupts and not resetting the scsi bus. + * + * Form: aic7xxx=extended,reverse,no_reset + * + * -- Daniel M. Eischen, deischen@iworks.ecn.uiowa.edu, 03/11/95 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../block/blk.h" +#include "sd.h" +#include "scsi.h" +#include "hosts.h" +#include "aic7xxx.h" + +/* + * Defines for PCI bus support, testing twin bus support, DMAing of + * SCBs, and tagged queueing. + * + * o PCI bus support - this has been implemented and working since + * the December 1, 1994 release of this driver. If you don't have + * a PCI bus and do not wish to configure your kernel with PCI + * support, then make sure this define is set to the cprrect + * define for PCI support (CONFIG_PCI) and configure your kernel + * without PCI support (make config). + * + * o Twin bus support - this has been tested and does work. + * + * o DMAing of SCBs - as of this writing, I have been unable to + * get this working. FYI, all memory allocated by scsi_register + * returns addresses that are physical addresses. There should + * be no need to convert a virtual address to a physical address. + * + * o Tagged queueing - this driver is capable of tagged queueing + * but I am unsure as to how well the higher level driver implements + * tagged queueing. Therefore, the maximum commands per lun is + * set to 2. If you want to implement tagged queueing, ensure + * this define is not commented out. + * + * Daniel M. Eischen, deischen@iworks.ecn.uiowa.edu, 03/11/95 + */ + +/* + * We use our own define for PCI support in case the name ever changes. + * It would then have to change only in one place - here. + */ + +#ifdef CONFIG_PCI +#define AIC7XXX_PCI_SUPPORT +#endif + + +/* Uncomment this for testing twin bus support. */ +#define AIC7XXX_TWIN_SUPPORT + +/* Uncomment this for DMAing of SCBs. */ +/* #define AIC7XXX_USE_DMA */ +/* #define V_TO_P(x) (x) */ + +/* Uncomment this for tagged queueing. */ +#define AIC7XXX_TAGGED_QUEUEING + +/* Uncomment this for extra delays and more prints */ +/* #define DAN_DEBUG */ + +/* + * There should be a specific return value for this in scsi.h, but + * it seems that most drivers ignore it. + */ +#define DID_UNDERFLOW DID_ERROR + + + +/* EISA/VL-bus stuff */ + +#define MINSLOT 1 +#define MAXSLOT 15 +#define SLOTBASE(x) ((x) << 12) + +#define MAXIRQ 15 + +/* AIC-7770 offset definitions */ + +#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */ +#define O_MAXREG(x) ((x) + 0xcbf) + +/* + * SCSI Sequence Control (p. 3-11). + * Each bit, when set starts a specific SCSI sequence on the bus + */ +#define O_SCSISEQ(x) ((x) + 0xc00) +#define TEMODEO 0x80 +#define ENSELO 0x40 +#define ENSELI 0x20 +#define ENRSELI 0x10 +#define ENAUTOATNO 0x08 +#define ENAUTOATNI 0x04 +#define ENAUTOATNP 0x02 +#define SCSIRSTO 0x01 + +/* + * SCSI Transfer Control 1 Register (pp. 3-14,15). + * Controls the SCSI module data path. + */ +#define O_SXFRCTL1(x) ((x) + 0xc02) +#define BITBUCKET 0x80 +#define SWRAPEN 0x40 +#define ENSPCHK 0x20 +#define STIMESEL 0x18 +#define ENSTIMER 0x04 +#define ACTNEGEN 0x02 +#define STPWEN 0x01 /* Powered Termination */ + +/* + * SCSI Control Signal Read Register (p. 3-15). + * Reads the actual state of the SCSI bus pins + */ +#define O_SCSISIGI(x) ((x) + 0xc03) +#define CDI 0x80 +#define IOI 0x40 +#define MSGI 0x20 +#define ATNI 0x10 +#define SELI 0x08 +#define BSYI 0x04 +#define REQI 0x02 +#define ACKI 0x01 + +/* + * SCSI Contol Signal Write Register (p. 3-16). + * Writing to this register modifies the control signals on the bus. Only + * those signals that are allowed in the current mode (Initiator/Target) are + * asserted. + */ +#define O_SCSISIGO(x) ((x) + 0xc03) +#define CDO 0x80 +#define IOO 0x40 +#define MSGO 0x20 +#define ATNO 0x10 +#define SELO 0x08 +#define BSYO 0x04 +#define REQO 0x02 +#define ACKO 0x01 + +#define O_SCSIRATE(x) ((x) + 0xc04) /* scsi rate */ + +/* + * SCSI ID (p. 3-18). + * Contains the ID of the board and the current target on the + * selected channel + */ +#define O_SCSIID(x) ((x) + 0xc05) +#define TID 0xf0 /* Target ID mask */ +#define OID 0x0f /* Our ID mask */ + +/* + * SCSI Status 0 (p. 3-21) + * Contains one set of SCSI Interrupt codes + * These are most likely of interest to the sequencer + */ +#define O_SSTAT0(x) ((x) + 0xc0b) +#define TARGET 0x80 /* Board is a target */ +#define SELDO 0x40 /* Selection Done */ +#define SELDI 0x20 /* Board has been selected */ +#define SELINGO 0x10 /* Selection In Progress */ +#define SWRAP 0x08 /* 24bit counter wrap */ +#define SDONE 0x04 /* STCNT = 0x000000 */ +#define SPIORDY 0x02 /* SCSI PIO Ready */ +#define DMADONE 0x01 /* DMA transfer completed */ + +/* + * Clear SCSI Interrupt 1 (p. 3-23) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1. + */ +#define O_CLRSINT1(x) ((x) + 0xc0c) +#define CLRSELTIMEO 0x80 +#define CLRATNO 0x40 +#define CLRSCSIRSTI 0x20 +/* UNUSED 0x10 */ +#define CLRBUSFREE 0x08 +#define CLRSCSIPERR 0x04 +#define CLRPHASECHG 0x02 +#define CLRREQINIT 0x01 + +/* + * SCSI Status 1 (p. 3-24) + * These interrupt bits are of interest to the kernel driver + */ +#define O_SSTAT1(x) ((x) + 0xc0c) +#define SELTO 0x80 +#define ATNTARG 0x40 +#define SCSIRSTI 0x20 +#define PHASEMIS 0x10 +#define BUSFREE 0x08 +#define SCSIPERR 0x04 +#define PHASECHG 0x02 +#define REQINIT 0x01 + +/* + * SCSI Interrrupt Mode 1 (pp. 3-28,29). + * Set bits in this register enable the corresponding + * interrupt source. + */ +#define O_SIMODE1(x) ((x) + 0xc11) +#define ENSELTIMO 0x80 +#define ENATNTARG 0x40 +#define ENSCSIRST 0x20 +#define ENPHASEMIS 0x10 +#define ENBUSFREE 0x08 +#define ENSCSIPERR 0x04 +#define ENPHASECHG 0x02 +#define ENREQINIT 0x01 + +/* + * Selection/Reselection ID (p. 3-31) + * Upper four bits are the device id. The ONEBIT is set when the re/selecting + * device did not set its own ID. + */ +#define O_SELID(x) ((x) + 0xc19) +#define SELID_MASK 0xf0 +#define ONEBIT 0x08 +/* UNUSED 0x07 */ + +/* + * SCSI Block Control (p. 3-32) + * Controls Bus type and channel selection. In a twin channel configuration + * addresses 0x00-0x1e are gated to the appropriate channel based on this + * register. SELWIDE allows for the coexistence of 8bit and 16bit devices + * on a wide bus. + */ +#define O_SBLKCTL(x) ((x) + 0xc1f) +/* UNUSED 0xc0 */ +#define AUTOFLUSHDIS 0x20 /* used for Rev C check */ +/* UNUSED 0x10 */ +#define SELBUSB 0x08 +/* UNUSED 0x04 */ +#define SELWIDE 0x02 +/* UNUSED 0x01 */ + +/* + * Sequencer Control (p. 3-33) + * Error detection mode and speed configuration + */ +#define O_SEQCTL(x) ((x) + 0xc60) +#define PERRORDIS 0x80 +#define PAUSEDIS 0x40 +#define FAILDIS 0x20 +#define FASTMODE 0x10 +#define BRKADRINTEN 0x08 +#define STEP 0x04 +#define SEQRESET 0x02 +#define LOADRAM 0x01 + +/* + * Sequencer RAM Data (p. 3-34) + * Single byte window into the Scratch Ram area starting at the address + * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write + * four bytes in sucessesion. The SEQADDRs will increment after the most + * significant byte is written + */ +#define O_SEQRAM(x) ((x) + 0xc61) + +/* + * Sequencer Address Registers (p. 3-35) + * Only the first bit of SEQADDR1 holds addressing information + */ +#define O_SEQADDR0(x) ((x) + 0xc62) +#define O_SEQADDR1(x) ((x) + 0xc63) + +#define O_ACCUM(x) ((x) + 0xc64) /* accumulator */ +#define O_BIDx(x) ((x) + 0xc80) /* board id */ + +/* + * Board Control (p. 3-43) + */ +#define O_BCTL(x) ((x) + 0xc84) +/* RSVD 0xf0 */ +#define ACE 0x08 /* Support for external processors */ +/* RSVD 0x06 */ +#define ENABLE 0x01 + +#define O_BUSSPD(x) ((x) + 0xc86) /* FIFO threshold bits ? */ + +/* + * Host Control (p. 3-47) R/W + * Overal host control of the device. + */ +#define O_HCNTRL(x) ((x) + 0xc87) +/* UNUSED 0x80 */ +#define POWRDN 0x40 +/* UNUSED 0x20 */ +#define SWINT 0x10 +#define IRQMS 0x08 +#define PAUSE 0x04 +#define INTEN 0x02 +#define CHIPRST 0x01 +#define REQ_PAUSE IRQMS | PAUSE | INTEN +#define UNPAUSE_274X IRQMS | INTEN +#define UNPAUSE_284X INTEN + +/* + * SCB Pointer (p. 3-49) + * Gate one of the four SCBs into the SCBARRAY window. + */ +#define O_SCBPTR(x) ((x) + 0xc90) + +/* + * Interrupt Status (p. 3-50) + * Status for system interrupts + */ +#define O_INTSTAT(x) ((x) + 0xc91) +#define SEQINT_MASK 0xf0 /* SEQINT Status Codes */ +#define BAD_PHASE 0x00 +#define SEND_REJECT 0x10 +#define NO_IDENT 0x20 +#define NO_MATCH 0x30 +#define MSG_SDTR 0x40 +#define MSG_WDTR 0x50 +#define MSG_REJECT 0x60 +#define BAD_STATUS 0x70 +#define BRKADRINT 0x08 +#define SCSIINT 0x04 +#define CMDCMPLT 0x02 +#define SEQINT 0x01 +#define INT_PEND SEQINT | SCSIINT | CMDCMPLT /* For polling */ + +/* + * Hard Error (p. 3-53) + * Reporting of catastrophic errors. You usually cannot recover from + * these without a full board reset. + */ +#define O_ERROR(x) ((x) + 0xc92) /* hard error */ +/* UNUSED 0xf0 */ +#define PARERR 0x08 +#define ILLOPCODE 0x04 +#define ILLSADDR 0x02 +#define ILLHADDR 0x01 + +/* + * Clear Interrupt Status (p. 3-52) + */ +#define O_CLRINT(x) ((x) + 0xc92) +#define CLRBRKADRINT 0x08 +#define CLRINTSTAT 0x04 /* UNDOCUMENTED - must be unpaused */ +#define CLRCMDINT 0x02 +#define CLRSEQINT 0x01 + +/* + * SCB Auto Increment (p. 3-59) + * Byte offset into the SCB Array and an optional bit to allow auto + * incrementing of the address during download and upload operations + */ +#define O_SCBCNT(x) ((x) + 0xc9a) +#define SCBAUTO 0x80 +#define SCBCNT_MASK 0x1f + +/* + * Queue In FIFO (p. 3-60) + * Input queue for queued SCBs (commands that the seqencer has yet to start) + */ +#define O_QINFIFO(x) ((x) + 0xc9b) + +/* + * Queue In Count (p. 3-60) + * Number of queued SCBs + */ +#define O_QINCNT(x) ((x) + 0xc9c) + +/* + * Queue Out FIFO (p. 3-61) + * Queue of SCBs that have completed and await the host + */ +#define O_QOUTFIFO(x) ((x) + 0xc9d) + +/* + * Queue Out Count (p. 3-61) + * Number of queued SCBs in the Out FIFO + */ +#define O_QOUTCNT(x) ((x) + 0xc9e) + +#define O_SCBARRAY(x) ((x) + 0xca0) + +/* AIC-7870-only definitions */ + +#define O_DSPCISTATUS(x) ((x) + 0xc86) /* ??? */ + +/* Scratch RAM offset definitions */ + +#define HA_TARG_SCRATCH(x) ((x) + 0xc20) /* one byte per target starting at */ + /* this address for config values */ + +#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */ +#define HA_MSG_LEN(x) ((x) + 0xc34) /* pending message length */ +#define HA_MSG_START(x) ((x) + 0xc35) /* outgoing message body */ +#define HA_ARG_1(x) ((x) + 0xc4a) /* sdtr <-> rate parameters */ +#define HA_RETURN_1(x) ((x) + 0xc4a) +#define SEND_WDTR 0x80 +#define SEND_SDTR 0x80 + +#define HA_SIGSTATE(x) ((x) + 0xc4b) /* value in SCSISIGO */ +#define HA_SCBCOUNT(x) ((x) + 0xc52) /* number of hardware SCBs */ + +#define HA_FLAGS(x) ((x) + 0xc53) /* TWIN and WIDE bus flags */ +#define TWIN_BUS 0x01 /* mask for TWIN bit */ +#define WIDE_BUS 0x02 /* mask for WIDE bit */ +#define SENSE 0x10 +#define ACTIVE_MSG 0x20 +#define IDENTIFY_SEEN 0x40 +#define RESELECTING 0x80 + +#define HA_ACTIVE0(x) ((x) + 0xc54) /* Active bits; targets 0-7 */ +#define HA_ACTIVE1(x) ((x) + 0xc55) /* Active bits; targets 8-15 */ +#define SAVED_TCL(x) ((x) + 0xc56) /* Saved target, channel, LUN */ + +#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */ +#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */ +#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */ + +#define MSG_ABORT 0x06 +#define BUS_8_BIT 0x00 +#define BUS_16_BIT 0x01 +#define BUS_32_BIT 0x02 + + +/* debugging code */ + +#define AIC7XXX_DEBUG + +/* + * If a parity error occurs during a data transfer phase, run the + * command to completion - it's easier that way - making a note + * of the error condition in this location. This then will modify + * a DID_OK status into a DID_PARITY one for the higher-level SCSI + * code. + */ +#define aic7xxx_parity(cmd) ((cmd)->SCp.Status) +#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command) +#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in) + +/* + * Since the sequencer code DMAs the scatter-gather structures + * directly from memory, we use this macro to assert that the + * kernel structure hasn't changed. + */ +#define SG_STRUCT_CHECK(sg) \ + ((char *)&(sg).address - (char *)&(sg) != 0 || \ + (char *)&(sg).length - (char *)&(sg) != 8 || \ + sizeof((sg).address) != 4 || \ + sizeof((sg).length) != 4 || \ + sizeof(sg) != 12) + +/* + * "Static" structures. Note that these are NOT initialized + * to zero inside the kernel - we have to initialize them all + * explicitly. + * + * We support a maximum of one adapter card per IRQ level (see the + * rationale for this above). On an interrupt, use the IRQ as an + * index into aic7xxx_boards[] to locate the card information. + */ +static struct Scsi_Host *aic7xxx_boards[MAXIRQ + 1]; + +/* + * The maximum number of SCBs we could have for ANY type + * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE + * SEQUENCER CODE IF THIS IS MODIFIED! + */ +#define AIC7XXX_MAXSCB 16 + +typedef enum {T_NONE, T_274X, T_284X, T_294X, T_MAX} aha_type; +typedef enum {T_NORMAL, T_TWIN, T_WIDE} aha_bus_type; + + +/* + * Define an enumerated type for the state of an SCB. + */ +typedef enum +{ + SCB_FREE = 0, + SCB_ACTIVE = 0x1, + SCB_ABORTED = 0x2, + SCB_IMMED = 0x04, + SCB_IMMED_FAIL = 0x08, + SCB_SENSE = 0x10 +} scb_state; + +/* Define the bits for the control of an scb. */ +#define SCB_NEEDWDTR 0x80 +#define SCB_NEEDSDTR 0x40 +#define SCB_TE 0x20 +#define SCB_NEEDDMA 0x08 +#define SCB_DIS 0x04 +#define SCB_TAG_TYPE 0x03 +#define SIMPLE_QUEUE 0x0 +#define HEAD_QUEUE 0x1 +#define OR_QUEUE 0x2 + +#define SCB_DOWNLOAD_SIZE 26 +#define SCB_UPLOAD_SIZE 26 +struct aic7xxx_scb { + unsigned char control; + unsigned char target_channel_lun; /* 4/1/3 bits */ + unsigned char SG_segment_count; + unsigned char SG_list_pointer[4] __attribute__ ((packed)); + unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed)); + unsigned char SCSI_cmd_length; + unsigned char RESERVED[2]; /* must be zero */ + unsigned char target_status; + unsigned char residual_data_count[3]; + unsigned char residual_SG_segment_count; + unsigned char data_pointer[4] __attribute__ ((packed)); + unsigned char data_count[3]; + unsigned long host_scb __attribute__ ((packed)); +#if 0 + /* + * No real point in transferring this to the + * SCB registers. + */ + unsigned char RESERVED[2]; +#endif + /*------------ end of hardware supported fields ----------------*/ + struct aic7xxx_scb * next; /* next ptr when in free list */ + Scsi_Cmnd * cmd; /* Scsi_Cmnd for this scb */ + scb_state state; /* current state of scb */ + unsigned int position; /* position in scb array */ +}; + +static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0}; + + +struct aic7xxx_host_config { + int irq; /* IRQ number */ + int base; /* I/O base */ + int maxscb; /* hardware SCBs */ + int unpause; /* unpause value for HCNTRL */ + int pause; /* pause value for HCNTRL */ + int scsi_id; /* host SCSI ID */ + int scsi_id_b; /* host SCSI ID B channel for twin cards */ + int extended; /* extended xlate? */ + aha_type type; /* card type */ + aha_bus_type bus_type; /* normal/twin/wide bus */ + unsigned char on_board; /* flag for on-board 7870 */ +}; + +/* Define a structure used for each host adapter, only one per IRQ. */ +struct aic7xxx_host +{ + int base; /* card base address */ + int maxscb; /* hardware SCBs */ + int numscb; /* current number of scbs */ + int startup; /* intr type check */ + int extended; /* extended xlate? */ + aha_type type; /* card type */ + aha_bus_type bus_type; /* normal/twin/wide bus */ + unsigned char a_scanned; /* 0 not scanned, 1 scanned */ + unsigned char b_scanned; /* 0 not scanned, 1 scanned */ + volatile unsigned char unpause; /* unpause value for HCNTRL */ + volatile unsigned char pause; /* pause value for HCNTRL */ + volatile unsigned short needsdtr_copy; /* default config */ + volatile unsigned short needsdtr; + volatile unsigned short sdtr_pending; + volatile unsigned short needwdtr_copy; /* default config */ + volatile unsigned short needwdtr; + volatile unsigned short wdtr_pending; + struct aic7xxx_scb scb_array[AIC7XXX_MAXSCB]; /* copy of boards scb array */ + struct aic7xxx_scb * free_scb; /* list of free SCBs */ +}; + + +/* + * NB. This table MUST be ordered shortest period first. + */ +static struct { + short period; + short rate; + char *english; +} aic7xxx_synctab[] = { + {100, 0, "10.0"}, + {125, 1, "8.0"}, + {150, 2, "6.67"}, + {175, 3, "5.7"}, + {200, 4, "5.0"}, + {225, 5, "4.4"}, + {250, 6, "4.0"}, + {275, 7, "3.6"} +}; + +static int aic7xxx_synctab_max = + sizeof(aic7xxx_synctab) / sizeof(aic7xxx_synctab[0]); + +#ifdef AIC7XXX_DEBUG + + extern int vsprintf(char *, const char *, va_list); + + static + void debug(const char *fmt, ...) + { + va_list ap; + char buf[256]; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + printk(buf); + va_end(ap); + } + + static + void debug_config(struct aic7xxx_host_config *p) + { + int ioport2, ioport3; + + static char *BRT[T_MAX][16] = { + { }, /* T_NONE */ + { + "2", "???", "???", "12", /* T_274X */ + "???", "???", "???", "28", + "???", "???", "???", "44", + "???", "???", "???", "60" + }, + { + "2", "4", "8", "12", /* T_284X */ + "16", "20", "24", "28", + "32", "36", "40", "44", + "48", "52", "56", "60" + }, + { + "???", "???", "???", "???", /* T_294X */ + "???", "???", "???", "???", + "???", "???", "???", "???", + "???", "???", "???", "???" + } + }; + static int DFT[4] = { + 0, 50, 75, 100 + }; + static int SST[4] = { + 256, 128, 64, 32 + }; + + static char * BUSW[3] = {"", "-TWIN", "-WIDE"}; + + ioport2 = inb(HA_HOSTCONF(p->base)); + ioport3 = inb(HA_SCSICONF(p->base)); + + switch (p->type) { + case T_274X: + printk("AHA274X%s AT EISA SLOT %d:\n", + BUSW[p->bus_type], p->base >> 12); + break; + case T_284X: + printk("AHA284X%s AT SLOT %d:\n", + BUSW[p->bus_type], p->base >> 12); + break; + case T_294X: + printk("AHA294X%s (PCI-bus):\n", BUSW[p->bus_type]); + break; + default: + panic("aic7xxx debug_config: internal error\n"); + } + + printk(" irq %d\n", p->irq); + if (p->type != T_294X) + { + printk (" bus release time %s bclks\n" + " data fifo threshold %d%%\n", + BRT[p->type][(ioport2 >> 2) & 0xf], + DFT[(ioport2 >> 6) & 0x3]); + } + else + { + printk (" bus release time %s bclks\n" + " data fifo threshold %d%%\n", + BRT[p->type][(ioport2 >> 2) & 0xf], + DFT[(ioport3 >> 6) & 0x3]); + } + /* Come back and fix these later. */ + printk(" SCSI CHANNEL A:\n" + " scsi id %d\n" + " scsi bus parity check %sabled\n" + " scsi selection timeout %d ms\n" + " scsi bus reset at power-on %sabled\n", + ioport3 & 0x7, + (ioport3 & 0x20) ? "en" : "dis", + SST[(ioport3 >> 3) & 0x3], + (ioport3 & 0x40) ? "en" : "dis"); + + if (p->type == T_274X) { + printk(" scsi bus termination %sabled\n", + (ioport3 & 0x80) ? "en" : "dis"); + } + } + + +#else + +# define debug(fmt, args...) +# define debug_config(x) + +#endif AIC7XXX_DEBUG + +/* + * XXX - these options apply unilaterally to _all_ 274x/284x/294x + * cards in the system. This should be fixed, but then, + * does anyone really have more than one in a machine? + */ +static int aic7xxx_extended = 0; /* extended translation on? */ +static int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */ + +void aic7xxx_setup(char *s, int *dummy) +{ + int i; + char *p; + + static struct { + char *name; + int *flag; + } options[] = { + {"extended", &aic7xxx_extended}, + {"no_reset", &aic7xxx_no_reset}, + {NULL, NULL} + }; + + for (p = strtok(s, ","); p; p = strtok(NULL, ",")) { + for (i = 0; options[i].name; i++) + if (!strcmp(options[i].name, p)) + *(options[i].flag) = !0; + } +} + +static +void aic7xxx_getscb(int base, struct aic7xxx_scb *scb) +{ + /* + * This is almost identical to aic7xxx_putscb(). + */ + outb(SCBAUTO, O_SCBCNT(base)); + + asm volatile("cld\n\t" + "rep\n\t" + "insb" + : /* no output */ + :"D" (scb), "c" (SCB_UPLOAD_SIZE), "d" (O_SCBARRAY(base)) + :"di", "cx", "dx"); + + outb(0, O_SCBCNT(base)); +} + +static +void aic7xxx_putscb(int base, struct aic7xxx_scb *scb) +{ +#ifdef AIC7XXX_USE_DMA + /* + * All we need to do, is to output the position + * of the SCB in the SCBARRAY to the QINFIFO + * of the host adapter. + */ + outb (scb->position, O_QINFIFO(base)); +#else + /* + * By turning on the SCB auto increment, any reference + * to the SCB I/O space postincrements the SCB address + * we're looking at. So turn this on and dump the relevant + * portion of the SCB to the card. + */ + outb(SCBAUTO, O_SCBCNT(base)); /* SCBAUTO */ + + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (O_SCBARRAY(base)) + :"si", "cx", "dx"); + + outb(0, O_SCBCNT(base)); +#endif +} + +#ifdef AIC7XXX_USE_DMA +static +void aic7xxx_putdmascb(int base, struct aic7xxx_scb *scb) +{ + /* + * By turning on the SCB auto increment, any reference + * to the SCB I/O space postincrements the SCB address + * we're looking at. So turn this on and dump the relevant + * portion of the SCB to the card. + */ + outb(SCBAUTO, O_SCBCNT(base)); /* SCBAUTO */ + + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (scb), "c" (30), "d" (O_SCBARRAY(base)) + :"si", "cx", "dx"); + + outb(0, O_SCBCNT(base)); +} +#endif + +/* + * How much data should be transferred for this SCSI command? Stop + * at segment sg_last if it's a scatter-gather command so we can + * compute underflow easily. + */ +static +unsigned aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) +{ + int i, segments; + unsigned length; + struct scatterlist *sg; + + segments = cmd->use_sg - sg_last; + sg = (struct scatterlist *)cmd->buffer; + + if (cmd->use_sg) { + for (i = length = 0; + i < cmd->use_sg && i < segments; + i++) + { + length += sg[i].length; + } + } else + length = cmd->request_bufflen; + + return(length); +} + +static +void aic7xxx_to_scsirate(unsigned char *rate, + unsigned char transfer, + unsigned char offset, + int target) +{ + int i; + + for (i = 0; i < aic7xxx_synctab_max; i++) { + + if ((aic7xxx_synctab[i].period - transfer) >= 0) { + *rate = (aic7xxx_synctab[i].rate << 4) | (offset & 0xf); + printk("aic7xxx: target %d now synchronous at %sMb/s\n", + target, + aic7xxx_synctab[i].english); + return; + } + } + *rate = 0; + printk("aic7xxx: target %d using asynchronous transfers\n", + target ); +} + +/* + * Pause the sequencer and wait for it to actually stop - this + * is important since the sequencer can disable pausing for critical + * sections. + */ +#define PAUSE_SEQUENCER(p) \ + do { \ + outb(p->pause, O_HCNTRL(p->base)); \ + \ + while ((inb(O_HCNTRL(p->base)) & PAUSE) == 0) \ + ; \ + } while (0) + +/* + * Unpause the sequencer. Unremarkable, yet done often enough to + * warrant an easy way to do it. + */ +#define UNPAUSE_SEQUENCER(p) \ + outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */ + +/* + * See comments in aic7xxx_loadram() wrt this. + */ +#define RESTART_SEQUENCER(p) \ + do { \ + outb(SEQRESET | FASTMODE, O_SEQCTL(p->base)); \ + \ + } while (inb(O_SEQADDR0(p->base)) != 0 && \ + inb(O_SEQADDR1(p->base)) != 0); \ + \ + UNPAUSE_SEQUENCER(p); + +/* + * Since we declared this using SA_INTERRUPT, interrupts should + * be disabled all through this function unless we say otherwise. + */ +static +void aic7xxx_isr(int irq, struct pt_regs * regs) +{ + int base, intstat; + struct aic7xxx_host *p; + struct aic7xxx_scb *scb; + unsigned char active, ha_flags, transfer; + unsigned char scsi_id, bus_width; + unsigned char offset, rate, scratch; + unsigned short target_mask; + long flags; + void *addr; + int actual; + int target, tcl; + int scbptr; + Scsi_Cmnd *cmd; +#if 0 +static int_count = 0; +#endif + + p = (struct aic7xxx_host *)aic7xxx_boards[irq]->hostdata; + base = p->base; + + /* + * Check the startup flag - if no commands have been queued, + * we probably have the interrupt type set wrong. Reverse + * the stored value and the active one in the host control + * register. + */ + if (p->startup) + { + switch (p->startup) + { + case 1: + /* Allow for 1 interrupt when the card is enabled. */ + p->startup = 2; + break; + + case 2: + p->pause = p->pause ^ IRQMS; + p->unpause = p->unpause ^ IRQMS; + /* + * Reverse the interrupt type being careful to preserve + * the current state (paused or unpaused). + */ + outb ((inb(O_HCNTRL(p->base)) & PAUSE) | p->unpause, + O_HCNTRL(p->base)); + printk("aic7xxx_isr: Switching interrupt trigger, hcntrl=0x%x\n", + inb(O_HCNTRL(p->base))); + p->startup = 3; + break; + + default: + break; + } + return; + } + + /* + * Handle all the interrupt sources - especially for SCSI + * interrupts, we won't get a second chance at them. + */ + intstat = inb(O_INTSTAT(base)); + + if (intstat & BRKADRINT) { + + panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n", + inb(O_ERROR(base)), + inb(O_SEQADDR1(base)) << 8 | inb(O_SEQADDR0(base))); + } + + if (intstat & SEQINT) { + + /* + * Although the sequencer is paused immediately on + * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT + * condition will have unpaused the sequencer before + * this point. + */ + PAUSE_SEQUENCER(p); + + switch (intstat & SEQINT_MASK) { + case BAD_PHASE: + panic("aic7xxx_isr: unknown scsi bus phase\n"); + case SEND_REJECT: + debug("aic7xxx_isr warning: " + "issuing message reject, 1st byte 0x%x\n", + inb(HA_REJBYTE(base))); + break; + case NO_IDENT: + panic("aic7xxx_isr: reconnecting target %d at seqaddr 0x%x " + "didn't issue IDENTIFY message\n", + (inb(O_SELID(base)) >> 4) & 0xf, + (inb(O_SEQADDR1(base)) << 8) | inb(O_SEQADDR0(base))); + break; + case NO_MATCH: + target = (inb(O_SELID(base)) >> 4) & 0xf; + tcl = inb(O_SCBARRAY(base) + 1); + /* Purposefully mask off the top bit of targets 8-15. */ + target_mask = 0x01 << (target & 0x07); + + debug("aic7xxx_isr: sequencer couldn't find match " + "for reconnecting target %d, channel %d, lun %d - " + "issuing ABORT\n", target, (tcl & 0x08) >> 3, + tcl & 0x07); + if (tcl & 0x88) { + /* Second channel stores its info in byte + * two of HA_ACTIVE + */ + active = inb(HA_ACTIVE1(base)); + active = active & ~(target_mask); + outb(active, HA_ACTIVE1(base)); + } + else + { + active = inb(HA_ACTIVE0(base)); + active = active & ~(target_mask); + outb(active, HA_ACTIVE0(base)); + } +#ifdef AIC7XXX_USE_DMA + outb (SCB_NEEDDMA, O_SCBARRAY(base)); +#endif + + /* Check out why this use to be outb (0x80, O_CLRINT(base)) */ + /* clear the timeout */ + outb(CLRSELTIMEO, O_CLRSINT1(base)); + RESTART_SEQUENCER (p); + break; + case MSG_SDTR: + /* + * Help the sequencer to translate the negotiated + * transfer rate. Transfer is 1/4 the period + * in ns as is returned by the sync negotiation + * message. So, we must multiply by four. + */ + transfer = (inb(HA_ARG_1(base)) << 2); + offset = inb(O_ACCUM(base)); + scsi_id = inb(O_SCSIID(base)) >> 0x04; + aic7xxx_to_scsirate(&rate, transfer, offset, scsi_id); + if (inb(O_SBLKCTL(base)) & 0x08) + scsi_id = scsi_id + 8; /* B channel */ + scratch = inb(HA_TARG_SCRATCH(base) + scsi_id); + /* Preserve the wide transfer flag. */ + rate = rate | (scratch & 0x80); + outb(rate, HA_TARG_SCRATCH(base) + scsi_id); + outb(rate, O_SCSIRATE(base)); + + target_mask = (0x01 << scsi_id); + /* See if we initiated Sync Negotiation */ + if (p->sdtr_pending & target_mask) + { /* + * Negate the flag and don't send an SDTR + * back to the target. + */ + p->needsdtr = p->needsdtr & ~target_mask; + p->sdtr_pending = p->sdtr_pending & ~target_mask; + outb(0, HA_RETURN_1(base)); + } + else + { /* Send our own SDTR in reply. */ + printk ("Sending SDTR!!\n"); + outb(SEND_SDTR, HA_RETURN_1(base)); + } + break; + case MSG_WDTR: + { + bus_width = inb(O_ACCUM(base)); + scsi_id = inb(O_SCSIID(base)) >> 0x04; + if (inb(O_SBLKCTL(base)) & 0x08) + scsi_id = scsi_id + 8; /* B channel */ + printk ("Received MSG_WDTR, scsi_id = %d, " + "needwdtr = 0x%x\n", scsi_id, p->needwdtr); + scratch = inb(HA_TARG_SCRATCH(base) + scsi_id); + + target_mask = (0x01 << scsi_id); + if (p->wdtr_pending & target_mask) + { /* + * Negate the flag and don't send an WDTR + * back to the target, since we asked first. + */ + p->needwdtr = p->needwdtr & ~target_mask; + p->wdtr_pending = p->wdtr_pending & ~target_mask; + outb(0, HA_RETURN_1(base)); + switch (bus_width) + { + case BUS_8_BIT: + scratch = scratch & 0x7f; + break; + case BUS_16_BIT: + printk ("aic7xxx_isr: target %d using 16 " + "bit transfers\n", scsi_id); + scratch = scratch | 0x88; + scratch = scratch & 0xf8; + break; + } + } + else + { /* Send our own WDTR in reply. */ + printk ("Will send WDTR!!\n"); + switch (bus_width) + { + case BUS_8_BIT: + scratch = scratch & 0x7f; + break; + case BUS_32_BIT: + /* Negotiate 16 bits. */ + bus_width = BUS_16_BIT; + case BUS_16_BIT: + printk ("aic7xxx_isr: target %d using 16 " + "bit transfers\n", scsi_id); + scratch = scratch | 0x88; + scratch = scratch & 0xf8; + break; + } + outb (bus_width | SEND_WDTR, HA_RETURN_1(base)); + } + outb (scratch, HA_TARG_SCRATCH(base) + scsi_id); + outb (scratch, O_SCSIRATE(base)); + break; + } + + case MSG_REJECT: + { + /* + * What we care about here is if we had an + * outstanding SDTR or WDTR message for this + * target. If we did, this is a signal that + * the target is refusing negotiation. + */ + + unsigned char targ_scratch, scsi_id; + unsigned short mask; + + scsi_id = inb(O_SCSIID(base)) >> 0x4; + if (inb(O_SBLKCTL(base)) & 0x08) + scsi_id = scsi_id + 8; + + mask = (0x01 << scsi_id); + + targ_scratch = inb(HA_TARG_SCRATCH(base) + + scsi_id); + + if (p->needwdtr & mask) { + /* note 8bit xfers and clear flag */ + targ_scratch = targ_scratch & 0x7f; + p->needwdtr = p->needwdtr & ~mask; + p->wdtr_pending = p->wdtr_pending & ~mask; + outb(targ_scratch, HA_TARG_SCRATCH(base) + + scsi_id); + printk("aic7xxx: target %d refusing " + "WIDE negotiation. Using " + "8 bit transfers\n", + scsi_id); + } + else + { + if (p->needsdtr & mask) { + /* note asynch xfers and clear flag */ + targ_scratch = targ_scratch & 0xf0; + p->needsdtr = p->needsdtr & ~mask; + p->sdtr_pending = p->sdtr_pending & ~mask; + outb(targ_scratch, HA_TARG_SCRATCH(base) + + scsi_id); + printk("aic7xxx: target %d refusing " + "syncronous negotiation. Using " + "asyncronous transfers\n", + scsi_id); + } + } + /* + * Otherwise, we ignore it. + */ + break; + } + case BAD_STATUS: + scsi_id = inb(O_SCSIID(base)) >> 0x04; + scbptr = inb(O_SCBPTR(base)); + scb = &(p->scb_array[scbptr]); + if ((scb->state != SCB_ACTIVE) + || (scb->cmd == NULL)) + { + printk("aic7xxx_isr: referenced scb not valid " + "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n", + intstat, scbptr, scb->state, (unsigned int) scb->cmd); + } + else + { + cmd = scb->cmd; + aic7xxx_getscb(base, scb); + aic7xxx_status(cmd) = scb->target_status; + + cmd->result = cmd->result | scb->target_status; + + /* + * Did we underflow? At this time, there's only + * one other driver that bothers to check for this, + * and cmd->underflow seems to be set rather half- + * heartedly in the higher-level SCSI code. + */ + actual = aic7xxx_length(cmd, + scb->residual_SG_segment_count); + + actual -= ((scb->residual_data_count[2] << 16) | + (scb->residual_data_count[1] << 8) | + (scb->residual_data_count[0])); + + if (actual < cmd->underflow) { + printk("aic7xxx: target %d underflow - " + "wanted (at least) %u, got %u\n", + cmd->target, cmd->underflow, actual); + + cmd->result = scb->target_status | + (DID_UNDERFLOW << 16); + } + /* + * This test is just here for debugging purposes. + * It will go away when the timeout problem is resolved. + */ + switch (status_byte(scb->target_status)) + { + case GOOD: + break; + case CHECK_CONDITION: + if ((aic7xxx_parity(cmd) == 0) + && !(cmd->flags & WAS_SENSE)) + { + /* Update the timeout for the SCSI command. */ + update_timeout (cmd, SENSE_TIMEOUT); + + /* Send a sense command to the requesting target. */ + cmd->flags = cmd->flags | WAS_SENSE; + memcpy ((void *) cmd->cmnd, (void *) generic_sense, + sizeof(generic_sense)); + + cmd->cmnd[1] = cmd->lun << 5; + cmd->cmnd[4] = sizeof(cmd->sense_buffer); + + cmd->request_buffer = &cmd->sense_buffer; + cmd->request_bufflen = sizeof(cmd->sense_buffer); + cmd->use_sg = 0; + cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); + memset(scb, 0, SCB_DOWNLOAD_SIZE); + scb->target_channel_lun = ((cmd->target << 4) & 0xf0) + | (cmd->lun & 0xf); +#ifdef AIC7XXX_USE_DMA + scb->control = SCB_NEEDDMA; +#endif + addr = cmd->cmnd; + scb->SCSI_cmd_length = cmd->cmd_len; + memcpy(scb->SCSI_cmd_pointer, &addr, + sizeof(scb->SCSI_cmd_pointer)); + + scb->SG_segment_count = 0; + memcpy(scb->data_pointer, + &cmd->request_buffer, + sizeof(scb->data_pointer)); + memcpy(scb->data_count, + &cmd->request_bufflen, + sizeof(scb->data_count)); + + outb(SCBAUTO, O_SCBCNT(base)); /* SCBAUTO */ + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (O_SCBARRAY(base)) + :"si", "cx", "dx"); + outb(0, O_SCBCNT(base)); + + ha_flags = inb(HA_FLAGS(base)); + outb (ha_flags | SENSE, HA_FLAGS(base)); + } /* first time sense, no errors */ + else + { /* + * Do a normal command complete and have the + * scsi driver handle this condition. + */ + cmd->flags = cmd->flags | ASKED_FOR_SENSE; + } + break; + case BUSY: + printk ("aic7xxx_isr: Target busy\n"); + break; + default: + printk ("aic7xxx_isr: Unexpected target status 0x%x\n", + scb->target_status); + break; + } /* end switch */ + } /* end else of */ + break; + default: /* unknown */ + debug("aic7xxx_isr: seqint, " + "intstat = 0x%x, scsisigi = 0x%x\n", + intstat, inb(O_SCSISIGI(base))); + break; + } + outb(CLRSEQINT, O_CLRINT(base)); /* CLRSEQINT */ + UNPAUSE_SEQUENCER(p); + } + + if (intstat & SCSIINT) { + + int status = inb(O_SSTAT1(base)); + + scbptr = inb(O_SCBPTR(base)); + scb = &p->scb_array[scbptr]; + if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk("aic7xxx_isr: no command for scb (scsiint)\n"); + /* + * Turn off the interrupt and set status + * to zero, so that it falls through the + * reset of the SCSIINT code. + */ + outb(status, O_CLRSINT1(base)); + UNPAUSE_SEQUENCER(p); + outb(CLRINTSTAT, O_CLRINT(base)); /* undocumented */ + status = 0; + scb = NULL; + } + else + { + cmd = scb->cmd; + + /* + * Only the SCSI Status 1 register has information + * about exceptional conditions that we'd have a + * SCSIINT about; anything in SSTAT0 will be handled + * by the sequencer. Note that there can be multiple + * bits set. + */ + if (status & SELTO) + { + unsigned char target_mask = (1 << (cmd->target & 0x7)); + + /* + * Hardware selection timer has expired. Turn + * off SCSI selection sequence. + */ + outb(0, O_SCSISEQ(base)); + cmd->result = (DID_TIME_OUT << 16); + /* + * Clear an pending messages for the timed out + * target and mark the target as free. + */ + ha_flags = inb(HA_FLAGS(base)); + outb(ha_flags & ~ACTIVE_MSG, HA_FLAGS(base)); + + if (scb->target_channel_lun & 0x88) + { + active = inb(HA_ACTIVE1(base)); + active = active & ~(target_mask); + outb(active, HA_ACTIVE1(base)); + } + else + { + active = inb(HA_ACTIVE0(base)); + active = active & ~(target_mask); + outb(active, HA_ACTIVE0(base)); + } + +#ifdef AIC7XXX_USE_DMA + outb (SCB_NEEDDMA, O_SCBARRAY(base)); +#endif + + /* + * Shut off the offending interrupt sources, reset + * the sequencer address to zero and unpause it, + * then call the high-level SCSI completion routine. + * + * WARNING! This is a magic sequence! After many + * hours of guesswork, turning off the SCSI interrupts + * in CLRSINT? does NOT clear the SCSIINT bit in + * INTSTAT. By writing to the (undocumented, unused + * according to the AIC-7770 manual) third bit of + * CLRINT, you can clear INTSTAT. But, if you do it + * while the sequencer is paused, you get a BRKADRINT + * with an Illegal Host Address status, so the + * sequencer has to be restarted first. + */ + outb(CLRSELTIMEO, O_CLRSINT1(base)); + RESTART_SEQUENCER(p); + + outb(CLRINTSTAT, O_CLRINT(base)); /* undocumented */ + + /* + * This is a critical section, since we don't want the + * queue routine mucking with the host data. + */ + save_flags(flags); + cli(); + + /* + * Process the command after marking the scb as free + * and adding it to the free list. + */ + scb->state = SCB_FREE; + scb->cmd = NULL; + scb->next = p->free_scb; /* preserve next pointer */ + p->free_scb = scb; /* add at head of list */ + + restore_flags (flags); + + cmd->scsi_done(cmd); +#if 0 + printk("aic7xxx_isr: SELTO scb(%d) state(%x), cmd(%x)\n", + scb->position, scb->state, (unsigned int) scb->cmd); +#endif + } + + if (status & SCSIPERR) + { + /* + * A parity error has occurred during a data + * transfer phase. Flag it and continue. + */ + printk("aic7xxx: parity error on target %d, " + "channel %d, lun %d\n", + cmd->target, + (cmd->lun & 0x8) >> 3, + cmd->lun & 0x7); + aic7xxx_parity(cmd) = DID_PARITY; + + /* + * Clear interrupt and resume as above. + */ + outb(CLRSCSIPERR, O_CLRSINT1(base)); + UNPAUSE_SEQUENCER(p); + + outb(CLRINTSTAT, O_CLRINT(base)); + scb = NULL; + } + + if (status & BUSFREE) + { + /* + * A spurious bus free since selection. This + * should not happen. + */ +#if 0 + print ("aic7xxx_isr: unexpected busfree\n"); + outb (CLRBUSFREE, O_CLRINT(base)); /* clear busfree */ +#endif + } + else + { + if ((status & (SCSIPERR | SELTO)) == 0 && status) + { + + /* + * We don't know what's going on. Turn off the + * interrupt source and try to continue. + */ + printk("aic7xxx_isr: sstat1 = 0x%x\n", status); + outb(status, O_CLRSINT1(base)); + UNPAUSE_SEQUENCER(p); + outb(CLRINTSTAT, O_CLRINT(base)); /* undocumented */ + scb = NULL; + } + } + } /* else */ + } + + if (intstat & CMDCMPLT) { + + int complete; + + /* + * The sequencer will continue running when it + * issues this interrupt. There may be >1 commands + * finished, so loop until we've processed them all. + */ + do { + complete = inb(O_QOUTFIFO(base)); + + scb = &(p->scb_array[complete]); + if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk("aic7xxx warning: " + "no command for scb %d (cmdcmplt)\n" + "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x\n", + complete, inb(O_QOUTFIFO(base)), + scb->state, (unsigned int) scb->cmd); + outb(CLRCMDINT, O_CLRINT(base)); + continue; + } + cmd = scb->cmd; + if (cmd->flags & WAS_SENSE) + { + if (cmd->flags & ASKED_FOR_SENSE) + { /* Failed to obtain sense information */ + cmd->result = aic7xxx_status(cmd) + | (aic7xxx_parity(cmd) << 16); + } + else + { /* + * Got sense information. The higher level scsi + * code checks the sense buffer in the scsi command + * if the target status comes back with a mode of + * CHECK_CONDITION. Clear the sense flags from the + * command and return the target status. + */ + cmd->result = aic7xxx_status(cmd) + | (aic7xxx_parity(cmd) << 16); + cmd->flags = cmd->flags & ~(WAS_SENSE | ASKED_FOR_SENSE); + } + } + else + { + cmd->result = aic7xxx_status(cmd); + } +#if 0 + printk ("aic7xxx_intr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n", + scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb); +#endif + /* + * This is a critical section, since we don't want the + * queue routine mucking with the host data. + */ + save_flags(flags); + cli(); + + scb->state = SCB_FREE; + scb->next = p->free_scb; + scb->cmd = NULL; + p->free_scb = &(p->scb_array[scb->position]); + + restore_flags (flags); +#if 0 + if (scb != &p->scb_array[scb->position]) + printk ("aic7xxx_isr: (complete) address mismatch, pos %d\n", scb->position); + printk ("aic7xxx_isr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n", + scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb); +#endif + + cmd->scsi_done(cmd); + + /* + * Clear interrupt status before checking + * the output queue again. This eliminates + * a race condition whereby a command could + * complete between the queue poll and the + * interrupt clearing, so notification of the + * command being complete never made it back + * up to the kernel. + */ + outb(CLRCMDINT, O_CLRINT(base)); + + } while (inb(O_QOUTCNT(base))); + } + +} + +/* + * Probing for EISA boards: it looks like the first two bytes + * are a manufacturer code - three characters, five bits each: + * + * BYTE 0 BYTE 1 BYTE 2 BYTE 3 + * ?1111122 22233333 PPPPPPPP RRRRRRRR + * + * The characters are baselined off ASCII '@', so add that value + * to each to get the real ASCII code for it. The next two bytes + * appear to be a product and revision number, probably vendor- + * specific. This is what is being searched for at each port, + * and what should probably correspond to the ID= field in the + * ECU's .cfg file for the card - if your card is not detected, + * make sure your signature is listed in the array. + * + * The fourth byte's lowest bit seems to be an enabled/disabled + * flag (rest of the bits are reserved?). + */ + +static +aha_type aic7xxx_probe(int slot, int s_base) +{ + int i; + unsigned char buf[4]; + + static struct { + int n; + unsigned char signature[sizeof(buf)]; + aha_type type; + } S[] = { + {4, { 0x04, 0x90, 0x77, 0x71 }, T_274X}, /* host adapter 274x */ + {4, { 0x04, 0x90, 0x77, 0x70 }, T_274X}, /* motherboard 274x */ + {4, { 0x04, 0x90, 0x77, 0x56 }, T_284X} /* 284x, BIOS enabled */ + }; + + for (i = 0; i < sizeof(buf); i++) { + /* + * The VL-bus cards need to be primed by + * writing before a signature check. + */ + outb(0x80 + i, s_base); + buf[i] = inb(s_base + i); + } + + for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) { + if (!memcmp(buf, S[i].signature, S[i].n)) { + /* + * Signature match on enabled card? + */ + if (inb(s_base + 4) & 1) + return(S[i].type); + printk("aic7xxx disabled at slot %d, ignored\n", slot); + } + } + return(T_NONE); +} + + +static +void aic7xxx_loadram(int base) +{ + static unsigned char seqprog[] = { + /* + * Each sequencer instruction is 29 bits + * long (fill in the excess with zeroes) + * and has to be loaded from least -> most + * significant byte, so this table has the + * byte ordering reversed. + */ +# include "aic7xxx_seq.h" + }; + + /* + * When the AIC-7770 is paused (as on chip reset), the + * sequencer address can be altered and a sequencer + * program can be loaded by writing it, byte by byte, to + * the sequencer RAM port - the Adaptec documentation + * recommends using REP OUTSB to do this, hence the inline + * assembly. Since the address autoincrements as we load + * the program, reset it back to zero afterward. Disable + * sequencer RAM parity error detection while loading, and + * make sure the LOADRAM bit is enabled for loading. + */ + outb(PERRORDIS | SEQRESET | LOADRAM, O_SEQCTL(base)); + + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base)) + :"si", "cx", "dx"); + + /* + * WARNING! This is a magic sequence! After extensive + * experimentation, it seems that you MUST turn off the + * LOADRAM bit before you play with SEQADDR again, else + * you will end up with parity errors being flagged on + * your sequencer program. (You would also think that + * turning off LOADRAM and setting SEQRESET to reset the + * address to zero would work, but you need to do it twice + * for it to take effect on the address. Timing problem?) + */ + outb(SEQRESET | FASTMODE, O_SEQCTL(base)); + do { + /* + * Actually, reset it until + * the address shows up as + * zero just to be safe.. + */ + outb(SEQRESET | FASTMODE, O_SEQCTL(base)); + + } while (inb(O_SEQADDR0(base)) != 0 && inb(O_SEQADDR1(base)) != 0); +} + + +static void aic7xxx_delay (int seconds) +{ + int i; + + for (i = 0; i < (seconds << 11); i = i + 1) + udelay (488); +} + + +static +int aic7xxx_register (Scsi_Host_Template *template, + struct aic7xxx_host_config *config) +{ + static char * board_name[T_MAX] = {"", "274x", "284x", "294x"}; + int i, base; + unsigned char sblkctl; + int max_targets; + int found = 1; + unsigned char target_settings; + unsigned char scsi_conf; + struct Scsi_Host *host; + struct aic7xxx_host *p; + + base = config->base; + + /* + * Lock out other contenders for our i/o space. + */ + request_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base), "aic7xxx"); + + /* + * Read the bus type from the SBLKCTL register. Set the FLAGS + * register in the sequencer for twin and wide bus cards. + */ + sblkctl = inb(O_SBLKCTL(base)) & 0x3f; /* mask out upper two bits */ + switch (sblkctl) + { + case 0: /* narrow/normal bus */ + config->scsi_id = inb(HA_SCSICONF(base)) & 0x7; + config->bus_type = T_NORMAL; + outb(0, HA_FLAGS(base)); + break; + case 2: /* Wide bus */ + config->scsi_id = inb(HA_SCSICONF(base) + 1) & 0xf; + config->bus_type = T_WIDE; + printk("aic7xxx : Enabling wide channel of %s-Wide\n", + board_name[config->type]); + outb(WIDE_BUS, HA_FLAGS(base)); + break; + case 8: /* Twin bus */ + config->scsi_id = inb(HA_SCSICONF(base)) & 0x7; +#ifdef AIC7XXX_TWIN_SUPPORT + config->scsi_id_b = inb(HA_SCSICONF(base) + 1) & 0x7; + config->bus_type = T_TWIN; + printk("aic7xxx : Enabled channel B of %s-Twin\n", + board_name[config->type]); + outb(TWIN_BUS, HA_FLAGS(base)); +#else + config->bus_type = T_NORMAL; + printk("aic7xxx : Channel B of %s-Twin will be ignored\n", + board_name[config->type]); + outb(0, HA_FLAGS(base)); +#endif + break; + default: + printk("aic7xxx is an unsupported type 0x%x, please " + "mail dean@ims.com\n", inb(O_SBLKCTL(base))); + outb(0, HA_FLAGS(base)); + return(0); + } + /* + * Clear the upper two bits. For the 294X cards, clearing the + * upper two bits, will take the card out of diagnostic mode + * and make the host adatper LED follow bus activity (will not + * always be on). + */ + outb (sblkctl, O_SBLKCTL(base)); + + /* + * The IRQ level in i/o port 4 maps directly onto the real + * IRQ number. If it's ok, register it with the kernel. + * + * NB. the Adaptec documentation says the IRQ number is only + * in the lower four bits; the ECU information shows the + * high bit being used as well. Which is correct? + * + * The 294X cards (PCI) get their interrupt from PCI BIOS. + */ + if ((config->type != T_294X) + && (config->irq < 9 || config->irq > 15)) { + printk("aic7xxx uses unsupported IRQ level, ignoring\n"); + return(0); + } + + /* + * Print out debugging information before re-enabling + * the card - a lot of registers on it can't be read + * when the sequencer is active. + */ + debug_config(config); + + /* + * Before registry, make sure that the offsets of the + * struct scatterlist are what the sequencer will expect, + * otherwise disable scatter-gather altogether until someone + * can fix it. This is important since the sequencer will + * DMA elements of the SG array in while executing commands. + */ + if (template->sg_tablesize != SG_NONE) { + struct scatterlist sg; + + if (SG_STRUCT_CHECK(sg)) { + printk("aic7xxx warning: kernel scatter-gather " + "structures changed, disabling it\n"); + template->sg_tablesize = SG_NONE; + } + } + + /* + * Register each "host" and fill in the returned Scsi_Host + * structure as best we can. Some of the parameters aren't + * really relevant for bus types beyond ISA, and none of the + * high-level SCSI code looks at it anyway.. why are the fields + * there? Also save the pointer so that we can find the + * information when an IRQ is triggered. + * + */ + host = scsi_register(template, sizeof(struct aic7xxx_host)); + host->can_queue = config->maxscb; +#ifdef AIC7XXX_TAGGED_QUEUEING + host->cmd_per_lun = 2; +#endif + host->this_id = config->scsi_id; + host->irq = config->irq; + if (config->bus_type == T_WIDE) + host->max_id = 16; + if (config->bus_type == T_TWIN) + host->max_lun = 16; + + aic7xxx_boards[config->irq] = host; + p = (struct aic7xxx_host *)host->hostdata; + + /* Initialize the scb array by setting the state to free. */ + for (i = 0; i < AIC7XXX_MAXSCB; i = i + 1) + { + p->scb_array[i].state = SCB_FREE; + p->scb_array[i].next = NULL; + p->scb_array[i].cmd = NULL; + } + + p->a_scanned = 0; + p->b_scanned = 0; + p->base = config->base; + p->maxscb = config->maxscb; + p->numscb = 0; + p->extended = config->extended; + p->type = config->type; + p->bus_type = config->bus_type; + p->free_scb = NULL; + + /* + * The interrupt trigger is different depending on + * whether the card is EISA or VL-bus - sometimes. The + * startup variable will be cleared once the first + * command is queued, and is checked in the isr to try + * and detect when the interrupt type is set incorrectly, + * immediately triggering spurious interrupts. This is + * now just set on a per-card-type basis. + * + * This should not occur anymore because the interrupt + * type was taken from the BIOS settings. But we leave + * it this way until we know this is the case. We also + * allow for one interrupt without a command being sent, + * in case the host adapter gives one interrupt upon + * being enabled. A second interrupt will reverse the + * interrupt trigger. + */ + p->unpause = config->unpause; + p->pause = config->pause; + p->startup = 1; + + /* + * Register IRQ with the kernel _after_ the host information + * is set up, in case we take an interrupt right away, due to + * the interrupt type being set wrong. + */ + if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT, "aic7xxx")) { + printk("aic7xxx couldn't register irq %d, ignoring\n", + config->irq); + return(0); + } + + /* + * Load the sequencer program, then re-enable the board - + * resetting the AIC-7770 disables it, leaving the lights + * on with nobody home. On the PCI bus you *may* be home, + * but then your mailing address is dynamically assigned + * so no one can find you anyway :-) + */ + printk("aic7xxx: Downloading sequencer code.."); + aic7xxx_loadram(base); + + /* Set Fast Mode and Enable the board */ + outb (FASTMODE, O_SEQCTL(base)); + + if (p->type != T_294X) + outb(ENABLE, O_BCTL(base)); + + printk ("done.\n"); + + + /* Set the SCSI Id, SXFRCTL1, and SIMODE1, for both channels */ + if (p->bus_type == T_TWIN) + { + /* + * The device is gated to channel B after a chip reset, + * so set those values first. + */ + outb (config->scsi_id_b, O_SCSIID(base)); + scsi_conf = inb (HA_SCSICONF(base) + 1) & (ENSPCHK|STIMESEL); + scsi_conf = scsi_conf | ENSTIMER | ACTNEGEN | STPWEN; + outb(scsi_conf, O_SXFRCTL1(base)); + outb (ENSELTIMO|ENSCSIPERR, O_SIMODE1(base)); + /* Select Channel A */ + outb(0, O_SBLKCTL(base)); + } + outb (config->scsi_id, O_SCSIID(base)); + scsi_conf = inb (HA_SCSICONF(base)) & (ENSPCHK|STIMESEL); + outb (scsi_conf|ENSTIMER|ACTNEGEN|STPWEN, O_SXFRCTL1(base)); + outb (ENSELTIMO|ENSCSIPERR, O_SIMODE1(base)); + + /* Look at the information that board initialization or the board + * BIOS has left us. In the lower four bits of each target's + * scratch space any value other than 0 indicates that we should + * initiate synchronous transfers. If it's zero, the user or the + * BIOS has decided to disable synchronous negotiation to that + * target so we don't activate the needsdtr flag. + */ + p->needsdtr_copy = 0; + p->sdtr_pending = 0; + p->needwdtr_copy = 0; + p->wdtr_pending = 0; + if (p->bus_type == T_NORMAL) + max_targets = 8; + else + max_targets = 16; + for (i = 0; i < max_targets; i = i + 1) + { + target_settings = inb(HA_TARG_SCRATCH(base) + i); + if (target_settings & 0x0f) + { + p->needsdtr_copy = p->needsdtr_copy | (0x01 << i); + target_settings = target_settings | 0x0f; + } + /* + * If we are not wide, forget WDTR. This makes the driver + * work on some cards that don't leave these fields cleared + * when BIOS is not installed. + */ + if ((target_settings & 0x80) && (p->bus_type == T_WIDE)) + { + p->needwdtr_copy = p->needwdtr_copy | (0x01 << i); + target_settings = target_settings & 0x7f; + } + outb(target_settings, (HA_TARG_SCRATCH(base) + i)); + } + + p->needsdtr = p->needsdtr_copy; + p->needwdtr = p->needwdtr_copy; + printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr); + printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr); + + /* + * For reconnecting targets, the sequencer code needs to + * know how many SCBs it has to search through. + */ + outb(config->maxscb, HA_SCBCOUNT(base)); + + /* Clear the active flags - no targets are busy. */ + outb(0, HA_ACTIVE0(base)); + outb(0, HA_ACTIVE1(base)); + + /* + * Reset the SCSI bus. Is this necessary? + * To answer - No. My Plextor PX-43CH CD-ROM does not respond + * to a Test Unit Ready when a wide cable is installed with a + * a wide device and the reset is performed. If the wide cable + * is disconnected and Linux boots from a floppy or non-wide + * disk, then the CD-ROM does respond to the Test Unit Ready + * after a reset. The CD-ROM responds to the Test Unit Ready + * with and without the wide cable/disk drive if the reset is + * NOT performed. Many hours were spent trying to figure this + * one out. + * + * By delaying after the reset to let the devices settle, my + * CD-ROM does not hang. I have not looked into other methods + * of delaying. + */ + + if (!aic7xxx_no_reset) + { + printk ("Resetting the SCSI bus...\n"); + outb(SCSIRSTO, O_SCSISEQ(base)); + udelay(1000); + outb(0, O_SCSISEQ(base)); + aic7xxx_delay (15); + } + + /* + * Unpause the sequencer before returning and enable + * interrupts - we shouldn't get any until the first + * command is sent to us by the high-level SCSI code. + */ + + UNPAUSE_SEQUENCER(p); + return(found); +} + + +#ifdef AIC7XXX_PCI_SUPPORT +static +int aic7xxx_7870_register (Scsi_Host_Template *template, + unsigned char bus, + unsigned char device_fn, + unsigned char on_board) +{ + struct aic7xxx_host_config config; + int error; + unsigned long io_port; + unsigned char irq; + + config.type = T_294X; + config.on_board = on_board; + + /* + * Read esundry information from PCI BIOS. + */ + error = pcibios_read_config_dword(bus, + device_fn, + PCI_BASE_ADDRESS_0, + &io_port); + if (error) { + panic("aha294x_config: error 0x%x reading i/o port\n", + error); + } + + error = pcibios_read_config_byte(bus, + device_fn, + PCI_INTERRUPT_LINE, + &irq); + if (error) { + panic("aha294x_config: error 0x%x reading irq\n", + error); + } + + /* + * Make the base I/O register look like EISA and VL-bus. + */ + config.base = io_port - 0xc01; + +#if 1 + printk ("aic7xxx: aic7870 hcntrl=0x%x\n", inb(O_HCNTRL(config.base))); +#endif + /* + * Give the AIC-7XXX a reset before reading any of its + * its registers - this forces a pause of the sequencer + * and returns everything to default values. + * + * Use the BIOS settings to determine the interrupt + * trigger type (level or edge) and use this value + * for pausing and unpausing the sequencer. + * + * For some 274X boards, we must clear the CHIPRST bit + * and pause the sequencer. For some reason, this makes + * the driver work. + */ + config.unpause = (inb(O_HCNTRL(config.base)) & IRQMS) | INTEN; + config.pause = config.unpause | PAUSE; + outb(config.pause | CHIPRST, O_HCNTRL(config.base)); + aic7xxx_delay (1); + outb(config.pause, O_HCNTRL(config.base)); + + config.irq = irq; + config.maxscb = 16; + + /* + * XXX - these are values that I don't know how to query + * the hardware for, so for now the SCSI host ID is + * hardwired to 7, and the "extended translation" + * flag is taken from boot-time flags. + */ + config.scsi_id = 7; + config.extended = aic7xxx_extended; + + /* + * XXX - force data fifo threshold to 100%. Why does this + * need to be done? + */ +# define DFTHRESH 0xc0 + + outb(inb(O_DSPCISTATUS(config.base)) | DFTHRESH, O_DSPCISTATUS(config.base)); + outb(config.scsi_id | DFTHRESH, HA_SCSICONF(config.base)); + /* In case we are a wide card, place scsi ID in second conf byte. */ + outb(config.scsi_id, (HA_SCSICONF(config.base) + 1)); + + /* + * A reminder until this can be detected automatically. + */ + printk("aha294x: extended translation %sabled\n", + config.extended ? "en" : "dis"); + + /* + * Finish the setup and registration by calling the + * generic routine. + */ + return (aic7xxx_register (template, &config)); +} +#endif + + +static +int aic7xxx_7770_register (Scsi_Host_Template *template, + aha_type type, + int base) +{ + struct aic7xxx_host_config config; + unsigned char sblkctl_reg; + + config.type = type; + config.base = base; + +#if 1 + printk ("aic7xxx: aic7770 hcntrl=0x%x\n", inb(O_HCNTRL(config.base))); +#endif + /* + * Give the AIC-7XXX a reset before reading any of its + * its registers - this forces a pause of the sequencer + * and returns everything to default values. + * + * Use the BIOS settings to determine the interrupt + * trigger type (level or edge) and use this value + * for pausing and unpausing the sequencer. + * + * For some 274X boards, we must clear the CHIPRST bit + * and pause the sequencer. For some reason, this makes + * the driver work. + */ + config.unpause = (inb(O_HCNTRL(config.base)) & IRQMS) | INTEN; + config.pause = config.unpause | PAUSE; + outb(config.pause | CHIPRST, O_HCNTRL(config.base)); + aic7xxx_delay (1); + outb(config.pause, O_HCNTRL(config.base)); + + + /* Get the IRQ */ + config.irq = inb(HA_INTDEF(config.base)) & 0xf; + + /* + * A reminder: extended translation cannot yet be + * automatically detected. + */ + if (config.type == T_274X) + { + config.extended = aic7xxx_extended; + printk("aha274x: extended translation %sabled\n", + config.extended ? "en" : "dis"); + printk ("aha274x: using 0x%x for pausing of sequencer.\n", config.unpause); + } + else + { + config.extended = aic7xxx_extended; + printk("aha284x: extended translation %sabled\n", + config.extended ? "en" : "dis"); + printk ("aha284x: using 0x%x for pausing of sequencer.\n", config.unpause); + } + + /* + * Check for Rev C or E boards. Rev E boards can have + * 16 SCBs, while the Rev C boards are limited to 4 SCBs. + * + * The Rev E boards have a read/write autoflush bit in the + * SBLKCTL registor, while in the Rev C boards it is read only. + */ + sblkctl_reg = inb(O_SBLKCTL(config.base)) ^ AUTOFLUSHDIS; + outb(sblkctl_reg, O_SBLKCTL(config.base)); + if (inb(O_SBLKCTL(config.base)) == sblkctl_reg) + { /* We detected a Rev E board. */ + printk ("aic7770: Rev E and subsequent; using 16 SCBs :-)\n"); + outb (sblkctl_reg ^ AUTOFLUSHDIS, O_SBLKCTL(config.base)); + config.maxscb = 16; + } + else + { + printk ("aic7770: Rev C and previous; using 4 SCBs :-(\n"); + config.maxscb = 4; + } + /* + * Finish the setup and registration by calling the + * generic routine. + */ + return (aic7xxx_register (template, &config)); +} + + +int aic7xxx_detect(Scsi_Host_Template *template) +{ + aha_type type; + int found = 0, slot, base; + +/* This should be defined in pci.h */ +#define PCI_DEVICE_ID_ADAPTEC_2940_MB 0x7078 + + /* + * EISA/VL-bus card signature probe. + */ + for (slot = MINSLOT; slot <= MAXSLOT; slot++) { + + base = SLOTBASE(slot); + + if (check_region(O_MINREG(base), + O_MAXREG(base)-O_MINREG(base))) + { + /* + * Some other driver has staked a + * claim to this i/o region already. + */ + continue; + } + + type = aic7xxx_probe(slot, O_BIDx(base)); + + if (type != T_NONE) { + /* + * We "find" a 274x if we locate the card + * signature and we can set it up and register + * it with the kernel without incident. + */ + found += aic7xxx_7770_register(template, type, base); + } + } + +#ifdef AIC7XXX_PCI_SUPPORT + /* + * PCI-bus probe. + */ + if (pcibios_present()) { + /* Look for an on-board controller. */ + int index = 0; + unsigned char bus, device_fn; + + while (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC, + PCI_DEVICE_ID_ADAPTEC_2940_MB, + index, + &bus, + &device_fn)) + { + found += aic7xxx_7870_register(template, bus, + device_fn, 1); + index += 1; + } + /* Look for controller not on motherboard. */ + index = 0; + while (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC, + PCI_DEVICE_ID_ADAPTEC_2940, + index, + &bus, + &device_fn)) + { + found += aic7xxx_7870_register(template, bus, + device_fn, 0); + index += 1; + } + } +#endif + + template->name = (char *)aic7xxx_info(NULL); + return(found); +} + + +const char *aic7xxx_info(struct Scsi_Host *notused) +{ + return("Adaptec AHA274x/284x/294x (EISA/VL-bus/PCI -> Fast SCSI) " + AIC7XXX_SEQ_VERSION "/" + AIC7XXX_H_VERSION "/" + "1.34"); +} + + +static +void aic7xxx_buildscb(struct aic7xxx_host *p, + Scsi_Cmnd *cmd, + struct aic7xxx_scb *scb) +{ + void *addr; + unsigned length; + unsigned short mask; + + /* + * Setup the control byte if we need negotiation and have not + * already requested it. Ensure that we only do one negotiation + * at a time including tag enable. This way, if we get a reject + * message, we know which message was rejected. Also, it seems + * that for some reason, we cannot perform tag enable at the + * same time as wide or synchronous negotiation. + */ + + mask = (0x01 << cmd->target); + if ((p->needwdtr & mask) && !(p->wdtr_pending & mask)) + { + p->wdtr_pending = p->wdtr_pending | mask; + scb->control = scb->control | SCB_NEEDWDTR; +#if 0 + printk ("Sending WDTR request to target %d.\n", cmd->target); +#endif + } + else + { + if ((p->needsdtr & mask) && !(p->sdtr_pending & mask)) + { + p->sdtr_pending = p->sdtr_pending | mask; + scb->control = scb->control | SCB_NEEDSDTR; +#if 0 + printk ("Sending SDTR request to target %d.\n", cmd->target); +#endif + } + +#ifdef AIC7XXX_TAGGED_QUEUEING + else + { + if (cmd->device->tagged_queue) + { + cmd->device->current_tag = 1; /* enable tagging */ + cmd->tag = cmd->device->current_tag; + cmd->device->current_tag = cmd->device->current_tag + 1; + scb->control = scb->control | SCB_TE; + } + } +#endif + } + + +#if 0 + printk ("aic7xxx_queue: target %d, cmd 0x%x (size %u), wdtr 0x%x, mask 0x%x\n", + cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask); +#endif + /* + * NB. channel selection is taken from the + */ + scb->target_channel_lun = ((cmd->target << 4) & 0xf0) | (cmd->lun & 0xf); + + /* + * The interpretation of request_buffer and request_bufflen + * changes depending on whether or not use_sg is zero; a + * non-zero use_sg indicates the number of elements in the + * scatter-gather array. + * + * The AIC-7770 can't support transfers of any sort larger + * than 2^24 (three-byte count) without backflips. For what + * the kernel is doing, this shouldn't occur. I hope. + */ + length = aic7xxx_length(cmd, 0); + + if (length > 0xffffff) { + panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n"); + } + + /* + * XXX - this relies on the host data being stored in a + * little-endian format. + * + * I'm not sure how the scatterlist is setup and what is + * and what is not physical memory. I think that at least + * the address of the command needs to be converted to + * physical memory. If the scatterlist needs to be converted, + * look at the aic7xxx_length function. A copy of that with + * some slight modifications to store the scatterlist in + * the scb->dma_segs array should work. I leave this + * unimplemented as of now. + */ + + addr = cmd->cmnd; + scb->SCSI_cmd_length = cmd->cmd_len; + memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); + + if (cmd->use_sg) { +#if 0 + debug("aic7xxx_buildscb: SG used, %d segments, length %u\n", + cmd->use_sg, + length); +#endif + scb->SG_segment_count = cmd->use_sg; + memcpy(scb->SG_list_pointer, + &cmd->request_buffer, + sizeof(scb->SG_list_pointer)); + } else { + scb->SG_segment_count = 0; + memcpy(scb->data_pointer, + &cmd->request_buffer, + sizeof(scb->data_pointer)); + memcpy(scb->data_count, + &cmd->request_bufflen, + sizeof(scb->data_count)); + } +} + +int aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) +{ + long flags; + int old_scbptr; + int position; + struct aic7xxx_host *p; + struct aic7xxx_scb *scb; +#ifdef AIC7XXX_USE_DMA + void *addr; + unsigned char curscb; +#endif + + + p = (struct aic7xxx_host *)cmd->host->hostdata; + + /* Check to see if channel was scanned. */ + if (!p->a_scanned && (cmd->lun < 0x8)) + { + printk ("aic7xxx: Scanning channel A for devices.\n"); + p->a_scanned = 1; + } + else + { + if (!p->b_scanned && (cmd->lun & 0x8)) + { + printk ("aic7xxx: Scanning channel B for devices.\n"); + p->b_scanned = 1; + } + } + +#if 0 + if (p->startup) + debug("aic7xxx_queue: cmd 0x%x (size %u), target %d, channel %d, lun %d\n", + cmd->cmnd[0], + cmd->cmd_len, + cmd->target, + (cmd->lun & 0x8) >> 3, + cmd->lun & 0x7); +#endif + + /* + * This is a critical section, since we don't want the + * interrupt routine mucking with the host data or the + * card. Since the kernel documentation is vague on + * whether or not we are in a cli/sti pair already, save + * the flags to be on the safe side. + */ + save_flags(flags); + cli(); + + /* + * Clear the startup flag - we can now legitimately + * expect interrupts. + */ + p->startup = 0; + + /* + * Find a free slot in the SCB array to load this command + * into. Since can_queue is set to the maximum number of + * SCBs for the card, we should always find one. + * + * First try to find an scb in the free list. If there are + * none in the free list, then check the current number of + * of scbs and take an unused one from the scb array. + */ + scb = p->free_scb; + if (scb != NULL) + { /* found one in the free list */ + p->free_scb = scb->next; /* remove and update head of list */ + scb = &p->scb_array[scb->position]; + position = scb->position; /* preserve the position */ + memset(scb, 0, sizeof(*scb)); + scb->state = SCB_ACTIVE; + scb->position = position; + } + else + { + if (p->numscb >= p->maxscb) + panic ("aic7xxx_queue: couldn't find a free scb\n"); + else + { + /* + * Initialize the scb within the scb array. The + * position within the array is the position on + * the board that it will be loaded. + */ + scb = &(p->scb_array[p->numscb]); + memset(scb, 0, sizeof(*scb)); + scb->position = p->numscb; + p->numscb = p->numscb + 1; + scb->state = SCB_ACTIVE; +#ifdef AIC7XXX_USE_DMA + addr = &scb; + memcpy(scb->host_scb, &addr, sizeof(scb)); + scb->control = SCB_NEEDDMA; + PAUSE_SEQUENCER (p); + curscb = inb (O_SCBPTR(p->base)); + outb (scb->position, O_SCBPTR(p->base)); + aic7xxx_putdmascb (p->base, scb); + outb (curscb, O_SCBPTR(p->base)); + UNPAUSE_SEQUENCER (p); + scb->control = 0; +#endif + } + } + scb->cmd = cmd; + aic7xxx_position(cmd) = scb->position; + /* + * Construct the SCB beforehand, so the sequencer is + * paused a minimal amount of time. + */ + aic7xxx_buildscb(p, cmd, scb); + +#if 0 + if (scb != &p->scb_array[scb->position]) + printk ("aic7xxx_queue: address of scb by position does not match scb address\n"); + printk ("aic7xxx_queue: SCB pos=%d, cmdptr=0x%x, state=%d, freescb=0x%x\n", + scb->position, (unsigned int) scb->cmd, + scb->state, (unsigned int) p->free_scb); +#endif + /* + * Pause the sequencer so we can play with its registers - + * wait for it to acknowledge the pause. + * + * XXX - should the interrupts be left on while doing this? + */ + PAUSE_SEQUENCER(p); + + /* + * Save the SCB pointer and put our own pointer in - this + * selects one of the four banks of SCB registers. Load + * the SCB, then write its pointer into the queue in FIFO + * and restore the saved SCB pointer. + */ + old_scbptr = inb(O_SCBPTR(p->base)); + outb(scb->position, O_SCBPTR(p->base)); + + aic7xxx_putscb(p->base, scb); + + outb(scb->position, O_QINFIFO(p->base)); + outb(old_scbptr, O_SCBPTR(p->base)); + + /* + * Make sure the Scsi_Cmnd pointer is saved, the struct it + * points to is set up properly, and the parity error flag + * is reset, then unpause the sequencer and watch the fun + * begin. + */ + cmd->scsi_done = fn; + aic7xxx_parity(cmd) = DID_OK; + aic7xxx_status(cmd) = 0; + + UNPAUSE_SEQUENCER(p); + restore_flags(flags); + return(0); +} + +/* return values from aic7xxx_kill */ + +enum k_state { + k_ok, /* scb found and message sent */ + k_busy, /* message already present */ + k_absent, /* couldn't locate scb */ + k_disconnect, /* scb found, but disconnected */ +}; + +/* + * This must be called with interrupts disabled - it's going to + * be messing around with the host data, and an interrupt being + * fielded in the middle could get ugly. + * + * Since so much of the abort and reset code is shared, this + * function performs more magic than it really should. If the + * command completes ok, then it will call scsi_done with the + * result code passed in. The unpause parameter controls whether + * or not the sequencer gets unpaused - the reset function, for + * instance, may want to do something more aggressive. + * + * Note that the command is checked for in our SCB_array first + * before the sequencer is paused, so if k_absent is returned, + * then the sequencer is NOT paused. + */ + +static +enum k_state aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message, + unsigned int result, int unpause) +{ + struct aic7xxx_host *p; + struct aic7xxx_scb *scb; + int i, active_scb, found, queued; + unsigned char scbsave[AIC7XXX_MAXSCB]; + unsigned char flags; + enum k_state status; + + p = (struct aic7xxx_host *)cmd->host->hostdata; + scb = &p->scb_array[aic7xxx_position(cmd)]; + +#if 0 + printk ("aic7xxx_kill: In the kill function...\n"); +#endif + PAUSE_SEQUENCER(p); + + /* + * Case 1: In the QINFIFO + * + * This is the best case, really. Check to see if the + * command is still in the sequencer's input queue. If + * so, simply remove it. Reload the queue afterward. + */ + queued = inb(O_QINCNT(p->base)); + + for (i = found = 0; i < queued; i++) { + scbsave[i] = inb(O_QINFIFO(p->base)); + + if (scbsave[i] == scb->position) { + found = 1; + i -= 1; + } + } + + queued -= found; + for (i = 0; i < queued; i++) + outb(scbsave[i], O_QINFIFO(p->base)); + + if (found) + { + status = k_ok; + goto complete; + } + + active_scb = inb(O_SCBPTR(p->base)); + /* + * Case 2: Not the active command + * + * Check the current SCB bank. If it's not the one belonging + * to the command we want to kill, select the scb we want to + * abort and turn off the disconnected bit. The driver will + * then abort the command and notify us of the abort. + */ + if (active_scb != scb->position) + { + int scb_control; + outb (scb->position, O_SCBPTR(p->base)); + scb_control = inb (O_SCBARRAY(p->base)); + scb_control = scb_control & ~SCB_DIS; + outb (scb_control, O_SCBARRAY(p->base)); + outb (active_scb, O_SCBPTR(p->base)); + status = k_disconnect; + goto complete; + } + + /* + * Presumably at this point our target command is active. Check + * to see if there's a message already in effect. If not, place + * our message in and assert ATN so the target goes into MESSAGE + * OUT phase. + */ + flags = inb(HA_FLAGS(p->base)); + if (flags & ACTIVE_MSG) + { + /* + * If there is a message in progress, reset the bus + * and have all devices renegotiate. + */ + if (cmd->lun & 0x8) + { + p->needsdtr = p->needsdtr_copy & 0xff00; + p->sdtr_pending = p->sdtr_pending & 0x00ff; + outb (0, HA_ACTIVE1(p->base)); + } + else + { + if (p->bus_type == T_WIDE) + { + p->needsdtr = p->needsdtr_copy; + p->needwdtr = p->needwdtr_copy; + p->sdtr_pending = 0; + p->wdtr_pending = 0; + outb(0, HA_ACTIVE0(p->base)); + outb(0, HA_ACTIVE1(p->base)); + } + else + { + p->needsdtr = p->needsdtr_copy & 0x00ff; + p->sdtr_pending = p->sdtr_pending & 0xff00; + outb (0, HA_ACTIVE0(p->base)); + } + } + /* Reset the bus. */ + outb (SCSIRSTO, O_SCSISEQ(p->base)); + udelay(1000); + outb(0, O_SCSISEQ(p->base)); + aic7xxx_delay (15); + + status = k_busy; + goto complete; + } + + outb(flags | ACTIVE_MSG, HA_FLAGS(p->base)); /* active message */ + outb(1, HA_MSG_LEN(p->base)); /* length = 1 */ + outb(message, HA_MSG_START(p->base)); /* message body */ + + /* + * Assert ATN. Use the value of SCSISIGO saved by the + * sequencer code so we don't alter its contents radically + * in the middle of something critical. + */ + outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base)); + + status = k_ok; + + /* + * The command has been killed. Do the bookkeeping, unpause + * the sequencer, and notify the higher-level SCSI code. + */ +complete: + if (unpause) + UNPAUSE_SEQUENCER(p); + + /* + * Mark the scb as free and clear the scbs command pointer. + * Add the scb to the head of the free list being careful + * to preserve the next pointers. + */ + scb->state = SCB_FREE; /* mark the scb as free */ + scb->cmd = NULL; /* clear the command pointer */ + scb->next = p->free_scb; /* preserve next pointer */ + p->free_scb = scb; /* add at head of free list */ + cmd->result = cmd->result << 16; + cmd->scsi_done(cmd); + return(status); +} + +int aic7xxx_abort(Scsi_Cmnd *cmd) +{ + int rv; + long flags; + + save_flags(flags); + cli(); + + switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0)) { + case k_ok: rv = SCSI_ABORT_SUCCESS; break; + case k_busy: rv = SCSI_ABORT_BUSY; break; + case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break; + case k_disconnect: rv = SCSI_ABORT_SNOOZE; break; + default: + panic("aic7xxx_do_abort: internal error\n"); + } + + restore_flags(flags); + return(rv); +} + +/* + * Resetting the bus always succeeds - is has to, otherwise the + * kernel will panic! Try a surgical technique - sending a BUS + * DEVICE RESET message - on the offending target before pulling + * the SCSI bus reset line. + */ + +int aic7xxx_reset(Scsi_Cmnd *cmd) +{ + long flags; + struct aic7xxx_host *p; + + p = (struct aic7xxx_host *)cmd->host->hostdata; + save_flags(flags); + cli(); + + switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) { + + case k_ok: + /* + * The RESET message was sent to the target + * with no problems. Flag that target as + * needing a SDTR negotiation on the next + * connection and restart the sequencer. + */ + p->needsdtr = p->needsdtr & (1 << cmd->target); + UNPAUSE_SEQUENCER(p); + break; + + case k_absent: + /* + * The sequencer will not be paused if aic7xxx_kill() + * couldn't find the command. + */ + PAUSE_SEQUENCER(p); + /* falls through */ + + case k_busy: + cmd->result = DID_RESET << 16; /* return reset code */ + cmd->scsi_done(cmd); + break; + + case k_disconnect: + /* + * Do a hard reset of the SCSI bus. According to the + * SCSI-2 draft specification, reset has to be asserted + * for at least 25us. I'm invoking the kernel delay + * function for 30us since I'm not totally trusting of + * the busy loop timing. + * + * XXX - I'm not convinced this works. I tried resetting + * the bus before, trying to get the devices on the + * bus to revert to asynchronous transfer, and it + * never seemed to work. + */ + debug("aic7xxx: attempting to reset scsi bus and card\n"); + + outb(SCSIRSTO, O_SCSISEQ(p->base)); + udelay(1000); + outb(0, O_SCSISEQ(p->base)); + aic7xxx_delay (15); + + UNPAUSE_SEQUENCER(p); + + /* + * Locate the command and return a "reset" status + * for it. This is not completely correct and will + * probably return to haunt me later. + */ + cmd->result = DID_RESET << 16; /* return reset code */ + cmd->scsi_done(cmd); + break; + + default: + panic("aic7xxx_reset: internal error\n"); + } + + restore_flags(flags); + return(SCSI_RESET_SUCCESS); +} + +int aic7xxx_biosparam(Disk *disk, int devno, int geom[]) +{ + int heads, sectors, cylinders; + struct aic7xxx_host *p; + + p = (struct aic7xxx_host *)disk->device->host->hostdata; + + /* + * XXX - if I could portably find the card's configuration + * information, then this could be autodetected instead + * of left to a boot-time switch. + */ + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + if (p->extended && cylinders > 1024) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (255 * 63); + } + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return(0); +} + + diff -urN lx-1.2.8/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h --- lx-1.2.8/drivers/scsi/aic7xxx.h Wed Dec 31 19:00:00 1969 +++ linux/drivers/scsi/aic7xxx.h Fri May 12 10:04:54 1995 @@ -0,0 +1,63 @@ +/* @(#)aic7xxx.h 1.14 94/11/30 jda */ + +/* + * Adaptec 274x/284x/294x device driver for Linux. + * Copyright (c) 1994 The University of Calgary Department of Computer Science. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef aic7xxx_h +#define aic7xxx_h + +#define AIC7XXX_H_VERSION "1.14" + +/* + * Scsi_Host_Template (see hosts.h) for 274x - some fields + * to do with card config are filled in after the card is + * detected. + */ +#define AIC7XXX { \ + NULL, \ + NULL, \ + NULL, \ + aic7xxx_detect, \ + NULL, \ + aic7xxx_info, \ + NULL, \ + aic7xxx_queue, \ + aic7xxx_abort, \ + aic7xxx_reset, \ + NULL, \ + aic7xxx_biosparam, \ + -1, /* max simultaneous cmds */\ + -1, /* scsi id of host adapter */\ + SG_ALL, /* max scatter-gather cmds */\ + 2, /* cmds per lun (linked cmds) */\ + 0, /* number of 274x's present */\ + 0, /* no memory DMA restrictions */\ + ENABLE_CLUSTERING \ +} + +extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); +extern int aic7xxx_biosparam(Disk *, int, int[]); +extern int aic7xxx_detect(Scsi_Host_Template *); +extern int aic7xxx_command(Scsi_Cmnd *); +extern int aic7xxx_abort(Scsi_Cmnd *); +extern int aic7xxx_reset(Scsi_Cmnd *); + +extern const char *aic7xxx_info(struct Scsi_Host *); + +#endif diff -urN lx-1.2.8/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq --- lx-1.2.8/drivers/scsi/aic7xxx.seq Wed Dec 31 19:00:00 1969 +++ linux/drivers/scsi/aic7xxx.seq Fri May 12 10:04:54 1995 @@ -0,0 +1,1257 @@ +# @(#)aic7xxx.seq 1.32 94/11/29 jda +# +# Adaptec 274x/284x/294x device driver for Linux and FreeBSD. +# Copyright (c) 1994 The University of Calgary Department of Computer Science. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other +# optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org) +# +# $Id: aic7xxx.seq,v 1.9 1995/03/07 09:00:44 gibbs Exp $ + +VERSION AIC7XXX_SEQ_VERSION 1.8 + +SCBMASK = 0x1f + +SCSISEQ = 0x00 +SXFRCTL0 = 0x01 +SXFRCTL1 = 0x02 +SCSISIGI = 0x03 +SCSISIGO = 0x03 +SCSIRATE = 0x04 +SCSIID = 0x05 +SCSIDATL = 0x06 +STCNT = 0x08 +STCNT+0 = 0x08 +STCNT+1 = 0x09 +STCNT+2 = 0x0a +SSTAT0 = 0x0b +CLRSINT1 = 0x0c +SSTAT1 = 0x0c +SIMODE1 = 0x11 +SCSIBUSL = 0x12 +SHADDR = 0x14 +SELID = 0x19 +SBLKCTL = 0x1f +SEQCTL = 0x60 +A = 0x64 # == ACCUM +SINDEX = 0x65 +DINDEX = 0x66 +ALLZEROS = 0x6a +NONE = 0x6a +SINDIR = 0x6c +DINDIR = 0x6d +FUNCTION1 = 0x6e +HADDR = 0x88 +HCNT = 0x8c +HCNT+0 = 0x8c +HCNT+1 = 0x8d +HCNT+2 = 0x8e +SCBPTR = 0x90 +INTSTAT = 0x91 +DFCNTRL = 0x93 +DFSTATUS = 0x94 +DFDAT = 0x99 +QINFIFO = 0x9b +QINCNT = 0x9c +QOUTFIFO = 0x9d + +SCSICONF_A = 0x5a +SCSICONF_B = 0x5b + +# The two reserved bytes at SCBARRAY+1[23] are expected to be set to +# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag +# to indicate whether or not to reload scatter-gather parameters after +# a disconnect. We also use bits 6 & 7 to indicate whether or not to +# initiate SDTR or WDTR repectively when starting this command. +# +SCBARRAY+0 = 0xa0 + +DISCONNECTED = 0x04 +NEEDDMA = 0x08 +SG_LOAD = 0x10 +TAG_ENB = 0x20 +NEEDSDTR = 0x40 +NEEDWDTR = 0x80 + +SCBARRAY+1 = 0xa1 +SCBARRAY+2 = 0xa2 +SCBARRAY+3 = 0xa3 +SCBARRAY+4 = 0xa4 +SCBARRAY+5 = 0xa5 +SCBARRAY+6 = 0xa6 +SCBARRAY+7 = 0xa7 +SCBARRAY+8 = 0xa8 +SCBARRAY+9 = 0xa9 +SCBARRAY+10 = 0xaa +SCBARRAY+11 = 0xab +SCBARRAY+12 = 0xac +SCBARRAY+13 = 0xad +SCBARRAY+14 = 0xae +SCBARRAY+15 = 0xaf +SCBARRAY+16 = 0xb0 +SCBARRAY+17 = 0xb1 +SCBARRAY+18 = 0xb2 +SCBARRAY+19 = 0xb3 +SCBARRAY+20 = 0xb4 +SCBARRAY+21 = 0xb5 +SCBARRAY+22 = 0xb6 +SCBARRAY+23 = 0xb7 +SCBARRAY+24 = 0xb8 +SCBARRAY+25 = 0xb9 +SCBARRAY+26 = 0xba + +BAD_PHASE = 0x01 # unknown scsi bus phase +SEND_REJECT = 0x11 # sending a message reject +NO_IDENT = 0x21 # no IDENTIFY after reconnect +NO_MATCH = 0x31 # no cmd match for reconnect +MSG_SDTR = 0x41 # SDTR message recieved +MSG_WDTR = 0x51 # WDTR message recieved +MSG_REJECT = 0x61 # Reject message recieved +BAD_STATUS = 0x71 # Bad status from target + +# The host adapter card (at least the BIOS) uses 20-2f for SCSI +# device information, 32-33 and 5a-5f as well. As it turns out, the +# BIOS trashes 20-2f, writing the synchronous negotiation results +# on top of the BIOS values, so we re-use those for our per-target +# scratchspace (actually a value that can be copied directly into +# SCSIRATE). The kernel driver will enable synchronous negotiation +# for all targets that have a value other than 0 in the lower four +# bits of the target scratch space. This should work irregardless of +# whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top +# two bits of the SCB control byte. The kernel driver will set these +# when a WDTR or SDTR message should be sent to the target the SCB's +# command references. +# +# The high bit of DROPATN is set if ATN should be dropped before the ACK +# when outb is called. REJBYTE contains the first byte of a MESSAGE IN +# message, so the driver can report an intelligible error if a message is +# rejected. +# +# FLAGS's high bit is true if we are currently handling a reselect; +# its next-highest bit is true ONLY IF we've seen an IDENTIFY message +# from the reselecting target. If we haven't had IDENTIFY, then we have +# no idea what the lun is, and we can't select the right SCB register +# bank, so force a kernel panic if the target attempts a data in/out or +# command phase instead of corrupting something. +# +# Note that SG_NEXT occupies four bytes. +# +SYNCNEG = 0x20 + +DROPATN = 0x30 +REJBYTE = 0x31 +DISC_DSB_A = 0x32 +DISC_DSB_B = 0x33 + +MSG_LEN = 0x34 +MSG_START+0 = 0x35 +MSG_START+1 = 0x36 +MSG_START+2 = 0x37 +MSG_START+3 = 0x38 +MSG_START+4 = 0x39 +MSG_START+5 = 0x3a +-MSG_START+0 = 0xcb # 2's complement of MSG_START+0 + +ARG_1 = 0x4a # sdtr conversion args & return +BUS_16_BIT = 0x01 +RETURN_1 = 0x4a + +SIGSTATE = 0x4b # value written to SCSISIGO + +# Linux users should use 0xc (12) for SG_SIZEOF +#SG_SIZEOF = 0x8 # sizeof(struct ahc_dma) +SG_SIZEOF = 0xc # sizeof(struct scatterlist) +SCB_SIZEOF = 0x13 # sizeof SCB to DMA (19 bytes) + +SG_NOLOAD = 0x4c # load SG pointer/length? +SG_COUNT = 0x4d # working value of SG count +SG_NEXT = 0x4e # working value of SG pointer +SG_NEXT+0 = 0x4e +SG_NEXT+1 = 0x4f +SG_NEXT+2 = 0x50 +SG_NEXT+3 = 0x51 + +SCBCOUNT = 0x52 # the actual number of SCBs +FLAGS = 0x53 # Device configuration flags +TWIN_BUS = 0x01 +WIDE_BUS = 0x02 +SENSE = 0x10 +ACTIVE_MSG = 0x20 +IDENTIFY_SEEN = 0x40 +RESELECTED = 0x80 + +ACTIVE_A = 0x54 +ACTIVE_B = 0x55 +SAVED_TCL = 0x56 +# Poll QINCNT for work - the lower bits contain +# the number of entries in the Queue In FIFO. +# +start: + test FLAGS,SENSE jnz start_sense +start_nosense: + test FLAGS,TWIN_BUS jz start2 # Are we a twin channel device? +# For fairness, we check the other bus first, since we just finished a +# transaction on the current channel. + xor SBLKCTL,0x08 # Toggle to the other bus + test SCSISIGI,0x4 jnz reselect # BSYI + xor SBLKCTL,0x08 # Toggle to the original bus +start2: + test SCSISIGI,0x4 jnz reselect # BSYI + test QINCNT,SCBMASK jz start_nosense + +# We have at least one queued SCB now. Set the SCB pointer +# from the FIFO so we see the right bank of SCB registers, +# then set SCSI options and set the initiator and target +# SCSI IDs. +# + mov SCBPTR,QINFIFO + +# If the control byte of this SCB has the NEEDDMA flag set, we have +# yet to DMA it from host memory + +test SCBARRAY+0,NEEDDMA jz test_busy + clr HCNT+2 + clr HCNT+1 + mvi HCNT+0,SCB_SIZEOF + + mvi DINDEX,HADDR + mvi SCBARRAY+26 call bcopy_4 + + mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET + +# Wait for DMA from host memory to data FIFO to complete, then disable +# DMA and wait for it to acknowledge that it's off. +# +scb_load1: + test DFSTATUS,0x8 jz scb_load1 # HDONE + + clr DFCNTRL # disable DMA +scb_load2: + test DFCNTRL,0x8 jnz scb_load2 # HDMAENACK + +# Copy the SCB from the FIFO to the SCBARRAY + + mov SCBARRAY+0, DFDAT + mov SCBARRAY+1, DFDAT + mov SCBARRAY+2, DFDAT + mov SCBARRAY+3, DFDAT + mov SCBARRAY+4, DFDAT + mov SCBARRAY+5, DFDAT + mov SCBARRAY+6, DFDAT + mov SCBARRAY+7, DFDAT + mov SCBARRAY+8, DFDAT + mov SCBARRAY+9, DFDAT + mov SCBARRAY+10, DFDAT + mov SCBARRAY+11, DFDAT + mov SCBARRAY+12, DFDAT + mov SCBARRAY+13, DFDAT + mov SCBARRAY+14, DFDAT + mov SCBARRAY+15, DFDAT + mov SCBARRAY+16, DFDAT + mov SCBARRAY+17, DFDAT + mov SCBARRAY+18, DFDAT + +# See if there is not already an active SCB for this target. This code +# locks out on a per target basis instead of target/lun. Although this +# is not ideal for devices that have multiple luns active at the same +# time, it is faster than looping through all SCB's looking for active +# commands. It may be benificial to make findscb a more general procedure +# to see if the added cost of the search is negligible. This code also +# assumes that the kernel driver will clear the active flags on board +# initialization, board reset, and a target's SELTO. + +test_busy: + test SCBARRAY+0,0x20 jnz start_scb + and FUNCTION1,0x70,SCBARRAY+1 + mov A,FUNCTION1 + test SCBARRAY+1,0x88 jz test_a # Id < 8 && A channel + + test ACTIVE_B,A jnz requeue + or ACTIVE_B,A # Mark the current target as busy + jmp start_scb + +start_sense: +# Clear the SENSE flag first, then do a normal start_scb + and FLAGS,0xef + jmp start_scb + +# Place the currently active back on the queue for later processing +requeue: + mov QINFIFO, SCBPTR + jmp start_nosense + +test_a: + test ACTIVE_A,A jnz requeue + or ACTIVE_A,A # Mark the current target as busy + +start_scb: + or SCBARRAY+0,NEEDDMA + and SINDEX,0xf7,SBLKCTL #Clear the channel select bit + and A,0x08,SCBARRAY+1 #Get new channel bit + or SINDEX,A + mov SBLKCTL,SINDEX # select channel + mov SCBARRAY+1 call initialize + clr SG_NOLOAD + and FLAGS,0x3f # !RESELECTING + +# As soon as we get a successful selection, the target should go +# into the message out phase since we have ATN asserted. Prepare +# the message to send, locking out the device driver. If the device +# driver hasn't beaten us with an ABORT or RESET message, then tack +# on an SDTR negotiation if required. +# +# Messages are stored in scratch RAM starting with a flag byte (high bit +# set means active message), one length byte, and then the message itself. +# + mov SCBARRAY+1 call disconnect # disconnect ok? + + and SINDEX,0x7,SCBARRAY+1 # lun + or SINDEX,A # return value from disconnect + or SINDEX,0x80 call mk_mesg # IDENTIFY message + + mov A,SINDEX + test SCBARRAY+0,0xe0 jz !message # WDTR, SDTR or TAG?? + cmp MSG_START+0,A jne !message # did driver beat us? + +# Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag +# value + +mk_tag: + mvi DINDEX, MSG_START+1 + test SCBARRAY+0,TAG_ENB jz mk_tag_done + and A,0x23,SCBARRAY+0 + mov DINDIR,A + mov DINDIR,SCBPTR + + add MSG_LEN,-MSG_START+0,DINDEX # update message length + jmp !message # Can't do DTR when taged + +mk_tag_done: + + mov DINDEX call mk_dtr # build DTR message if needed + +!message: + +# Enable selection phase as an initiator, and do automatic ATN +# after the selection. +# + mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO + +# Wait for successful arbitration. The AIC-7770 documentation says +# that SELINGO indicates successful arbitration, and that it should +# be used to look for SELDO. However, if the sequencer is paused at +# just the right time - a parallel fsck(8) on two drives did it for +# me - then SELINGO can flip back to false before we've seen it. This +# makes the sequencer sit in the arbitration loop forever. This is +# Not Good. +# +# Therefore, I've added a check in the arbitration loop for SELDO +# too. This could arguably be made a critical section by disabling +# pauses, but I don't want to make a potentially infinite loop a CS. +# I suppose you could fold it into the select loop, too, but since +# I've been hunting this bug for four days it's kinda like a trophy. +# +arbitrate: + test SSTAT0,0x40 jnz *select # SELDO + test SSTAT0,0x10 jz arbitrate # SELINGO + +# Wait for a successful selection. If the hardware selection +# timer goes off, then the driver gets the interrupt, so we don't +# need to worry about it. +# +select: + test SSTAT0,0x40 jz select # SELDO + jmp *select + +# Reselection is being initiated by a target - we've seen the BSY +# line driven active, and we didn't do it! Enable the reselection +# hardware, and wait for it to finish. Make a note that we've been +# reselected, but haven't seen an IDENTIFY message from the target +# yet. +# +reselect: + mvi SCSISEQ,0x10 # ENRSELI + +reselect1: + test SSTAT0,0x20 jz reselect1 # SELDI + mov SELID call initialize + + and FLAGS,0x3f # reselected, no IDENTIFY + or FLAGS,RESELECTED + +# After the [re]selection, make sure that the [re]selection enable +# bit is off. This chip is flaky enough without extra things +# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be +# using it shortly. +# +*select: + clr SCSISEQ + mvi CLRSINT1,0x8 # CLRBUSFREE + +# Main loop for information transfer phases. If BSY is false, then +# we have a bus free condition, expected or not. Otherwise, wait +# for the target to assert REQ before checking MSG, C/D and I/O +# for the bus phase. +# +# We can't simply look at the values of SCSISIGI here (if we want +# to do synchronous data transfer), because the target won't assert +# REQ if it's already sent us some data that we haven't acknowledged +# yet. +# +ITloop: + test SSTAT1,0x8 jnz p_busfree # BUSFREE + test SSTAT1,0x1 jz ITloop # REQINIT + + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + + cmp ALLZEROS,A je p_dataout + cmp A,0x40 je p_datain + cmp A,0x80 je p_command + cmp A,0xc0 je p_status + cmp A,0xa0 je p_mesgout + cmp A,0xe0 je p_mesgin + + mvi INTSTAT,BAD_PHASE # unknown - signal driver + +p_dataout: + mvi 0 call scsisig # !CDO|!IOO|!MSGO + call assert + call sg_load + + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy_4 + +# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR + mvi SCBARRAY+23 call bcopy_3 + + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy_3 + + mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + +# After a DMA finishes, save the final transfer pointer and count +# back into the SCB, in case a device disconnects in the middle of +# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since +# it's a reflection of how many bytes were transferred on the SCSI +# (as opposed to the host) bus. +# + mvi DINDEX,SCBARRAY+23 + mvi STCNT call bcopy_3 + + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy_4 + + call sg_advance + mov SCBARRAY+18,SG_COUNT # residual S/G count + + jmp ITloop + +p_datain: + mvi 0x40 call scsisig # !CDO|IOO|!MSGO + call assert + call sg_load + + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy_4 + +# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR + mvi SCBARRAY+23 call bcopy_3 + + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy_3 + + mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN| + # !DIRECTION|FIFORESET + mvi DINDEX,SCBARRAY+23 + mvi STCNT call bcopy_3 + + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy_4 + + call sg_advance + mov SCBARRAY+18,SG_COUNT # residual S/G count + + jmp ITloop + +# Command phase. Set up the DMA registers and let 'er rip - the +# two bytes after the SCB SCSI_cmd_length are zeroed by the driver, +# so we can copy those three bytes directly into HCNT. +# +p_command: + mvi 0x80 call scsisig # CDO|!IOO|!MSGO + call assert + + mvi DINDEX,HADDR + mvi SCBARRAY+7 call bcopy_4 + +# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR + mvi SCBARRAY+11 call bcopy_3 + + mvi DINDEX,STCNT + mvi SCBARRAY+11 call bcopy_3 + + mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + jmp ITloop + +# Status phase. Wait for the data byte to appear, then read it +# and store it into the SCB. +# +p_status: + mvi 0xc0 call scsisig # CDO|IOO|!MSGO + + mvi SCBARRAY+14 call inb + jmp ITloop + +# Message out phase. If there is no active message, but the target +# took us into this phase anyway, build a no-op message and send it. +# +p_mesgout: + mvi 0xa0 call scsisig # CDO|!IOO|MSGO + mvi 0x8 call mk_mesg # build NOP message + +# Set up automatic PIO transfer from MSG_START. Bit 3 in +# SXFRCTL0 (SPIOEN) is already on. +# + mvi SINDEX,MSG_START+0 + mov DINDEX,MSG_LEN + clr A + +# When target asks for a byte, drop ATN if it's the last one in +# the message. Otherwise, keep going until the message is exhausted. +# (We can't use outb for this since it wants the input in SINDEX.) +# +# Keep an eye out for a phase change, in case the target issues +# a MESSAGE REJECT. +# +p_mesgout2: + test SSTAT0,0x2 jz p_mesgout2 # SPIORDY + test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS + + cmp DINDEX,1 jne p_mesgout3 # last byte? + mvi CLRSINT1,0x40 # CLRATNO - drop ATN + +# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically +# send ACKs in automatic PIO or DMA mode unless you make sure that the +# "expected" bus phase in SCSISIGO matches the actual bus phase. This +# behaviour is completely undocumented and caused me several days of +# grief. +# +# After plugging in different drives to test with and using a longer +# SCSI cable, I found that I/O in Automatic PIO mode ceased to function, +# especially when transferring >1 byte. It seems to be much more stable +# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is +# polled for transfer completion - for both output _and_ input. The +# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL +# is accessed (like the documentation says it does), and that on a longer +# cable run, the sequencer code was fast enough to loop back and see +# an SPIORDY that hadn't dropped yet. +# +p_mesgout3: + call one_stcnt + mov SCSIDATL,SINDIR + +p_mesgout4: + test SSTAT0,0x4 jz p_mesgout4 # SDONE + dec DINDEX + inc A + cmp MSG_LEN,A jne p_mesgout2 + +# If the next bus phase after ATN drops is a message out, it means +# that the target is requesting that the last message(s) be resent. +# +p_mesgout5: + test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE + test SSTAT1,0x1 jz p_mesgout5 # REQINIT + + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + cmp A,0xa0 jne p_mesgout6 + mvi 0x10 call scsisig # ATNO - re-assert ATN + + jmp ITloop + +p_mesgout6: + mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS + and FLAGS,0xdf # no active msg + jmp ITloop + +# Message in phase. Bytes are read using Automatic PIO mode, but not +# using inb. This alleviates a race condition, namely that if ATN had +# to be asserted under Automatic PIO mode, it had to beat the SCSI +# circuitry sending an ACK to the target. This showed up under heavy +# loads and really confused things, since ABORT commands wouldn't be +# seen by the drive after an IDENTIFY message in until it had changed +# to a data I/O phase. +# +p_mesgin: + mvi 0xe0 call scsisig # CDO|IOO|MSGO + mvi A call inb_first # read the 1st message byte + mvi REJBYTE,A # save it for the driver + + cmp ALLZEROS,A jne p_mesgin1 + +# We got a "command complete" message, so put the SCB pointer +# into the Queue Out, and trigger a completion interrupt. +# Check status for non zero return and interrupt driver if needed +# This allows the driver to interpret errors only when they occur +# instead of always uploading the scb. If the status is SCSI_CHECK, +# the driver will download a new scb requesting sense, to replace +# the old one and the sequencer code will imediately jump to start +# working on it. If the kernel driver does not wish to request sense, +# the sequencer program counter is incremented by 1, preventing another run +# on the current SCB and the command is allowed to complete. We don't +# bother to post to the QOUTFIFO in the error case since it would require +# extra work in the kernel driver to ensure that the entry was removed +# before the command complete code tried processing it. + + test SCBARRAY+14,0xff jz status_ok # 0 Status? + mvi INTSTAT,BAD_STATUS # let driver know + test FLAGS,SENSE jz status_ok + jmp p_mesgin_done + +status_ok: + +# First, mark this target as free. + test SCBARRAY+0,0x20 jnz complete # Tagged command + and FUNCTION1,0x70,SCBARRAY+1 + mov A,FUNCTION1 + test SCBARRAY+1,0x88 jz clear_a + xor ACTIVE_B,A + jmp complete + +clear_a: + xor ACTIVE_A,A + +complete: + mov QOUTFIFO,SCBPTR + mvi INTSTAT,0x02 # CMDCMPLT + jmp p_mesgin_done + +# Is it an extended message? We only support the synchronous and wide data +# transfer request messages, which will probably be in response to +# WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it - +# apparently this can be done after any message in byte, according +# to the SCSI-2 spec. +# +p_mesgin1: + cmp A,1 jne p_mesgin2 # extended message code? + + mvi ARG_1 call inb_next # extended message length + mvi A call inb_next # extended message code + + cmp A,1 je p_mesginSDTR # Syncronous negotiation message + cmp A,3 je p_mesginWDTR # Wide negotiation message + jmp p_mesginN + +p_mesginWDTR: + cmp ARG_1,2 jne p_mesginN # extended mesg length = 2 + mvi A call inb_next # Width of bus + mvi INTSTAT,MSG_WDTR # let driver know + test RETURN_1,0x80 jz p_mesgin_done# Do we need to send WDTR? + +# We didn't initiate the wide negotiation, so we must respond to the request + and RETURN_1,0x7f # Clear the SEND_WDTR Flag + or FLAGS,ACTIVE_MSG + mvi DINDEX,MSG_START+0 + mvi MSG_START+0 call mk_wdtr # build WDTR message + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig + jmp p_mesgin_done + +p_mesginSDTR: + cmp ARG_1,3 jne p_mesginN # extended mesg length = 3 + mvi ARG_1 call inb_next # xfer period + mvi A call inb_next # REQ/ACK offset + mvi INTSTAT,MSG_SDTR # call driver to convert + + test RETURN_1,0x80 jz p_mesgin_done# Do we need to mk_sdtr? + + or FLAGS,ACTIVE_MSG + mvi DINDEX, MSG_START+0 + mvi MSG_START+0 call mk_sdtr + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig + jmp p_mesgin_done + +# Is it a disconnect message? Set a flag in the SCB to remind us +# and await the bus going free. +# +p_mesgin2: + cmp A,4 jne p_mesgin3 # disconnect code? + + or SCBARRAY+0,0x4 # set "disconnected" bit + jmp p_mesgin_done + +# Save data pointers message? Copy working values into the SCB, +# usually in preparation for a disconnect. +# +p_mesgin3: + cmp A,2 jne p_mesgin4 # save data pointers code? + + call sg_ram2scb + jmp p_mesgin_done + +# Restore pointers message? Data pointers are recopied from the +# SCB anyway at the start of any DMA operation, so the only thing +# to copy is the scatter-gather values. +# +p_mesgin4: + cmp A,3 jne p_mesgin5 # restore pointers code? + + call sg_scb2ram + jmp p_mesgin_done + +# Identify message? For a reconnecting target, this tells us the lun +# that the reconnection is for - find the correct SCB and switch to it, +# clearing the "disconnected" bit so we don't "find" it by accident later. +# +p_mesgin5: + test A,0x80 jz p_mesgin6 # identify message? + + test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved + + and A,0x7 # lun in lower three bits + or SAVED_TCL,A,SELID + and SAVED_TCL,0xf7 + and A,0x08,SBLKCTL # B Channel?? + or SAVED_TCL,A + call inb_last # Ack + +# Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. +# If we get one, we use the tag returned to switch to the proper +# SCB. Otherwise, we just use the findSCB method. +p_mesgin5_loop: + test SSTAT1,0x8 jnz use_findSCB # BUSFREE + test SSTAT1,0x1 jz p_mesgin5_loop # REQINIT + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + cmp A,0xe0 jne use_findSCB # Still p_mesgin? + mvi A call inb_first + cmp A,0x20 je get_tag # Simple Tag message? +use_findSCB: + mov ALLZEROS call findSCB # Have to search + +# If a active message is present after calling findSCB, then either it +# or the driver is trying to abort the command. Either way, something +# untoward has happened and we should just leave it alone. +# +setup_SCB: + test FLAGS,ACTIVE_MSG jnz p_mesgin_done + + and SCBARRAY+0,0xfb # clear disconnect bit in SCB + or FLAGS,0xc0 # make note of IDENTIFY + + call sg_scb2ram # implied restore pointers + # required on reselect + jmp ITloop + +get_tag: + mvi A call inb_next + test A,0xf0 jnz abort_tag # Tag in range? + mov SCBPTR,A + mov A,SAVED_TCL + cmp SCBARRAY+1,A jne abort_tag + test SCBARRAY+0,TAG_ENB jz abort_tag + call inb_last + jmp setup_SCB + +# Message reject? Let the kernel driver handle this. If we have an +# outstanding WDTR or SDTR negotiation, assume that it's a response from +# the target selecting 8bit or asynchronous transfer, otherwise just ignore +# it since we have no clue what it pertains to. +# +p_mesgin6: + cmp A,7 jne p_mesgin7 # message reject code? + + mvi INTSTAT, MSG_REJECT + jmp p_mesgin_done + +# [ ADD MORE MESSAGE HANDLING HERE ] +# +p_mesgin7: + +# We have no idea what this message in is, and there's no way +# to pass it up to the kernel, so we issue a message reject and +# hope for the best. Since we're now using manual PIO mode to +# read in the message, there should no longer be a race condition +# present when we assert ATN. In any case, rejection should be a +# rare occurrence - signal the driver when it happens. +# +p_mesginN: + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig + mvi INTSTAT,SEND_REJECT # let driver know + + mvi 0x7 call mk_mesg # MESSAGE REJECT message + +p_mesgin_done: + call inb_last # ack & turn auto PIO back on + jmp ITloop + +abort_tag: + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig +# mvi INTSTAT,ABORT_TAG # let driver know + mvi 0xd call mk_mesg # ABORT TAG message + jmp p_mesgin_done + +# Bus free phase. It might be useful to interrupt the device +# driver if we aren't expecting this. For now, make sure that +# ATN isn't being asserted and look for a new command. +# +p_busfree: + mvi CLRSINT1,0x40 # CLRATNO + clr SIGSTATE + jmp start + +# Instead of a generic bcopy routine that requires an argument, we unroll +# the two cases that are actually used, and call them explicitly. This +# not only reduces the overhead of doing a bcopy by 2/3rds, but ends up +# saving space in the program since you don't have to put the argument +# into the accumulator before the call. Both functions expect DINDEX to +# contain the destination address and SINDEX to contain the source +# address. +bcopy_3: + mov DINDIR,SINDIR + mov DINDIR,SINDIR + mov DINDIR,SINDIR ret + +bcopy_4: + mov DINDIR,SINDIR + mov DINDIR,SINDIR + mov DINDIR,SINDIR + mov DINDIR,SINDIR ret + + +# Locking the driver out, build a one-byte message passed in SINDEX +# if there is no active message already. SINDEX is returned intact. +# +mk_mesg: + mvi SEQCTL,0x50 # PAUSEDIS|FASTMODE + test FLAGS,ACTIVE_MSG jnz mk_mesg1 # active message? + + or FLAGS,ACTIVE_MSG # if not, there is now + mvi MSG_LEN,1 # length = 1 + mov MSG_START+0,SINDEX # 1-byte message + +mk_mesg1: + mvi SEQCTL,0x10 ret # !PAUSEDIS|FASTMODE + +# Input byte in Automatic PIO mode. The address to store the byte +# in should be in SINDEX. DINDEX will be used by this routine. +# +inb: + test SSTAT0,0x2 jz inb # SPIORDY + mov DINDEX,SINDEX + call one_stcnt # xfer one byte + mov DINDIR,SCSIDATL +inb1: + test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish" + ret + +# Carefully read data in Automatic PIO mode. I first tried this using +# Manual PIO mode, but it gave me continual underrun errors, probably +# indicating that I did something wrong, but I feel more secure leaving +# Automatic PIO on all the time. +# +# According to Adaptec's documentation, an ACK is not sent on input from +# the target until SCSIDATL is read from. So we wait until SCSIDATL is +# latched (the usual way), then read the data byte directly off the bus +# using SCSIBUSL. When we have pulled the ATN line, or we just want to +# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI +# spec guarantees that the target will hold the data byte on the bus until +# we send our ACK. +# +# The assumption here is that these are called in a particular sequence, +# and that REQ is already set when inb_first is called. inb_{first,next} +# use the same calling convention as inb. +# +inb_first: + mov DINDEX,SINDEX + mov DINDIR,SCSIBUSL ret # read byte directly from bus + +inb_next: + mov DINDEX,SINDEX # save SINDEX + + call one_stcnt # xfer one byte + mov NONE,SCSIDATL # dummy read from latch to ACK +inb_next1: + test SSTAT0,0x4 jz inb_next1 # SDONE +inb_next2: + test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte + mov DINDIR,SCSIBUSL ret # read byte directly from bus + +inb_last: + call one_stcnt # ACK with dummy read + mov NONE,SCSIDATL +inb_last1: + test SSTAT0,0x4 jz inb_last1 # wait for completion + ret + +# Output byte in Automatic PIO mode. The byte to output should be +# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped +# before the byte is output. +# +outb: + test SSTAT0,0x2 jz outb # SPIORDY + call one_stcnt # xfer one byte + + test DROPATN,0x80 jz outb1 + mvi CLRSINT1,0x40 # CLRATNO + clr DROPATN +outb1: + mov SCSIDATL,SINDEX +outb2: + test SSTAT0,0x4 jz outb2 # SDONE + ret + +# Write the value "1" into the STCNT registers, for Automatic PIO +# transfers. +# +one_stcnt: + clr STCNT+2 + clr STCNT+1 + mvi STCNT+0,1 ret + +# DMA data transfer. HADDR and HCNT must be loaded first, and +# SINDEX should contain the value to load DFCNTRL with - 0x3d for +# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared +# during initialization. +# +dma: + mov DFCNTRL,SINDEX +dma1: +dma2: + test SSTAT0,0x1 jnz dma3 # DMADONE + test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun + +# We will be "done" DMAing when the transfer count goes to zero, or +# the target changes the phase (in light of this, it makes sense that +# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are +# doing a SCSI->Host transfer, the data FIFO should be flushed auto- +# magically on STCNT=0 or a phase change, so just wait for FIFO empty +# status. +# +dma3: + test SINDEX,0x4 jnz dma5 # DIRECTION +dma4: + test DFSTATUS,0x1 jz dma4 # !FIFOEMP + +# Now shut the DMA enables off, and copy STCNT (ie. the underrun +# amount, if any) to the SCB registers; SG_COUNT will get copied to +# the SCB's residual S/G count field after sg_advance is called. Make +# sure that the DMA enables are actually off first lest we get an ILLSADDR. +# +dma5: + clr DFCNTRL # disable DMA +dma6: + test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK + + mvi DINDEX,SCBARRAY+15 + mvi STCNT call bcopy_3 + + ret + +# Common SCSI initialization for selection and reselection. Expects +# the target SCSI ID to be in the upper four bits of SINDEX, and A's +# contents are stomped on return. +# +initialize: + and SINDEX,0xf0 # Get target ID + and A,0x0f,SCSIID + or SINDEX,A + mov SCSIID,SINDEX + +# Esundry initialization. +# + clr DROPATN + clr SIGSTATE + +# Turn on Automatic PIO mode now, before we expect to see a REQ +# from the target. It shouldn't hurt anything to leave it on. Set +# CLRCHN here before the target has entered a data transfer mode - +# with synchronous SCSI, if you do it later, you blow away some +# data in the SCSI FIFO that the target has already sent to you. +# + mvi SXFRCTL0,0x8a # DFON|SPIOEN|CLRCHN + +# Initialize scatter-gather pointers by setting up the working copy +# in scratch RAM. +# + call sg_scb2ram + +# Initialize SCSIRATE with the appropriate value for this target. +# + call ndx_dtr + mov SCSIRATE,SINDIR ret + +# Assert that if we've been reselected, then we've seen an IDENTIFY +# message. +# +assert: + test FLAGS,RESELECTED jz assert1 # reselected? + test FLAGS,IDENTIFY_SEEN jnz assert1 # seen IDENTIFY? + + mvi INTSTAT,NO_IDENT # no - cause a kernel panic + +assert1: + ret + +# Find out if disconnection is ok from the information the BIOS has left +# us. The tcl from SCBARRAY+1 should be in SINDEX; A will +# contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok) +# on exit. +# +# To allow for wide or twin busses, we check the upper bit of the target ID +# and the channel ID and look at the appropriate disconnect register. +# +disconnect: + and FUNCTION1,0x70,SINDEX # strip off extra just in case + mov A,FUNCTION1 + test SINDEX, 0x88 jz disconnect_a + + test DISC_DSB_B,A jz disconnect1 # bit nonzero if DISabled + clr A ret + +disconnect_a: + test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled + clr A ret + +disconnect1: + mvi A,0x40 ret + +# Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch +# the SCB to it. Have the kernel print a warning message if it can't be +# found, and generate an ABORT message to the target. SINDEX should be +# cleared on call. +# +findSCB: + mov A,SAVED_TCL + mov SCBPTR,SINDEX # switch to new SCB + cmp SCBARRAY+1,A jne findSCB1 # target ID/channel/lun match? + test SCBARRAY+0,0x4 jz findSCB1 # should be disconnected + ret + +findSCB1: + inc SINDEX + mov A,SCBCOUNT + cmp SINDEX,A jne findSCB + + mvi INTSTAT,NO_MATCH # not found - signal kernel + mvi 0x6 call mk_mesg # ABORT message + + or SINDEX,0x10,SIGSTATE # assert ATNO + call scsisig + ret + +# Make a working copy of the scatter-gather parameters in the SCB. +# +sg_scb2ram: + mov SG_COUNT,SCBARRAY+2 + + mvi DINDEX,SG_NEXT + mvi SCBARRAY+3 call bcopy_4 + + mvi SG_NOLOAD,0x80 + test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g? + clr SG_NOLOAD + +sg_scb2ram1: + ret + +# Copying RAM values back to SCB, for Save Data Pointers message. +# +sg_ram2scb: + mov SCBARRAY+2,SG_COUNT + + mvi DINDEX,SCBARRAY+3 + mvi SG_NEXT call bcopy_4 + + and SCBARRAY+0,0xef,SCBARRAY+0 + test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g? + or SCBARRAY+0,SG_LOAD + +sg_ram2scb1: + ret + +# Load a struct scatter if needed and set up the data address and +# length. If the working value of the SG count is nonzero, then +# we need to load a new set of values. +# +# This, like the above DMA, assumes a little-endian host data storage. +# +sg_load: + test SG_COUNT,0xff jz sg_load3 # SG being used? + test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g? + + clr HCNT+2 + clr HCNT+1 + mvi HCNT+0,SG_SIZEOF + + mvi DINDEX,HADDR + mvi SG_NEXT call bcopy_4 + + mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET + +# Wait for DMA from host memory to data FIFO to complete, then disable +# DMA and wait for it to acknowledge that it's off. +# +sg_load1: + test DFSTATUS,0x8 jz sg_load1 # HDONE + + clr DFCNTRL # disable DMA +sg_load2: + test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK + +# Copy data from FIFO into SCB data pointer and data count. This assumes +# that the struct scatterlist has this structure (this and sizeof(struct +# scatterlist) == 12 are asserted in aic7xxx.c): +# +# struct scatterlist { +# char *address; /* four bytes, little-endian order */ +# ... /* four bytes, ignored */ +# unsigned short length; /* two bytes, little-endian order */ +# } +# + +# Not in FreeBSD. the scatter list is only 8 bytes. +# +# struct ahc_dma_seg { +# physaddr addr; /* four bytes, little-endian order */ +# long len; /* four bytes, little endian order */ +# }; +# + + mov SCBARRAY+19,DFDAT # new data address + mov SCBARRAY+20,DFDAT + mov SCBARRAY+21,DFDAT + mov SCBARRAY+22,DFDAT + +# For Linux, we must throw away four bytes since there is a 32bit gap +# in the middle of a struct scatterlist + mov NONE,DFDAT + mov NONE,DFDAT + mov NONE,DFDAT + mov NONE,DFDAT + + mov SCBARRAY+23,DFDAT + mov SCBARRAY+24,DFDAT + mov SCBARRAY+25,DFDAT #Only support 24 bit length. + +sg_load3: + ret + +# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled, +# and the SCSI transfer count is zero (note that this should be called +# right after a DMA finishes), then move the working copies of the SG +# pointer/length along. If the SCSI transfer count is not zero, then +# presumably the target is disconnecting - do not reload the SG values +# next time. +# +sg_advance: + test SG_COUNT,0xff jz sg_advance2 # s/g enabled? + + test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero? + test STCNT+1,0xff jnz sg_advance1 + test STCNT+2,0xff jnz sg_advance1 + + clr SG_NOLOAD # reload s/g next time + dec SG_COUNT # one less segment to go + + clr A # add sizeof(struct scatter) + add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 + adc SG_NEXT+1,A,SG_NEXT+1 + adc SG_NEXT+2,A,SG_NEXT+2 + adc SG_NEXT+3,A,SG_NEXT+3 ret + +sg_advance1: + mvi SG_NOLOAD,0x80 # don't reload s/g next time +sg_advance2: + ret + +# Add the array base SYNCNEG to the target offset (the target address +# is in SCSIID), and return the result in SINDEX. The accumulator +# contains the 3->8 decoding of the target ID on return. +# +ndx_dtr: + shr A,SCSIID,4 + test SBLKCTL,0x08 jz ndx_dtr_2 + or A,0x08 # Channel B entries add 8 +ndx_dtr_2: + add SINDEX,SYNCNEG,A + + and FUNCTION1,0x70,SCSIID # 3-bit target address decode + mov A,FUNCTION1 ret + +# If we need to negotiate transfer parameters, build the WDTR or SDTR message +# starting at the address passed in SINDEX. DINDEX is modified on return. +# The SCSI-II spec requires that Wide negotiation occur first and you can +# only negotiat one or the other at a time otherwise in the event of a message +# reject, you wouldn't be able to tell which message was the culpret. +# +mk_dtr: + mov DINDEX,SINDEX # save SINDEX + + test SCBARRAY+0,NEEDWDTR jnz mk_wdtr_16bit + jmp mk_sdtr + +mk_wdtr_16bit: + mvi ARG_1,BUS_16_BIT +mk_wdtr: + mvi DINDIR,1 # extended message + mvi DINDIR,2 # extended message length = 2 + mvi DINDIR,3 # WDTR code + mov DINDIR,ARG_1 # bus width + + add MSG_LEN,-MSG_START+0,DINDEX ret # update message length + +mk_sdtr: + mvi DINDIR,1 # extended message + mvi DINDIR,3 # extended message length = 3 + mvi DINDIR,1 # SDTR code + call sdtr_to_rate + mov DINDIR,RETURN_1 # REQ/ACK transfer period + and DINDIR,0xf,SINDIR # Sync Offset + + add MSG_LEN,-MSG_START+0,DINDEX ret # update message length + +# Set SCSI bus control signal state. This also saves the last-written +# value into a location where the higher-level driver can read it - if +# it has to send an ABORT or RESET message, then it needs to know this +# so it can assert ATN without upsetting SCSISIGO. The new value is +# expected in SINDEX. Change the actual state last to avoid contention +# from the driver. +# +scsisig: + mov SIGSTATE,SINDEX + mov SCSISIGO,SINDEX ret + +sdtr_to_rate: + call ndx_dtr # index scratch space for target + shr A,SINDIR,0x4 + dec SINDEX #Preserve SINDEX + and A,0x7 + clr RETURN_1 +sdtr_to_rate_loop: + test A,0x0f jz sdtr_to_rate_done + add RETURN_1,0x18 + dec A + jmp sdtr_to_rate_loop +sdtr_to_rate_done: + shr RETURN_1,0x2 + add RETURN_1,0x18 ret diff -urN lx-1.2.8/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- lx-1.2.8/drivers/scsi/hosts.c Sun Feb 19 04:33:14 1995 +++ linux/drivers/scsi/hosts.c Fri May 12 10:04:54 1995 @@ -44,8 +44,8 @@ #include "aha1740.h" #endif -#ifdef CONFIG_SCSI_AHA274X -#include "aha274x.h" +#ifdef CONFIG_SCSI_AIC7XXX +#include "aic7xxx.h" #endif #ifdef CONFIG_SCSI_BUSLOGIC @@ -160,8 +160,8 @@ #ifdef CONFIG_SCSI_AHA1740 AHA1740, #endif -#ifdef CONFIG_SCSI_AHA274X - AHA274X, +#ifdef CONFIG_SCSI_AIC7XXX + AIC7XXX, #endif #ifdef CONFIG_SCSI_FUTURE_DOMAIN FDOMAIN_16X0, @@ -255,6 +255,8 @@ retval->last_reset = 0; retval->irq = 0; retval->dma_channel = 0xff; + retval->max_id = 8; + retval->max_lun = 8; retval->io_port = 0; retval->forbidden_addr = 0; retval->forbidden_size = 0; diff -urN lx-1.2.8/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h --- lx-1.2.8/drivers/scsi/hosts.h Sun Feb 19 04:42:34 1995 +++ linux/drivers/scsi/hosts.h Fri May 12 10:04:54 1995 @@ -239,6 +239,16 @@ Scsi_Cmnd *host_queue; Scsi_Host_Template * hostt; + /* + * These two parameters can be used to allow for wide scsi, + * and for host adapters that support multiple busses (by + * mapping 8-15 to the second bus, for example). + * These should be set to 1 more than the actual max id + * or lun (i.e. 8 for normal systems). + */ + unsigned int max_id; + unsigned int max_lun; + /* Pointer to a circularly linked list - this indicates the hosts that should be locked out of performing I/O while we have an active command on this host. */ diff -urN lx-1.2.8/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- lx-1.2.8/drivers/scsi/scsi.c Wed May 3 05:56:30 1995 +++ linux/drivers/scsi/scsi.c Fri May 12 10:04:54 1995 @@ -44,7 +44,6 @@ #define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__)) static void scsi_done (Scsi_Cmnd *SCpnt); -static int update_timeout (Scsi_Cmnd *, int); static void print_inquiry(unsigned char *data); static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid); @@ -94,10 +93,8 @@ #define WAS_RESET 0x01 #define WAS_TIMEDOUT 0x02 -#define WAS_SENSE 0x04 #define IS_RESETTING 0x08 #define IS_ABORTING 0x10 -#define ASKED_FOR_SENSE 0x20 /* * This is the number of clock ticks we should wait before we time out @@ -121,11 +118,9 @@ #endif #ifdef DEBUG - #define SENSE_TIMEOUT SCSI_TIMEOUT #define ABORT_TIMEOUT SCSI_TIMEOUT #define RESET_TIMEOUT SCSI_TIMEOUT #else - #define SENSE_TIMEOUT (5*HZ/10) #define RESET_TIMEOUT (5*HZ/10) #define ABORT_TIMEOUT (5*HZ/10) #endif @@ -303,6 +298,7 @@ void scan_scsis (struct Scsi_Host * shpnt) { int dev, lun, type; + int channel = 0; unsigned char scsi_cmd [12]; unsigned char scsi_result0 [256]; unsigned char * scsi_result; @@ -326,8 +322,11 @@ shpnt->host_queue = SCpnt; /* We need this so that commands can time out */ - for (dev = 0; dev < 8; ++dev) - if (shpnt->this_id != dev) + for (channel = 0; channel < (shpnt->max_lun >> 3); channel = channel + 1) + { + + for (dev = 0; dev < 8; ++dev) + if (shpnt->this_id != dev) /* * We need the for so our continue, etc. work fine. @@ -337,7 +336,7 @@ memset(SDpnt, 0, sizeof(Scsi_Device)); SDpnt->host = shpnt; SDpnt->id = dev; - SDpnt->lun = lun; + SDpnt->lun = lun + (channel << 3); /* Some low level driver could use device->type (DB) */ SDpnt->type = -1; @@ -616,6 +615,7 @@ } } /* if result == DID_OK ends */ } /* for lun ends */ + } /* for channel ends */ shpnt->host_queue = NULL; /* No longer needed here */ @@ -1770,7 +1770,7 @@ set the timer, we want to take this value into account. */ -static int update_timeout(Scsi_Cmnd * SCset, int timeout) +extern int update_timeout(Scsi_Cmnd * SCset, int timeout) { unsigned int least, used; unsigned int oldto; diff -urN lx-1.2.8/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- lx-1.2.8/drivers/scsi/scsi.h Tue Apr 11 00:56:25 1995 +++ linux/drivers/scsi/scsi.h Fri May 12 10:04:54 1995 @@ -267,6 +267,31 @@ #define SCSI_2 3 /* + * ------------------------------------------------------------- + * Define sense constants and prototypes necessary for drivers + * that automatically request sense. + * ------------------------------------------------------------- + */ + +#ifdef DEBUG + #define SENSE_TIMEOUT 500 +#else + #define SENSE_TIMEOUT 50 +#endif +/* + * Define values applicable to autosensing for the flags + * component of the Scsi_Cmnd structure. + */ +#define WAS_SENSE 0x04 +#define ASKED_FOR_SENSE 0x20 + +/* + * ---------------------------------------------------------- + * - End of autosense definitions. + * ---------------------------------------------------------- + */ + +/* Every SCSI command starts with a one byte OP-code. The next byte's high three bits are the LUN of the device. Any multi-byte quantities are stored high byte @@ -539,6 +564,13 @@ extern int scsi_reset (Scsi_Cmnd *); extern int max_scsi_hosts; + +/* + * Define a prototype for the function that updates a Scsi_Cmnd + * timeout value. An autosensing driver should call this function + * when requesting sense so the Scsi_Cmnd does not timeout. + */ +extern int update_timeout (Scsi_Cmnd *, int); #if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR) #include "hosts.h" diff -urN lx-1.2.8/init/main.c linux/init/main.c --- lx-1.2.8/init/main.c Fri Feb 24 09:41:01 1995 +++ linux/init/main.c Fri May 12 10:04:54 1995 @@ -64,7 +64,7 @@ extern void generic_NCR5380_setup(char *str, int *intr); extern void aha152x_setup(char *str, int *ints); extern void aha1542_setup(char *str, int *ints); -extern void aha274x_setup(char *str, int *ints); +extern void aic7xxx_setup(char *str, int *ints); extern void buslogic_setup(char *str, int *ints); extern void scsi_luns_setup(char *str, int *ints); extern void sound_setup(char *str, int *ints); @@ -176,8 +176,8 @@ #ifdef CONFIG_SCSI_AHA1542 { "aha1542=", aha1542_setup}, #endif -#ifdef CONFIG_SCSI_AHA274X - { "aha274x=", aha274x_setup}, +#ifdef CONFIG_SCSI_AIC7XXX + { "aic7xxx=", aic7xxx_setup}, #endif #ifdef CONFIG_SCSI_BUSLOGIC { "buslogic=", buslogic_setup},