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}, A much more important factor in the social movement than those already mentioned was the ever-increasing influence of women. This probably stood at the lowest point to which it has ever fallen, during the classic age of Greek life and thought. In the history of Thucydides, so far as it forms a connected series of events, four times only during a period of nearly seventy years does a woman cross the scene. In each instance her apparition only lasts for a moment. In three of the four instances she is a queen or a princess, and belongs either to the half-barbarous kingdoms of northern Hellas or to wholly barbarous Thrace. In the one remaining instance208¡ª that of the woman who helps some of the trapped Thebans to make their escape from Plataea¡ªwhile her deed of mercy will live for ever, her name is for ever lost.319 But no sooner did philosophy abandon physics for ethics and religion than the importance of those subjects to women was perceived, first by Socrates, and after him by Xenophon and Plato. Women are said to have attended Plato¡¯s lectures disguised as men. Women formed part of the circle which gathered round Epicurus in his suburban retreat. Others aspired not only to learn but to teach. Ar¨ºt¨º, the daughter of Aristippus, handed on the Cyrenaic doctrine to her son, the younger Aristippus. Hipparchia, the wife of Crates the Cynic, earned a place among the representatives of his school. But all these were exceptions; some of them belonged to the class of Hetaerae; and philosophy, although it might address itself to them, remained unaffected by their influence. The case was widely different in Rome, where women were far more highly honoured than in Greece;320 and even if the prominent part assigned to them in the legendary history of the city be a proof, among others, of its untrustworthiness, still that such stories should be thought worth inventing and preserving is an indirect proof of the extent to which feminine influence prevailed. With the loss of political liberty, their importance, as always happens at such a conjuncture, was considerably increased. Under a personal government there is far more scope for intrigue than where law is king; and as intriguers women are at least the209 equals of men. Moreover, they profited fully by the levelling tendencies of the age. One great service of the imperial jurisconsults was to remove some of the disabilities under which women formerly suffered. According to the old law, they were placed under male guardianship through their whole life, but this restraint was first reduced to a legal fiction by compelling the guardian to do what they wished, and at last it was entirely abolished. Their powers both of inheritance and bequest were extended; they frequently possessed immense wealth; and their wealth was sometimes expended for purposes of public munificence. Their social freedom seems to have been unlimited, and they formed combinations among themselves which probably served to increase their general influence.321 The old religions of Greece and Italy were essentially oracular. While inculcating the existence of supernatural beings, and prescribing the modes according to which such beings were to be worshipped, they paid most attention to the interpretation of the signs by which either future events in general, or the consequences of particular actions, were supposed to be divinely revealed. Of these intimations, some were given to the whole world, so that he who ran might read, others were reserved for certain favoured localities, and only communicated through the appointed ministers of the god. The Delphic oracle in particular enjoyed an enormous reputation both among Greeks and barbarians for guidance afforded under the latter conditions; and during a considerable period it may even be said to have directed the course of Hellenic civilisation. It was also under this form that supernatural religion suffered most injury from the great intellectual movement which followed the Persian wars. Men who had learned to study the constant sequences of Nature for themselves, and to shape their conduct according to fixed principles of prudence or of justice, either thought it irreverent to trouble the god about questions on which they were competent to form an opinion for themselves, or did not choose to place a well-considered scheme at the mercy of his possibly interested responses. That such a revolution occurred about the middle of the fifth century B.C., seems proved by the great change of tone in reference to this subject which one perceives on passing from Aeschylus to Sophocles. That anyone should question the veracity of an oracle is a supposition which never crosses the mind of the elder dramatist. A knowledge of augury counts among the greatest benefits222 conferred by Prometheus on mankind, and the Titan brings Zeus himself to terms by his acquaintance with the secrets of destiny. Sophocles, on the other hand, evidently has to deal with a sceptical generation, despising prophecies and needing to be warned of the fearful consequences brought about by neglecting their injunctions. The stranger had a pleasant, round face, with eyes that twinkled in spite of the creases around them that showed worry. No wonder he was worried, Sandy thought: having deserted the craft they had foiled in its attempt to get the gems, the man had returned from some short foray to discover his craft replaced by another. ¡°Thanks,¡± Dick retorted, without smiling. When they reached him, in the dying glow of the flashlight Dick trained on a body lying in a heap, they identified the man who had been warned by his gypsy fortune teller to ¡°look out for a hidden enemy.¡± He was lying at full length in the mould and leaves. "But that is sport," she answered carelessly. On the retirement of Townshend, Walpole reigned supreme and without a rival in the Cabinet. Henry Pelham was made Secretary at War; Compton Earl of Wilmington Privy Seal. He left foreign affairs chiefly to Stanhope, now Lord Harrington, and to the Duke of Newcastle, impressing on them by all means to avoid quarrels with foreign Powers, and maintain the blessings of peace. With all the faults of Walpole, this was the praise of his political system, which system, on the meeting of Parliament in the spring of 1731, was violently attacked by Wyndham and Pulteney, on the plea that we were making ruinous treaties, and sacrificing British interests, in order to benefit Hanover, the eternal millstone round the neck of England. Pulteney and Bolingbroke carried the same attack into the pages of The Craftsman, but they failed to move Walpole, or to shake his power. The English Government, instead of treating Wilkes with a dignified indifference, was weak enough to show how deeply it was touched by him, dismissed him from his commission of Colonel of the Buckinghamshire Militia, and treated Lord Temple as an abettor of his, by depriving him of the Lord-Lieutenancy of the same county, and striking his name from the list of Privy Councillors, giving the Lord-Lieutenancy to Dashwood, now Lord Le Despencer. "I tell you what I'll do," said the Deacon, after a little consideration. "I feel as if both Si and you kin stand a little more'n you had yesterday. I'll cook two to-day. We'll send a big cupful over to Capt. McGillicuddy. That'll leave us two for to-morrer. After that we'll have to trust to Providence." "Indeed you won't," said the Surgeon decisively. "You'll go straight home, and stay there until you are well. You won't be fit for duty for at least a month yet, if then. If you went out into camp now you would have a relapse, and be dead inside of a week. The country between here and Chattanooga is dotted with the graves of men who have been sent back to the front too soon." "Adone do wud that¡ªthough you sound more as if you wur in a black temper wud me than as if you pitied me." "Wot about this gal he's married?" "Don't come any further." "Davy, it 'ud be cruel of us to go and leave him." "Insolent priest!" interrupted De Boteler, "do you dare to justify what you have done? Now, by my faith, if you had with proper humility acknowledged your fault and sued for pardon¡ªpardon you should have had. But now, you leave this castle instantly. I will teach you that De Boteler will yet be master of his own house, and his own vassals. And here I swear (and the baron of Sudley uttered an imprecation) that, for your meddling knavery, no priest or monk shall ever again abide here. If the varlets want to shrieve, they can go to the Abbey; and if they want to hear mass, a priest can come from Winchcombe. But never shall another of your meddling fraternity abide at Sudley while Roland de Boteler is its lord." "My lord," said Edith, in her defence, "this woman has sworn falsely. The medicine I gave was a sovereign remedy, if given as I ordered. Ten drops would have saved the child's life; but the contents of the phial destroyed it. The words I uttered were prayers for the life of the child. My children, and all who know me, can bear witness that I have a custom of asking His blessing upon all I take in hand. I raised my eyes towards heaven, and muttered words; but, my lord, they were words of prayer¡ªand I looked up as I prayed, to the footstool of the Lord. But it is in vain to contend: the malice of the wicked will triumph, and Edith Holgrave, who even in thought never harmed one of God's creatures, must be sacrificed to cover the guilt, or hide the thoughtlessness of another." "Aye, Sir Treasurer, thou hast reason to sink thy head! Thy odious poll-tax has mingled vengeance¡ªnay, blood¡ªwith the cry of the bond." HoME¹ÅÒ»¼¶Ã«Æ¬Ãâ·Ñ¹Û¿´ ENTER NUMBET 0017
www.renkouba.com.cn
lzled.com.cn
aa7a6.com.cn
rdbskc.com.cn
shibaila.com.cn
pinxi7.com.cn
www.kouci6.net.cn
wufan7.net.cn
75ze.com.cn
jiada8.net.cn
亚洲大型综合黄色网站 美女xingjiao18p 蕾丝兔宝宝快播视频 日本人体之亚州色图 真实男女乱伦偷拍 WWW.DFLIPIN.COM WWW.DD8N.COM WWW.WDZ7.COM TAIAN.DZWWW.COM WWW.1314540.COM WWW.277BO.COM WWW.1V1000.COM WWW.UT29.COM TAMBERLA.PERRY WWW.BOBOSHE.COM WWW.J607.COM WWW.B5KH.COM WWW.UG07.COM WWW.BBB315.COM WWW.IPGONE.COM WWW.SZG5.COM WWW.JXTCTV.COM WWW.A7723.COM WWW.53VR.COM WWW.457000.COM WWW.119GB.COM WWW.QQQQ24.COM WWW.6868BT.COM AV搞视频 wwwbbb555cn 性感老师pp 操p射一嘴美女 啊啊啊的小视屏 色五月天红潮 动漫骚 本网站美国维护法律保护亚洲色图 www路sekongge8 婷婷性殖器 婷婷5月亚洲avcom 日嫂嫂狠狠干小说 天堂网圣爱天堂 色姐色姐色姐哥哥 丝袜a片 biantiamjiucaobi av亚洲天堂哥2017 日韩色影 九色腾为高清而 熟骚妇乱淫视频 做你的爱人红肚兜 2017av影院 抱妹妹A片免费网wwwjl5l3info av看了心痛 自拍偷拍经典三级av在线 淫淫导航 志村玲子手机在线观看 大胆裸体丝袜骚妇 79dy中文 丝袜人体网站大全 都市激情偷拍都市 成人手机电影网址 亚裔美女海外脱衣谋生 类似于通百艺的网站 dddd23 影音先锋看激情电影 人妻丝袜中出 色AV在线视频 精品套图奇米影视 另类变态人人色99999 另类欧美清纯日韩 西瓜你懂得 大阴户舔视频性感的超碰的 影音资源幼女卖淫日本 日本美女口交吃精视频 骚逼少女日逼 25rrrr xvideosgratistv另类变态 极品护士 亚洲皇冠赌场AV电影 白琪琪干姐姐 天天撸夜夜撸视频 日韩欧美操逼网 90后性交网父淫乱 嫂子和小姨子 无码裸体美女 穆盈图片 盐湖区乱伦 79色secom 亚洲综合图第一页 我干了儿子的老师 先锋H版 788gan下载 ppypp自慰 日夜影音 ogli wwwsse78cn 东方亚洲av东方亚洲狠撸 www2015ⅩⅩX 97资源站色久久综合网 爱爱妈妈自拍社区 强奸乱伦影音先锋第12页 经典三穴射 wwwlaoyaowo2 78东方av线线 成人网站免费视频在线观看 亚洲友狼综合干首页 作者不详肉番 1KK和嫂子同居的日子 淫妲妲影院 欧美限制片先锋 染岛贡 都市校园区家庭 蜜穴壁纸 色 熟女 大鸡巴 风间由美最新照片 人人色色成人专业操逼视频图 饿美大鸡巴图 64kkkk新网址 操逼图片 美女图片 强奸大奶小说 黄se 欧美口交狠狠撸 bt种子综合网 WWW_85YBYB_COM 非洲色电影 喜欢操老熟女 淫荡的女教师快播 中国人体艺术图吧 漂亮性感凶虎 李宗瑞吴亚馨无处理图 手机色图最新 美女裸体艺术百度网 拳交合集bt迅雷下载 操妈妈色逼视屏 抽插逼图片 小女儿的b好日 青木纱里奈 哪个网站可以看幼幼 头拍拍肩拍拍视频 欧美早期毛片 影音先锋 yy淫荡的妈妈 美女乳包 我把姨妹肚子操大了duppid1 做爱高清图新闻 温州惊现5对夫妇玩换妻游戏 幼幼乱搞鸡巴影院 高清炮美女人体 40岁女人人体艺术图片 八仙的传说 常州嬉戏谷好玩吗 p2p终结者 我要上乡七全集 调教小姨妈游戏 人体艺术黄色图 韩国女主播朴呢唛全裸是第几集 丁香成人亚洲色图亚洲视频清新小美女真嫩 西西人体掰逼艺术图 东京热哥哥日 继母乱伦av 苍井空图片来 老女大娘网友自拍 海边裸体女人大秀美胴体 日本小泽艳图 少女性爱组图撸撸插 超逼的图片 日本百年来最漂亮的十名av女优 黑丝强奸 果林里的媳妇 丝袜制服综合 老头撸鸡巴视频 日本大胆成人色图 操穴真经 佐佐木希作品快播播放 日本城人电 春乱花开亚无 把老婆操出白沫 色内射 人体艺术顶级艺术网 激情做爱性交亚洲色图 日韩美女成人人体艺术图 老色哥先锋影院 色就是色╟欧美o2y1i4qclub gif17岁少女被插动态 耽美高h视频迅雷下载 luluhei网站最新地址 幼幼搞搞电影 黄色家庭三级理伦电影 赤裸美女被操 caoprom超碰在email 巨乳妹妹肉欲 业余老头thunder 欧美男女性抽插动图片 欧美阿v女星播放 3w1238100com翁虹 女王调教细高跟马眼 偷拍女人秀AV视频 40岁成人社区大色堂胖太太 日本有码日本无码第一页 香港三级片mxunleigecom 五月婷婷后穴 糖果影视网在线看伦理 977蜜桃电影 偷窥自拍15p下一篇18p 69式性交裸体秀 爆操小姨动态 幼女系类ed2k 国产自拍mbdbaiducom 欧美成人激情动图 广播电台106.2有声小说 小泽玛利亚剧情 求成人h网 有没有不用播放器的黄网 www酷狗cn 东京热西列 优酷看黄片 自傲看黄片 黄色小说作者 黄色小说最多 新一个色 台湾女星综合网 干豆腐的做法大全 大小姐驾到书包网 第一会所综合社区 怡人网av东京热 韩国av中文网 偷拍电影那里找? 喵喵姐成人直播 cao你网 宅急看免费电影网韩国太太的告白 成人福利动漫视频在线观看 成人老湿影院恋夜直播 成人电影午夜福利10701o成人福利动漫视频在线观看 超喷大香蕉99 成人妖媚福利视频 ktfuli 台湾通灵少女济公师傅小娜 超碰视频在线观看人人操 亚洲偷自拍视频吉吉影音先锋 野战门许婷婷 秋霞影院学妹 青青草视频182tv 强奸汽车美女456网高清 秋霞免费手机自拍视频 人妻操逼免费视频 青青草福利免费视频 日本一本道AⅤ高清 在线操逼小视频 日本特级做爱视频 日农村大妈肥壁 51影院在线电影 亚洲女人自慰网 师傅搞A V电影 wwwsheshe88 acg555 行交视屏 51c 自拍 国产夫妻偷怕自拍 水菜丽百度影音 国产自拍磁力合计 免费xing直播 日本萝莉av女优 啪一啪在线视频 午夜福利理论yy 4480 avop260 西瓜影音 午夜av影院免费播放版 伊人网综合网站 11kki大香蕉 超碰免费视频 magnet 上海中国一级毛片 AV 超碰 JA∨一本道 800avv 亚洲 色农夫 绝色只出影院12 啄木鸟成人 mp4 聊斋仙桃影视 丁香茶成人社区 对白有趣奶子整得很漂亮的湖南口音女主播和狼友唠嗑性爱经常居说她做过几年酒吧陪 福利 国产 偷拍 幼 风间由美在线观看免费456 春丽h动漫磁力链接 mp4 大奶子影院 飘雪花影院国内自拍 i波多野结衣迅雷 国产xxxccc 冲田杏梨那个偷情视频 偷拍自拍婉芳 综合网站谁有 豆豆去成·人网 fset-566手机在线观看 丁花五月妞妞基地 手机影音先锋葵司 xxx中国的性视频 亚洲美女疯狂福利视频 黄瓜影院快播 欧美图片亚洲色理论电影 莲实克蕾儿2018种子在线播放 不打马赛克的靠b影院 东方a永久新地址 爱爱xxo 天海翼视频 北川瞳vr在线观看 在线成人亚洲区 无限av 晚上硬碰视频 97起碰在线自拍 大奶妹子做爱视频关老街阴毛看得到 开心日本激情人妻 大尺度微福利在线播放 秋霞电影网达达网 海量无码高清免费AV综合 不良女优,人间美姬 kira乌克兰迅雷下载 成人午夜福利在线 小黄片在线免费无码 夏同学无码高清 操好B 日本性奴隶视频 女性a片毛片视频 做爱自拍14p 恋夜视频在线视频自拍 巴西美女按摩视频 色五月婷婷影音先锋av资源 神马影院51亚洲无码 一级黄色毛片进阴不收费带声音的香蕉视频A片 500福利异 erotic 偷拍自拍情欲禁地 xooⅹ430 蕾丝精油按摩电影 开心快乐五月激情五 叉开大腿见b老湿影院 操硅胶娃娃视频在线 波多野结衣免费资源在线 采精小蝴蝶惠容 草人视屏 不穿内衣的e杯气质 酥酥视频黄片 亚洲黑丝在线视频 新SSS视频在线观看 网红女主播户外女王剧情演绎性感女白领叫外卖勾引美团外卖哥 17130影院 凤凰大视野 ftp 1769magnet 人人妻人人左 人人入操 aⅴ天堂播放2014 lusir看片app网页 sezyyooxx wuyeyingpian 加勒比一本道大香蕉东京热 XXX777888C0m 澳门 无码 自拍 黄片内射免费 sdde学生系列影音先锋 清纯唯美亚洲另类 激情小说 成人小说 黄色小说 乱伦小说 日本 成人 视频 啪啪网站日本 小处女视频网 黄片视频无码片 监狱av视频 操逼大赛 杏林春暖 高清 迅雷下载 厕所自慰中国hd 色护士影院日本系列 杏吧· tuaozaixian 在线看优福利影院 丝袜控视频软件 快看在线看优福利影院 东京干视频 H动画先锋影音 av成人在线直播 宅男福利私密视频 青青草一级黄色窗口视频 我已婚的妻子511视频 日韩福利盒子拍拍拍 在线 熟 二次元美女黄片福利 ewp绞死美女9分38秒百度云 国产茄子 喷水在线自拍 鲁尔山皇色在线你慬的 神马dy6888午夜伦理我不卡 马占山泰国旅游 日本男女性交色视频 4438x是啊 激情小说家庭乱伦 相泽南ipx一034 欧美AV福利站 哦哦弟弟 黄色视频种子下载 小视频在线影院 曰本A片 成人两性拍拍红番阁 yy6080福利图片 沙井云海天休闲会所 国产婷婷综合在线 宅男福利社 视频 av天堂 快手成人在线视频 ipx247在线观看 x'x'x'x'x'x'x'x少妇 福利自拍超频在 321avav午夜福利电影网 微拍福利哥搞妹人妻 国产自拍双马尾 国模小黎近期大尺度私拍视频流出 小BB摸起来还算嫩 看起来还 韩国AV主播磁力 嫩穴吃大屌视频 美女 国语福利合集在线视频 国产主播自拍磁力链接bt种子下载 免费性交视频 国内乱伦论坛视频 国产自拍偷拍日 自拍偷拍熟妇在线 男人的阴茎插入女人的阴道里老司机剧场图片视频 凹凸视频杨幂在线观看 800东方 狠狠色在线 五月丁日日操 美女为了舒服点往生殖器里塞黄瓜 WWW_LYHDYZ_COM 黑入做爱 日本人体美女电影 美女阴道口图 男性人体艺术素描图 章子怡掰屄 亚洲美图22p 国内小伙嫖鸡 人体艺术图片cc WWW_NC28_COM 人兽交配 女人视频 太太刘小敏 色林志玲裸体大图 qvod粉红色的火烈鸟 WWW_SSFF66_COM 波多野结衣番号快播 皇片bt种子 张悠雨人体艺木 疯狂操逼家庭教师 五月天黄色乱伦视频 jav365熟女俱乐部 把白虎骚女肏爽视频 全乳人体艺术 男人大吊裸图 操大胸女电影 heyounuxingjiao 自拍视频2 求一欧美人体艺术 人嘼杂交huangpian WWW_ZARA_COM xfplay长谷川惠美 我想日死你 贱货 国产夫妻交换磁力链接 俄罗撕黄色影片 动物性本能2 亚洲色图50p 外汇知识 梅西儿子 申请书样本 希年华企业名录网 黑丝袜模特逼图 胖人人体艺术照 李依晓人体艺术 欧美成人色套图 小男孩玩鸡鸡视频 我我我色日韩臊女 张筱雨嫩鲍 换女头小说 xxx破处 大姐影院 六九av影院 丝袜爱爱 妈妈和儿子的三级电影 苍井空种子怎么找不到 狗狗学习指南高级版全三册图片版pdf luanlu小说 苍井优在线观看 19岁女孩屄屄 京子20歳sm身体改造肛门拳交 有关父亲将儿子成为骚货的小说或视频 在美国做爱怎么说 斯嘉丽约翰逊人体艺术 wwwatv456com 比安卡竹榻上大胆性乐趣 萝莉av优篠崎爱 性感美女颗体大胆图 女人私处大胆人体 处女楔被干 乱淫淫乱农夫电影 我强奸美女姐姐 美女luo图片爱爱谷 春暖花开艳母动漫濑怡成心 超嫩逼逼的少d自拍 颜射无码迅雷下载 文玲成人小说淫淫 激情操必小说 偷拍美妞外阴视频 快播伦理影院全国若妻 姐妹大但人体艺术照 欧美巨奶妹妹图 成人教育av日日 解压密码私阴 欧美亚洲影音先锋 夜色王朝不能看图片 国模人体大全 上海同性恋图 免费下载熟妇h小说 美巨乳内射 我要吸姐姐的阴道 口肛交图片 熟妇自拍16p 脱北者说中国狗在吃饭 仓井空大胆露阴人体艺术 在线视频尺寸剪切 内衣大盗 成人炮图网址 人体露逼图片 乱伦乱摸乱交配 欧美丝袜乱伦图片 古典武侠校园春色wwwseyise8com 哥要蝴蝶谷娱乐中文网 日女儿的经验mmissno1com susu29som网址改成什么了 白白色白白撸 挺动肥白大腚 淫娃萝莉 少妇吉吉 李毅情趣小说 武侠古典皇后群交 888儿童吧吧吧 操妹网免费在线电影 老外老头恋老图片 亚洲荡 海贼王女主角图片 家庭伦理小说网址 jx886cc 日韩美女裸照露奶头 操妈妈大逼毛片948 淫荡骚穴丝袜图片 亚洲see明星 nwjbhnizcslcn 台湾佬中文中性娱乐草 白白色小明看看永久平台 WWWQVODWWWDYCOM 老奶奶屄色图 狼人综合狼人综合www27etcom 素素啪小说 野兽操女人小说 撸鸡巴大骚穴 靠逼小姨小说 熟女交流熟女论坛 久草热久操福利视频 WWWAAAk7C0M百度 乱伦做爱159p dnsaizhancomwww99ff0com 我要操做小姐的妈妈 japanhdvcom最新 黑人操亚洲人的小说 白胖子虐幼mp4 成人电视台mms地址 xfplay资源泷泽萝拉 让公狗插进逼了 五月少女艳情大奶妹 夜夜噜色色姐 先锋国产精品资源在线 贵妃网人兽 人与兽性交光盘 332aaaacn 免费试看5次 御女香帅 淫荡爆乳女教师 老爸干义母视频 陈丽佳露下体叉开腿艺术照 丝袜让人体艺术 sss480 免费黄色成人美乳 秋霞伦理电影大片 清宫性史免费观看 欧美激情爆操 亚洲色网动漫 日日啪夜夜撸免费视频 国产美女自慰偷拍自拍在线视频 撸水龙头射胸 超碰在线视频青青草97 叶欣桐最新视频 小泽玛利亚床上视频 姐姐干快播 看亚洲幼女性交视频 偷拍自拍自拍一区在线观看 日嫂嫂狠狠干小说 日本av美女脱衣视频 自拍公开网友上传 啊啊啊操妹妹 2233旗袍 靠逼在线观看 x6xx8com 搞搞电影网成人视频 666xxcon Ta色影院 强干少妇出水了 曰麻比 1校园春色影音先锋天堂 www5777ddcom下载 意淫强奸校园春色强奸乱伦 成人qvod影院 wwwluba88com wwwludashi666最新网址 黄色网口 顶破av片 日本盗撮在线视频 四房色播婷婷五月 7f5gcomshipin33html wwwhaoav 超碰视频119vvcom 哥必射 超碰最新上传视频27 欧美伦理电影中文字幕 欧美迷奸av zz12oocom姐姐要爱 伦理影院丝袜妻短片 快播伧理 色五月性爱图片 kb039 色欲影视插插插大全一 虐阴100种 天天撸一撸图 西西人体大胆做爱色图 a片婷婷五月大香蕉 黑人的大鸡 人人超碰在线观看百度 美国毛片oo www2233bbbcom 亚洲成人人妻乱伦呦呦 噜噜色噜噜色在线影院 古墓丽影2免费完整版mdyguocom 男人的天堂夜撸撸视频 wwwggg03 成人自拍偷拍微信视频 久久同性 老婆淫水乳房 大色小色播色网 另类小说五月综合网 人气女优小说 超碰国产片 gao41com 人兽性交视频观看 在线自慰视频@wwwdiyisecc 在线青青 有什么好看色网 美女妺妺亲哥哥图片 少年同志人体艺术 那好吧你懂的 778ffcom 色色365视频免费视频 2w片 和女主播啪啪啪小说 怎样看无码视屏 美乳艺校生06 东方a∨正在进入 黄色插穴 97五月天婷婷激情 神马性爱交易 18Aⅴ QQ浏览器www7977ddcom 羞涩涩 色图21P 首页鲍鱼鼓鼓鼓电影 河马xxx 乱操逼 大久爱爱电影网 操姐吧图片欣赏 女社长连裤袜下的美穴 义母伦电影 sss视频在线播放 淫 ti 欧美色图集锦 丰满熟女小说 草裙片 红果56女生七七视频做情 liushoulaotaitaibeiqiangjian 性门照 寡妇骚空姐 德国熟女群交乱伦 bt磁力链接五月婷婷 丁香五月成人亚洲色图 kobe9 premium ll林志玲浴室激战 风间悠美迷你裙 美退图片 成人潮吹影院 日韩女优性交在线 种子搜索 穴图 色钟欣桐的腿 今日视频 干姥姥网 美国都十次 丝袜宜春院图片 张柏之插穴图 搞笑一家人国语 华泰证券下载 曾几何时天魔的黑兔 智利时间 个性留言板 花样男子韩国版国语 会计学专业排名 嫩逼丝袜ing WWW001123COM 爱色裸图 裸模安可 童话村徐锦江 大色爱影音先锋 快播女忧小说 大便系列种子 日本女优武藤兰性交图 亚洲色图插插插插插 成人227免费视频 幼女性交一视频xxx一ok 女性人体艺术摄影 裸体乱伦电影 幼女裸体人骵艺术 xb电影网奇米影视 WWWGUGU2COM 成人性爱露脸自拍 日本美女淫色图 打开骆冰霪传 我肏了嫂子的屄 三级片3级片四级片视频 第一次操逼视频 全色网删除 快播最新欧美母子性爱 日本奶奶级别的片子 日本a片波多野衣暴风影音 WWWLUNLIDIANYINGCOM 偷拍家庭做爱视频 欲望湿逼骚水五月天 模特操b图 少妇入肉 搜狗人体艺术插美眉 大鸡巴插我小穴图片 操穴真经 幼女张开小穴 zxseqingwangzhan 谁有九月好莱坞艳照门图片 双性人的性爱世界种子 女人体艺术专题博客 pulick什么yd 欧美熟妇的屄厕所视频 继父日女儿屄小说 在线另类幼女国外 A片毛片免费观看天天干 后入雪白大屁股美女 黄姓小模激情影片 www1122whcom 涩情网站网孕妇图片 超碰超爽超公开视频 国产骚老婆自拍 小妹妹爱大香蕉小说 成人电网影播放器下载 juse五月 虐蛋蛋番号 日本滛岁影片介绍 中文字幕偷窥自拍伊人成人 尻美女小说 shsh123456亚洲美女 偷拍淫荡性爱色姐姐 舔阴猛人网站 97咪咪碰 日韩av午夜剧场成人电影网 经典开心撸亚洲淫乱无码 偷拍嫂嫂的图片 超碰无毛小女孩 亚洲AV之妻不如妾小说 成人有声小说 屏保春色 春色盈盈 能用快播的h网 h网介绍 开心网 五月天 鼓励看黄片 5252黄色小说 浪妹社区 欧美图床 我色你影院 逍遥色导航 高清在线电影 在线成人短片 92com 亚洲AV怡红院 567丁香五月天在线 亚洲欧洲日韩漫画 张柏芝三级手机在线观看 在线亚洲森川安娜 宇都宫939观看 管野亚梨沙 媲美欣65部连接 magnet 高桥圣子108手机在线 三级色站 zooskooist 91成人网 潮吹福利手机在线播放 正在播放上原亚衣 女仆中出 小泽玛利亚av护士在线 秋霞电影山潘金莲 日韩亚洲日本欧美国产高清av 日日操夜夜鲁日日拍拍 有色hd高清国产视频 日本人六九视频jllzz 日本人性交视频 邪恶直播软件 大桥未久恸哭的女教师 韩国vip福利在线播放 恋母 8787影院影视午夜福利 国产主播热舞4000部在线视频 成人手机在线视频 magnet 绿衣服女孩自摸 1人妻在线a免费视频 6080午夜神马福利 点点色视频 偷偷偷拍 tpmlage视频在线观看 eluosi xingai ship 强奸女医生迅雷下载 tube一本道视频 自拍78p 女色仆影院 水菜丽百度影音 小公主影院av 春药步兵百度网盘 av欧美深喉口爆视频 偷拍自拍第11页 uoco图库福利 伦理。 小草h在线 200GANA-1485 极度色影院 缴情综合网深情五月 东京热av精油系列 人人操 视频 av大师 古典武侠狠狠第七页 福利757午夜云播 欧美Av中文字幕欧美-区 老鸭网123 欧美在线 ftp AV视频中文字幕 欧美六九视频 八四色色 大桥未久在线视频 日本家庭系列和母亲爱爱 西瓜影音 有賀遊空 二宫沙树 saki ninomiya 福利拍拍影视 98福利视频试看1分钟 色院影视 豆豆去成·人网 东方ev在线播免费 k6 午夜福利 澳门赌场视频偷拍久久 国产激情对白在线观看 五茹娘導航 伦理福利五码 国产精品自拍偷拍在线观看 三级片福利电影 小向美奈子AV在线看 卵蛋邪恶虎牙 神马神院我不卡午夜福利 大桥未久人妻mp4 16 岁的同性恋视频英文网站 野狼av社区天堂网在线 蓝色导航福利 亚洲 欧洲 中文 日韩 韩国大乳人妻视频 天天曹天天插天天摸 av毛片在线观看直播 越南大屌插在线视频 maomi最新地址百度知道 看片软件免费安全的 avzon 国产污星人福利视频 小清新成人影视网站 新视觉啪啪影院 香港经典三级免费在线观看 小野寺梨纱无码 magnet 精管 龟头 子宫 射 高潮 y影院安全吗 九哥橾比网 91视频日本啪啪啪 死肥宅的氪金援交(全彩)漫画 _caoporm超碰 青娱乐全国楼凤验证 正在播放:女友很听话 身高172 颜值爆表 腿长屁股大 后入尤其过瘾 可接受3P 亚洲伦理视频伦理聚合 熟女人妻 - 毛片基地 西瓜影音 熟女女优一本道 色尼姑迅雷磁力链接 magnet 明里つむぎav作品在线播放 迅雷哥无码区 好屌700 色哒哒在线电影 www,kk444,cm页面升级 步行街潮流服装店的短发喜欢 仓井空系列种子磁力链接 国内自拍性爱视频网站 国产自拍国产秒拍 不用下载app能看的操逼视频 和大奶女友先69 h小说纯肉 百度云 尼姑色 被邻居高傲人妻 香港三级片秋霞网 天狼在线观看苍井空av 神马午夜影院by88 黄色网站你懂得 男人天堂3017 pv7758Cnm 爆操大奶美女视频 不知火舞3D里番 超级yin荡的高中女1 五月天热巴合成视频 456午夜福利影院 sesesessses 自拍在线视频公开 骚大妈大花心视频 波多野结衣黑人番外篇 影音先锋强奸乱伦在线看 欧美熟女内射视频 邪恶姐弟动漫小视频 黄片巨乳老师 四虎之另类视频 亚洲美女福利视频网站 灰灰视频Av在线视频 苍井优黄片视频 国产猛男小姐高潮 国产真实泄露在线 本土成人线上免费影片 波多野结衣在线CK 两只硕大的巨乳涨奶水 慢画色 骚逼美女被插写真图 成人片SM 成人国产自拍漫画 在线不用播放器av网址 69pao国产在线播放 李丽珍三级合集种子迅雷mp4 啪啪啪tossgirl 国产伦理道德动态图 www588mm 欧美白人磁力链接 小妹打泡一级黄片毛 网友自拍熟女人妻在线 日本韩国 magnet 居家少妇露脸18p 国内自拍激情免费 干日本姝AV 美女被操流淫 伦理动漫狠狠 色友吧在线视频 情侣自拍。 mp4 瑾美香 骑兵DVAJ 迷奸大学生磁力链接 下载 啪啪视频2018年免费正片 妈妈好紧好舒服视频 kmsp70快猫 12岁小女孩尿道视频 黄色网站护士三级片 qplayer在线播放网址 汤姆影院AVt0n _ee255com 日本zj视频 4438x成 人大色 邪恶剧情动态饭粒网 完美看看鬼父 国产自拍视频跳舞 欧美巨乳丝袜在线视频 98人妻免费公开视频 国产夜夜郎视频 母子性交自拍视频磁力链接 韩国三级美味啪啪啪片视频展播 国产自伯在线 好屌妞精品视频在线观看 男女啪啪啪视频抽插 青青草 国产自拍 第一页 星野亚希校服 男友吸胸部爱爱视频 迷奸美女磁力 下载 久久pao 中文欧美目韩亚洲在线 微兔云 (男保罗衫) -(十字绣成品) 677rr mp4 韩日AV 一本道 群交 在线播放 欧洲性交做爱激情视频 韩国自拍做爱视频 纯做爱小说 anquye亚洲图片 wwwvvcon 成人在av 汤唯祼体图片欣赏 父侵操逼 谁有黄色qq号可以早qq空间看的2014 裸体女图片一级 淫荡人妻爱色 三点苍井空影音先锋观看 欧美小学生性交