From: smurph Date: Wed, 22 Mar 2000 02:50:49 +0000 (+0000) Subject: new ahc driver. Adds support for newer Adaptec controllers. This represents two... X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=d83edbeac707a46b3df17240c6b13abe408ba05b;p=openbsd new ahc driver. Adds support for newer Adaptec controllers. This represents two months of work. --- diff --git a/share/man/man4/ahc.4 b/share/man/man4/ahc.4 index e47d014a20b..46b1444fec5 100644 --- a/share/man/man4/ahc.4 +++ b/share/man/man4/ahc.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ahc.4,v 1.6 1999/07/09 13:35:48 aaron Exp $ +.\" $OpenBSD: ahc.4,v 1.7 2000/03/22 03:00:57 smurph Exp $ .\" $NetBSD: ahc.4,v 1.1.2.1 1996/08/25 17:22:14 thorpej Exp $ .\" .\" Copyright (c) 1995, 1996 @@ -27,7 +27,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" -.Dd April 20, 1996 +.Dd March 20, 2000 .\".Dt AHC 4 .\".Os FreeBSD .Dt AHC 4 @@ -55,12 +55,6 @@ For one or more PCI cards: \{For PCI cards: .Cd ahc* at pci? dev ? function ?\} .Pp -To enable tagged queuing: -.Cd option AHC_TAGENABLE -.Pp -To enable SCB paging: -.Cd options AHC_SCBPAGING_ENABLE -.Pp .ie 0 \{ For one or more SCSI busses: .Cd controller scbus0 at ahc0 @@ -70,96 +64,177 @@ For one or more SCSI busses: .Sh DESCRIPTION This driver provides access to the .Tn SCSI -bus connected to an Adaptec -274x, 284x, 2940, 3940, or controllers based on the +bus(es) connected to Adaptec .Tn AIC7770, .Tn AIC7850, .Tn AIC7860, .Tn AIC7870, +.Tn AIC7880, +.Tn AIC7890, +.Tn AIC7891, +.Tn AIC7895, +.Tn AIC7896, or -.Tn AIC7880 +.Tn AIC7897 host adapter chips. -Features include support for twin and wide busses, -ultra -.Tn SCSI, -two active commands at a time per non-tagged queuing target, +These chips are found on many motherboards as well as the following +Adaptec SCSI controller cards: +.Tn 274X(W), +.Tn 274X(T), +.Tn 284X, +.Tn 2920C, +.Tn 2930U2, +.Tn 2940, +.Tn 2940U, +.Tn 2940AU, +.Tn 2940UW, +.Tn 2940UW Dual, +.Tn 2940U2W, +.Tn 2940U2B, +.Tn 2950U2W, +.Tn 2950U2B, +.Tn 3940, +.Tn 3940U, +.Tn 3940AU, +.Tn 3940UW, +.Tn 3940AUW, +.Tn 3940U2W, +.Tn 3950U2, +and +.Tn 3985. +.Pp +Driver features include support for twin and wide busses, +fast, ultra and ultra2 synchronous transfers depending on controller type, tagged queuing, -and SCB paging which allows up to 255 active commands on all adapters -except those using -.Tn AIC7770 -chips prior to revision E. -Tagged queuing is enabled with the -.Dq Dv AHC_TAGENABLE -configuration option. -SCB paging is enabled with the -.Dq Dv AHC_SCBPAGING_ENABLE +and SCB paging. +.Pp +Memory mapped I/O can be enabled for PCI devices with the +.Dq Dv AHC_ALLOW_MEMIO configuration option. +Memory mapped I/O is more efficient than the alternative, programmed I/O. +Most PCI BIOSes will map devices so that either technique for communicating +with the card is available. +In some cases, +usually when the PCI device is sitting behind a PCI->PCI bridge, +the BIOS fails to properly initialize the chip for memory mapped I/O. +The symptom of this problem is usually a system hang if memory mapped I/O +is attempted. +Most modern motherboards perform the initialization correctly and work fine +with this option enabled. .Pp -Per target configuration performed in the +Per target configuration performed in the .Tn SCSI-Select menu, accessible at boot -in +in .No non- Ns Tn EISA -models or through an +models, +or through an .Tn EISA -configuration utility for +configuration utility for .Tn EISA models, -is honored by this driver with the stipulation that the +is honored by this driver with the stipulation that the .Tn BIOS -must be enabled for +must be enabled for .Tn EISA adaptors. This includes synchronous/asynchronous transfers, maximum synchronous negotiation rate, disconnection, -and the host adapter's SCSI ID. -.Pp -Note that I/O addresses are determined automatically by the probe routines, -but care should be taken when using a 284x -.Pq Tn VESA No local bus controller -in an -.Tn EISA -system. Ensure that the jumpers setting the I/O area for the 284x match the +the host adapter's SCSI ID, +and, +in the case of .Tn EISA -slot into which the card is inserted to prevent conflicts with other -.Tn EISA -cards. +Twin Channel controllers, +the primary channel selection. +.Pp +Performance and feature sets vary throughout the aic7xxx product line. +The following table provides a comparison of the different chips supported +by the +.Nm +driver. Note that wide and twin channel features, although always supported +by a particular chip, may be disabled in a particular motherboard or card +design. +.Pp +.Bd -filled -offset indent +.Bl -column "aic7770 " "10 " "EISA/VL " "10MHz " "16bit " "SCBs " Features +.Em "Chip MIPS Bus MaxSync MaxWidth SCBs Features" +aic7770 10 EISA/VL 10MHz 16Bit 4 1 +aic7850 10 PCI/32 10MHz 8Bit 3 +aic7860 10 PCI/32 20MHz 8Bit 3 +aic7870 10 PCI/32 10MHz 16Bit 16 +aic7880 10 PCI/32 20MHz 16Bit 16 +aic7890 20 PCI/32 40MHz 16Bit 16 3 4 5 6 7 +aic7891 20 PCI/64 40MHz 16Bit 16 3 4 5 6 7 +aic7895 15 PCI/32 20MHz 16Bit 16 2 3 4 5 +aic7896 20 PCI/32 40MHz 16Bit 16 2 3 4 5 6 7 +aic7897 20 PCI/64 40MHz 16Bit 16 2 3 4 5 6 7 +.El +.Pp +.Bl -enum -compact +.It +Multiplexed Twin Channel Device - One controller servicing two busses. +.It +Multi-function Twin Channel Device - Two controllers on one chip. +.It +Command Channel Secondary DMA Engine - Allows scatter gather list and +SCB prefetch. +.It +64 Byte SCB Support - SCSI CDB is embedded in the SCB to eliminate an extra DMA. +.It +Block Move Instruction Support - Doubles the speed of certain sequencer +operations. +.It +.Sq Bayonet +style Scatter Gather Engine - Improves S/G prefetch performance. +.It +Queuing Registers - Allows queuing of new transactions without pausing the +sequencer. +.El +.Ed +.Pp + +.Sh SCSI CONTROL BLOCKS (SCBs) +Every transaction sent to a device on the SCSI bus is assigned a +.Sq SCSI Control Block +(SCB). The SCB contains all of the information required by the +controller to process a transaction. The chip feature table lists +the number of SCBs that can be stored in on chip memory. All chips +with model numbers greater than or equal to 7870 allow for the on chip +SCB space to be augmented with external SRAM up to a maximum of 255 SCBs. +Very few Adaptec controller have external SRAM. + +If external SRAM is not available, SCBs are a limited resource and +using them in a straight forward manner would only allow us to +keep as many transactions as there are SCBs outstanding at a time. +This would not allow enough concurrency to fully utilize the SCSI +bus and it's devices. +The solution to this problem is +.Em SCB Paging , +a concept similar to memory paging. SCB paging takes advantage of +the fact that devices usually disconnect from the SCSI bus for long +periods of time without talking to the controller. The SCBs +for disconnected transactions are only of use to the controller +when the transfer is resumed. When the host queues another transaction +for the controller to execute, the controller firmware will use a +free SCB if one is available. Otherwise, the state of the most recently +disconnected (and therefor most likely to stay disconnected) SCB is +saved, via dma, to host memory, and the local SCB reused to start +the new transaction. This allows the controller to queue up to +255 transactions regardless of the amount of SCB space. Since the +local SCB space serves as a cache for disconnected transactions, the +more SCB space available, the less host bus traffic consumed saving +and restoring SCB data. .Sh BUGS Some Quantum drives (at least the Empire 2100 and 1080s) will not run on an .Tn AIC7870 Rev B in synchronous mode at 10MHz. Controllers with this problem have a -42 MHz clock crystal on them and run slightly above 10MHz, causing the -drive much confusion. Setting a maximum synchronous negotiation rate of 8MHz -in the +42 MHz clock crystal on them and run slightly above 10MHz. This confuses +the drive and hangs the bus. Setting a maximum synchronous negotiation rate +of 8MHz in the .Tn SCSI-Select utility -will allow normal function. -.if 1 \{ -.Pp -There are four problems known in this version of this driver as follows. -All of these problems relate to MI SCSI system. -.Pp -Cannot handle SCSI ID greater than 7 with WIDE SCSI adapter. -.Pp -It is dangerous to simultaneously access SCSI devices more than half -number of SCBs. It may cause fatal disk trouble. In this case, -diagnostics message -.Dq not queued, error ... -is displayed to console. -For example, AHA-274x and AHA-284x have only 4 SCBs, so that using 3 SCSI -devices is dangerous, using 2 devices is OK. -.Pp -When you suddenly access SCSI devices on free physical memory shortage -conditions, and if there have been very few load on SCSI devices until -then, it may cause fatal disk trouble. In this case, diagnostics -message -.Dq ahc0: Can't malloc SCB -and -.Dq not queued, error ... -is displayed to console. -.Pp -SCSI residual handling is not complete. -\} +will allow normal operation. + .Sh SEE ALSO .Xr aha 4 , .Xr ahb 4 @@ -172,7 +247,9 @@ SCSI residual handling is not complete. .Sh AUTHOR The .Nm -driver was written by Justin Gibbs. The +driver, the .Tn AIC7xxx -sequencer-code assembler was -written by John Aycock. +sequencer-code assembler, +and the firmware running on the aic7xxx chips was written by +.An Justin T. Gibbs . + diff --git a/sys/arch/i386/isa/ahc_isa.c b/sys/arch/i386/isa/ahc_isa.c index a7bb4f33511..e504eeafa3e 100644 --- a/sys/arch/i386/isa/ahc_isa.c +++ b/sys/arch/i386/isa/ahc_isa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ahc_isa.c,v 1.4 1999/01/11 05:11:24 millert Exp $ */ +/* $OpenBSD: ahc_isa.c,v 1.5 2000/03/22 02:57:17 smurph Exp $ */ /* $NetBSD: ahc_isa.c,v 1.5 1996/10/21 22:27:39 thorpej Exp $ */ /* @@ -84,6 +84,13 @@ #include #include +#include + +#ifdef DEBUG +#define bootverbose 1 +#else +#define bootverbose 0 +#endif /* IO port address setting range as EISA slot number */ #define AHC_ISA_MIN_SLOT 0x1 /* from iobase = 0x1c00 */ @@ -112,9 +119,10 @@ int ahc_isa_match __P((struct isa_attach_args *, bus_addr_t)); int ahc_isa_probe __P((struct device *, void *, void *)); void ahc_isa_attach __P((struct device *, struct device *, void *)); +void aha2840_load_seeprom __P((struct ahc_softc *ahc)); struct cfattach ahc_isa_ca = { - sizeof(struct ahc_data), ahc_isa_probe, ahc_isa_attach + sizeof(struct ahc_softc), ahc_isa_probe, ahc_isa_attach }; /* @@ -144,8 +152,12 @@ ahc_isa_irq(iot, ioh) { int irq; u_char intdef; + u_char hcntrl; + + /* Pause the card preseving the IRQ type */ + hcntrl = bus_space_read_1(iot, ioh, HCNTRL) & IRQMS; + bus_space_write_1(iot, ioh, HCNTRL, hcntrl | PAUSE); - ahc_reset("ahc_isa", iot, ioh); intdef = bus_space_read_1(iot, ioh, INTDEF); switch (irq = (intdef & 0xf)) { case 9: @@ -261,7 +273,7 @@ ahc_isa_match(ia, iobase) if (ia->ia_irq != IRQUNK && ia->ia_irq != irq) { printf("ahc_isa_match: irq mismatch (kernel %d, card %d)\n", - ia->ia_irq, irq); + ia->ia_irq, irq); return (0); } @@ -334,15 +346,18 @@ ahc_isa_attach(parent, self, aux) struct device *parent, *self; void *aux; { - ahc_type type; - struct ahc_data *ahc = (void *)self; + ahc_chip chip; + struct ahc_softc *ahc = (void *)self; struct isa_attach_args *ia = aux; bus_space_tag_t iot = ia->ia_iot; bus_space_handle_t ioh; int irq; char idstring[EISA_IDSTRINGLEN]; const char *model; + u_char channel = 'A'; + ahc->sc_dmat = ia->ia_dmat; + chip = AHC_VL; /* We are a VL Bus Controller */ if (bus_space_map(iot, ia->ia_iobase, ia->ia_iosize, 0, &ioh)) panic("ahc_isa_attach: could not map slot I/O addresses"); if (!ahc_isa_idstring(iot, ioh, idstring)) @@ -352,16 +367,16 @@ ahc_isa_attach(parent, self, aux) if (strcmp(idstring, "ADP7756") == 0) { model = EISA_PRODUCT_ADP7756; - type = AHC_284; + chip |= AHC_AIC7770; } else if (strcmp(idstring, "ADP7757") == 0) { model = EISA_PRODUCT_ADP7757; - type = AHC_284; + chip |= AHC_AIC7770; } else { panic("ahc_isa_attach: Unknown device type %s", idstring); } printf(": %s\n", model); - ahc_construct(ahc, iot, ioh, type, AHC_FNONE); + ahc_construct(ahc, iot, ioh, chip, AHC_FNONE, AHC_AIC7770_FE, channel); #ifdef DEBUG /* @@ -372,23 +387,15 @@ ahc_isa_attach(parent, self, aux) ahc->pause & IRQMS ? "Level Sensitive" : "Edge Triggered"); #endif + ahc->channel = 'A'; + ahc->channel_b = 'B'; + if (ahc_reset(ahc) != 0) + return; /* * Now that we know we own the resources we need, do the * card initialization. - * - * First, the aic7770 card specific setup. - */ - - /* XXX - * On AHA-284x, - * all values are automagically intialized at - * POST for these cards, so we can always rely - * on the Scratch Ram values. However, we should - * read the SEEPROM here (Dan has the code to do - * it) so we can say what kind of translation the - * BIOS is using. Printing out the geometry could - * save a lot of users the grief of failed installs. */ + aha2840_load_seeprom(ahc); /* * See if we have a Rev E or higher aic7770. Anything below a @@ -462,3 +469,98 @@ ahc_isa_attach(parent, self, aux) /* Attach sub-devices - always succeeds */ ahc_attach(ahc); } + +/* + * Read the 284x SEEPROM. + */ +void +aha2840_load_seeprom(struct ahc_softc *ahc) +{ + struct seeprom_descriptor sd; + struct seeprom_config sc; + u_int16_t checksum = 0; + u_int8_t scsi_conf; + int have_seeprom; + + sd.sd_tag = ahc->sc_iot; + sd.sd_bsh = ahc->sc_ioh; + sd.sd_control_offset = SEECTL_2840; + sd.sd_status_offset = STATUS_2840; + sd.sd_dataout_offset = STATUS_2840; + sd.sd_chip = C46; + sd.sd_MS = 0; + sd.sd_RDY = EEPROM_TF; + sd.sd_CS = CS_2840; + sd.sd_CK = CK_2840; + sd.sd_DO = DO_2840; + sd.sd_DI = DI_2840; + + if (bootverbose) + printf("%s: Reading SEEPROM...", ahc_name(ahc)); + have_seeprom = read_seeprom(&sd, + (u_int16_t *)&sc, + /*start_addr*/0, + sizeof(sc)/2); + + if (have_seeprom) { + /* Check checksum */ + int i; + int maxaddr = (sizeof(sc)/2) - 1; + u_int16_t *scarray = (u_int16_t *)≻ + + for (i = 0; i < maxaddr; i++) + checksum = checksum + scarray[i]; + if (checksum != sc.checksum) { + if(bootverbose) + printf ("checksum error\n"); + have_seeprom = 0; + } else if (bootverbose) { + printf("done.\n"); + } + } + + if (!have_seeprom) { + if (bootverbose) + printf("%s: No SEEPROM available\n", ahc_name(ahc)); + ahc->flags |= AHC_USEDEFAULTS; + } else { + /* + * Put the data we've collected down into SRAM + * where ahc_init will find it. + */ + int i; + int max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8; + u_int16_t discenable; + + discenable = 0; + for (i = 0; i < max_targ; i++){ + u_int8_t target_settings; + target_settings = (sc.device_flags[i] & CFXFER) << 4; + if (sc.device_flags[i] & CFSYNCH) + target_settings |= SOFS; + if (sc.device_flags[i] & CFWIDEB) + target_settings |= WIDEXFER; + if (sc.device_flags[i] & CFDISC) + discenable |= (0x01 << i); + ahc_outb(ahc, TARG_SCSIRATE + i, target_settings); + } + ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff)); + ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff)); + + ahc->our_id = sc.brtime_id & CFSCSIID; + + scsi_conf = (ahc->our_id & 0x7); + if (sc.adapter_control & CFSPARITY) + scsi_conf |= ENSPCHK; + if (sc.adapter_control & CFRESETB) + scsi_conf |= RESET_SCSI; + + if (sc.bios_control & CF284XEXTEND) + ahc->flags |= AHC_EXTENDED_TRANS_A; + /* Set SCSICONF info */ + ahc_outb(ahc, SCSICONF, scsi_conf); + + if (sc.adapter_control & CF284XSTERM) + ahc->flags |= AHC_TERM_ENB_A; + } +} diff --git a/sys/dev/eisa/ahc_eisa.c b/sys/dev/eisa/ahc_eisa.c index b0cf4bdf72b..37c3642f56f 100644 --- a/sys/dev/eisa/ahc_eisa.c +++ b/sys/dev/eisa/ahc_eisa.c @@ -1,6 +1,6 @@ -/* $OpenBSD: ahc_eisa.c,v 1.8 1999/01/11 01:57:59 millert Exp $ */ +/* $OpenBSD: ahc_eisa.c,v 1.9 2000/03/22 02:55:40 smurph Exp $ */ /* $NetBSD: ahc_eisa.c,v 1.10 1996/10/21 22:30:58 thorpej Exp $ */ - + /* * Product specific probe and attach routines for: * 27/284X and aic7770 motherboard SCSI controllers @@ -32,164 +32,32 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ahc_eisa.c,v 1.8 1999/01/11 01:57:59 millert Exp $ + * $Id: ahc_eisa.c,v 1.9 2000/03/22 02:55:40 smurph Exp $ */ -#if defined(__FreeBSD__) -#include -#endif -#if NEISA > 0 || defined(__NetBSD__) || defined(__OpenBSD__) +#include "eisa.h" +#if NEISA > 0 #include #include -#if defined(__FreeBSD__) -#include -#endif #include - -#if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #include -#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */ #include #include -#if defined(__FreeBSD__) - -#include - -#include -#include -#include - -#define EISA_DEVICE_ID_ADAPTEC_AIC7770 0x04907770 -#define EISA_DEVICE_ID_ADAPTEC_274x 0x04907771 -#define EISA_DEVICE_ID_ADAPTEC_284xB 0x04907756 /* BIOS enabled */ -#define EISA_DEVICE_ID_ADAPTEC_284x 0x04907757 /* BIOS disabled*/ - -#elif defined(__NetBSD__) || defined(__OpenBSD__) - #include #include #include - #include #include -#endif /* defined(__NetBSD__) */ - #define AHC_EISA_SLOT_OFFSET 0xc00 #define AHC_EISA_IOSIZE 0x100 #define INTDEF 0x5cul /* Interrupt Definition Register */ -#if defined(__FreeBSD__) - -static int aic7770probe __P((void)); -static int aic7770_attach __P((struct eisa_device *e_dev)); - -static struct eisa_driver ahc_eisa_driver = { - "ahc", - aic7770probe, - aic7770_attach, - /*shutdown*/NULL, - &ahc_unit - }; - -DATA_SET (eisadriver_set, ahc_eisa_driver); - -static struct kern_devconf kdc_aic7770 = { - 0, 0, 0, /* filled in by dev_attach */ - "ahc", 0, { MDDT_EISA, 0, "bio" }, - eisa_generic_externalize, 0, 0, EISA_EXTERNALLEN, - &kdc_eisa0, /* parent */ - 0, /* parentdata */ - DC_UNCONFIGURED, /* always start out here */ - NULL, - DC_CLS_MISC /* host adapters aren't special */ -}; - - -static char *aic7770_match __P((eisa_id_t type)); - -static char* -aic7770_match(type) - eisa_id_t type; -{ - switch(type) { - case EISA_DEVICE_ID_ADAPTEC_AIC7770: - return ("Adaptec aic7770 SCSI host adapter"); - break; - case EISA_DEVICE_ID_ADAPTEC_274x: - return ("Adaptec 274X SCSI host adapter"); - break; - case EISA_DEVICE_ID_ADAPTEC_284xB: - case EISA_DEVICE_ID_ADAPTEC_284x: - return ("Adaptec 284X SCSI host adapter"); - break; - default: - break; - } - return (NULL); -} - -static int -aic7770probe(void) -{ - u_long iobase; - char intdef; - u_long irq; - struct eisa_device *e_dev = NULL; - int count; - - count = 0; - while ((e_dev = eisa_match_dev(e_dev, aic7770_match))) { - iobase = (e_dev->ioconf.slot * EISA_SLOT_SIZE) - + AHC_EISA_SLOT_OFFSET; - ahc_reset(iobase); - - eisa_add_iospace(e_dev, iobase, AHC_EISA_IOSIZE, RESVADDR_NONE); - intdef = inb(INTDEF + iobase); - switch (intdef & 0xf) { - case 9: - irq = 9; - break; - case 10: - irq = 10; - break; - case 11: - irq = 11; - break; - case 12: - irq = 12; - break; - case 14: - irq = 14; - break; - case 15: - irq = 15; - break; - default: - printf("aic7770 at slot %d: illegal " - "irq setting %d\n", e_dev->ioconf.slot, - intdef); - continue; - } - eisa_add_intr(e_dev, irq); - eisa_registerdev(e_dev, &ahc_eisa_driver, &kdc_aic7770); - if(e_dev->id == EISA_DEVICE_ID_ADAPTEC_284xB - || e_dev->id == EISA_DEVICE_ID_ADAPTEC_284x) { - /* Our real parent is the isa bus. Say so. */ - e_dev->kdc->kdc_parent = &kdc_isa0; - } - count++; - } - return count; -} - -#elif defined(__NetBSD__) || defined(__OpenBSD__) - /* * Under normal circumstances, these messages are unnecessary * and not terribly cosmetic. @@ -197,16 +65,16 @@ aic7770probe(void) #ifdef DEBUG #define bootverbose 1 #else -#define bootverbose 0 +#define bootverbose 1 #endif - -int ahc_eisa_irq __P((bus_space_tag_t, bus_space_handle_t)); -int ahc_eisa_match __P((struct device *, void *, void *)); -void ahc_eisa_attach __P((struct device *, struct device *, void *)); + +int ahc_eisa_irq __P((bus_space_tag_t, bus_space_handle_t)); +int ahc_eisa_match __P((struct device *, void *, void *)); +void ahc_eisa_attach __P((struct device *, struct device *, void *)); struct cfattach ahc_eisa_ca = { - sizeof(struct ahc_data), ahc_eisa_match, ahc_eisa_attach + sizeof(struct ahc_softc), ahc_eisa_match, ahc_eisa_attach }; /* @@ -214,13 +82,17 @@ struct cfattach ahc_eisa_ca = { */ int ahc_eisa_irq(iot, ioh) - bus_space_tag_t iot; - bus_space_handle_t ioh; +bus_space_tag_t iot; +bus_space_handle_t ioh; { int irq; u_char intdef; - - ahc_reset("ahc_eisa", iot, ioh); + u_char hcntrl; + + /* Pause the card preseving the IRQ type */ + hcntrl = bus_space_read_1(iot, ioh, HCNTRL) & IRQMS; + bus_space_write_1(iot, ioh, HCNTRL, hcntrl | PAUSE); + intdef = bus_space_read_1(iot, ioh, INTDEF); switch (irq = (intdef & 0xf)) { case 9: @@ -246,8 +118,8 @@ ahc_eisa_irq(iot, ioh) */ int ahc_eisa_match(parent, match, aux) - struct device *parent; - void *match, *aux; +struct device *parent; +void *match, *aux; { struct eisa_attach_args *ea = aux; bus_space_tag_t iot = ea->ea_iot; @@ -256,16 +128,16 @@ ahc_eisa_match(parent, match, aux) /* must match one of our known ID strings */ if (strcmp(ea->ea_idstring, "ADP7770") && - strcmp(ea->ea_idstring, "ADP7771") + strcmp(ea->ea_idstring, "ADP7771") #if 0 - && strcmp(ea->ea_idstring, "ADP7756") /* not EISA, but VL */ - && strcmp(ea->ea_idstring, "ADP7757") /* not EISA, but VL */ + && strcmp(ea->ea_idstring, "ADP7756") /* not EISA, but VL */ + && strcmp(ea->ea_idstring, "ADP7757") /* not EISA, but VL */ #endif - ) + ) return (0); if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) + - AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh)) + AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh)) return (0); irq = ahc_eisa_irq(iot, ioh); @@ -275,72 +147,14 @@ ahc_eisa_match(parent, match, aux) return (irq >= 0); } -#endif /* defined(__NetBSD__) */ - -#if defined(__FreeBSD__) -static int -aic7770_attach(e_dev) - struct eisa_device *e_dev; -#elif defined(__NetBSD__) || defined(__OpenBSD__) void ahc_eisa_attach(parent, self, aux) - struct device *parent, *self; - void *aux; -#endif +struct device *parent, *self; +void *aux; { - ahc_type type; - -#if defined(__FreeBSD__) - struct ahc_data *ahc; - resvaddr_t *iospace; - int unit = e_dev->unit; - int irq = ffs(e_dev->ioconf.irq) - 1; - - iospace = e_dev->ioconf.ioaddrs.lh_first; - - if(!iospace) - return -1; - - switch(e_dev->id) { - case EISA_DEVICE_ID_ADAPTEC_AIC7770: - type = AHC_AIC7770; - break; - case EISA_DEVICE_ID_ADAPTEC_274x: - type = AHC_274; - break; - case EISA_DEVICE_ID_ADAPTEC_284xB: - case EISA_DEVICE_ID_ADAPTEC_284x: - type = AHC_284; - break; - default: - printf("aic7770_attach: Unknown device type!\n"); - return -1; - break; - } - - if(!(ahc = ahc_alloc(unit, iospace->addr, type, AHC_FNONE))) - return -1; - - eisa_reg_start(e_dev); - if(eisa_reg_iospace(e_dev, iospace)) { - ahc_free(ahc); - return -1; - } - - /* - * The IRQMS bit enables level sensitive interrupts. Only allow - * IRQ sharing if it's set. - */ - if(eisa_reg_intr(e_dev, irq, ahc_intr, (void *)ahc, &bio_imask, - /*shared ==*/ahc->pause & IRQMS)) { - ahc_free(ahc); - return -1; - } - eisa_reg_end(e_dev); - -#elif defined(__NetBSD__) || defined(__OpenBSD__) - - struct ahc_data *ahc = (void *)self; + ahc_chip chip; + u_char channel = 'A'; /* Only one channel */ + struct ahc_softc *ahc = (void *)self; struct eisa_attach_args *ea = aux; bus_space_tag_t iot = ea->ea_iot; bus_space_handle_t ioh; @@ -348,49 +162,51 @@ ahc_eisa_attach(parent, self, aux) eisa_chipset_tag_t ec = ea->ea_ec; eisa_intr_handle_t ih; const char *model, *intrstr; - + u_int biosctrl; + u_int scsiconf; + u_int scsiconf1; + + ahc->sc_dmat = ea->ea_dmat; if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) + - AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh)) + AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh)) panic("ahc_eisa_attach: could not map I/O addresses"); if ((irq = ahc_eisa_irq(iot, ioh)) < 0) panic("ahc_eisa_attach: ahc_eisa_irq failed!"); if (strcmp(ea->ea_idstring, "ADP7770") == 0) { model = EISA_PRODUCT_ADP7770; - type = AHC_AIC7770; + chip = AHC_AIC7770|AHC_EISA; } else if (strcmp(ea->ea_idstring, "ADP7771") == 0) { model = EISA_PRODUCT_ADP7771; - type = AHC_274; -#if 0 - } else if (strcmp(ea->ea_idstring, "ADP7756") == 0) { - model = EISA_PRODUCT_ADP7756; - type = AHC_284; - } else if (strcmp(ea->ea_idstring, "ADP7757") == 0) { - model = EISA_PRODUCT_ADP7757; - type = AHC_284; -#endif + chip = AHC_AIC7770|AHC_EISA; } else { panic("ahc_eisa_attach: Unknown device type %s", - ea->ea_idstring); + ea->ea_idstring); } printf(": %s\n", model); - ahc_construct(ahc, iot, ioh, type, AHC_FNONE); + ahc_construct(ahc, iot, ioh, chip, AHC_FNONE, AHC_AIC7770_FE, channel); + + ahc->channel = 'A'; + ahc->channel_b = 'B'; + if (ahc_reset(ahc) != 0) + return; + if (eisa_intr_map(ec, irq, &ih)) { printf("%s: couldn't map interrupt (%d)\n", - ahc->sc_dev.dv_xname, irq); + ahc->sc_dev.dv_xname, irq); return; } -#endif /* defined(__NetBSD__) */ /* * Tell the user what type of interrupts we're using. * usefull for debugging irq problems */ - if(bootverbose) { + if (bootverbose) { printf("%s: Using %s Interrupts\n", - ahc_name(ahc), - ahc->pause & IRQMS ? "Level Sensitive" : "Edge Triggered"); + ahc_name(ahc), + ahc->pause & IRQMS ? + "Level Sensitive" : "Edge Triggered"); } /* @@ -399,36 +215,35 @@ ahc_eisa_attach(parent, self, aux) * * First, the aic7770 card specific setup. */ - switch( ahc->type ) { - case AHC_AIC7770: - case AHC_274: - { - u_char biosctrl = AHC_INB(ahc, HA_274_BIOSCTRL); - - /* Get the primary channel information */ - ahc->flags |= (biosctrl & CHANNEL_B_PRIMARY); - - if((biosctrl & BIOSMODE) == BIOSDISABLED) - ahc->flags |= AHC_USEDEFAULTS; - break; - } - case AHC_284: - { - /* XXX - * All values are automagically intialized at - * POST for these cards, so we can always rely - * on the Scratch Ram values. However, we should - * read the SEEPROM here (Dan has the code to do - * it) so we can say what kind of translation the - * BIOS is using. Printing out the geometry could - * save a lot of users the grief of failed installs. - */ - break; - } - default: - break; + biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL); + scsiconf = ahc_inb(ahc, SCSICONF); + scsiconf1 = ahc_inb(ahc, SCSICONF + 1); + + /* Get the primary channel information */ + if ((biosctrl & CHANNEL_B_PRIMARY) != 0) + ahc->flags |= AHC_CHANNEL_B_PRIMARY; + + if ((biosctrl & BIOSMODE) == BIOSDISABLED) { + ahc->flags |= AHC_USEDEFAULTS; + } else if ((ahc->features & AHC_WIDE) != 0) { + ahc->our_id = scsiconf1 & HWSCSIID; + if (scsiconf & TERM_ENB) + ahc->flags |= AHC_TERM_ENB_A; + } else { + ahc->our_id = scsiconf & HSCSIID; + ahc->our_id_b = scsiconf1 & HSCSIID; + if (scsiconf & TERM_ENB) + ahc->flags |= AHC_TERM_ENB_A; + if (scsiconf1 & TERM_ENB) + ahc->flags |= AHC_TERM_ENB_B; } - + /* + * We have no way to tell, so assume extended + * translation is enabled. + */ + + ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B; + /* * See if we have a Rev E or higher aic7770. Anything below a * Rev E will have a R/O autoflush disable configuration bit. @@ -436,7 +251,7 @@ ahc_eisa_attach(parent, self, aux) * We think it allows 8 bit entries in the QOUTFIFO to support * "paging" SCBs so you can have more than 4 commands active at * once. - */ + */ { char *id_string; u_char sblkctl; @@ -446,8 +261,7 @@ ahc_eisa_attach(parent, self, aux) sblkctl = sblkctl_orig ^ AUTOFLUSHDIS; AHC_OUTB(ahc, SBLKCTL, sblkctl); sblkctl = AHC_INB(ahc, SBLKCTL); - if(sblkctl != sblkctl_orig) - { + if (sblkctl != sblkctl_orig) { id_string = "aic7770 >= Rev E, "; /* * Ensure autoflush is enabled @@ -457,8 +271,7 @@ ahc_eisa_attach(parent, self, aux) /* Allow paging on this adapter */ ahc->flags |= AHC_PAGESCBS; - } - else + } else id_string = "aic7770 <= Rev C, "; printf("%s: %s", ahc_name(ahc), id_string); @@ -474,52 +287,28 @@ ahc_eisa_attach(parent, self, aux) /* * Generic aic7xxx initialization. */ - if(ahc_init(ahc)){ -#if defined(__FreeBSD__) - ahc_free(ahc); - /* - * The board's IRQ line is not yet enabled so it's safe - * to release the irq. - */ - eisa_release_intr(e_dev, irq, ahc_intr); - return -1; -#elif defined(__NetBSD__) || defined(__OpenBSD__) + if (ahc_init(ahc)) { ahc_free(ahc); return; -#endif } - + /* * Enable the board's BUS drivers */ AHC_OUTB(ahc, BCTL, ENABLE); -#if defined(__FreeBSD__) - /* - * Enable our interrupt handler. - */ - if(eisa_enable_intr(e_dev, irq)) { - ahc_free(ahc); - eisa_release_intr(e_dev, irq, ahc_intr); - return -1; - } - - e_dev->kdc->kdc_state = DC_BUSY; /* host adapters always busy */ -#elif defined(__NetBSD__) || defined(__OpenBSD__) intrstr = eisa_intr_string(ec, ih); /* * The IRQMS bit enables level sensitive interrupts only allow * IRQ sharing if its set. */ - ahc->sc_ih = eisa_intr_establish(ec, ih, - ahc->pause & IRQMS ? IST_LEVEL : IST_EDGE, IPL_BIO, ahc_intr, ahc -#ifdef __OpenBSD__ - , ahc->sc_dev.dv_xname -#endif - ); + ahc->sc_ih = eisa_intr_establish(ec, ih, ahc->pause & IRQMS + ? IST_LEVEL : IST_EDGE, + IPL_BIO, ahc_intr, ahc, + ahc->sc_dev.dv_xname); if (ahc->sc_ih == NULL) { printf("%s: couldn't establish interrupt", - ahc->sc_dev.dv_xname); + ahc->sc_dev.dv_xname); if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); @@ -529,14 +318,9 @@ ahc_eisa_attach(parent, self, aux) if (intrstr != NULL) printf("%s: interrupting at %s\n", ahc->sc_dev.dv_xname, intrstr); -#endif /* defined(__NetBSD__) */ /* Attach sub-devices - always succeeds */ ahc_attach(ahc); -#if defined(__FreeBSD__) - return 0; -#endif } - #endif /* NEISA > 0 */ diff --git a/sys/dev/microcode/Makefile.inc b/sys/dev/microcode/Makefile.inc deleted file mode 100644 index d0074735511..00000000000 --- a/sys/dev/microcode/Makefile.inc +++ /dev/null @@ -1,4 +0,0 @@ -# $OpenBSD: Makefile.inc,v 1.3 1997/11/07 08:07:15 niklas Exp $ - -.include "$S/dev/microcode/aic7xxx/Makefile.inc" -.include "$S/dev/microcode/ncr53cxxx/Makefile.inc" diff --git a/sys/dev/microcode/aic7xxx/Makefile b/sys/dev/microcode/aic7xxx/Makefile new file mode 100644 index 00000000000..2b91caf6dcd --- /dev/null +++ b/sys/dev/microcode/aic7xxx/Makefile @@ -0,0 +1,44 @@ +# $OpenBSD: Makefile,v 1.1 2000/03/22 02:50:49 smurph Exp $ +# $FreeBSD: src/sys/dev/aic7xxx/Makefile,v 1.6 1999/08/28 00:41:22 peter Exp $ + +PROG= aicasm + +CSRCS= aicasm.c aicasm_symbol.c +GENSRCS= aicasm_gram.c aicasm_scan.c + +GENHDRS= y.tab.h + +SRCS= ${GENSRCS} ${CSRCS} +CLEANFILES+= ${GENSRCS} ${GENHDRS} y.output +DPADD+= ${LIBL} +LDADD+= -ll + +# Correct path for kernel builds +# Don't rely on the kernel's .depend file +.ifdef MAKESRCPATH +.PATH: ${MAKESRCPATH} +DEPENDFILE= +.endif + +CFLAGS+= -I/usr/include -I. +NOMAN= noman + +.ifdef DEBUG +CFLAGS+= -DDEBUG -g +YFLAGS+= -t +LFLAGS+= -d +MFLAGS= -l seq.lst +.endif + +microcode aic7xxxreg.h aic7xxx_seq.h: aic7xxx.seq aic7xxx.reg + ./aicasm -I/sys ${MFLAGS} -r tempreg.h -o tempseq.h ${.CURDIR}/aic7xxx.seq + grep OpenBSD: ${.CURDIR}/aic7xxx.seq | cat - tempseq.h > aic7xxx_seq.h + grep OpenBSD: ${.CURDIR}/aic7xxx.reg | cat - tempreg.h > aic7xxxreg.h + mv aic7xxx_seq.h /sys/dev/microcode/aic7xxx/ +.ifdef DEBUG + mv seq.lst /sys/dev/microcode/aic7xxx/ +.endif + cp aic7xxxreg.h /sys/dev/ic/ + rm -f tempseq.h tempreg.h + +.include diff --git a/sys/dev/microcode/aic7xxx/Makefile.inc b/sys/dev/microcode/aic7xxx/Makefile.inc deleted file mode 100644 index 7e89b517581..00000000000 --- a/sys/dev/microcode/aic7xxx/Makefile.inc +++ /dev/null @@ -1,17 +0,0 @@ -# $OpenBSD: Makefile.inc,v 1.5 1996/11/28 23:27:58 niklas Exp $ -# $NetBSD: Makefile.inc,v 1.4 1996/05/20 00:48:43 thorpej Exp $ - -.if target(aic7xxx.o) -PATH: $S/dev/microcode/aic7xxx - -aic7xxx.o: aic7xxx_seq.h - -aic7xxx_seq.h: aic7xxx_asm $S/dev/microcode/aic7xxx/aic7xxx.seq - ./aic7xxx_asm -o ${.TARGET} $S/dev/microcode/aic7xxx/aic7xxx.seq - -aic7xxx_asm: $S/dev/microcode/aic7xxx/aic7xxx_asm.c - ${HOSTCC} -U_KERNEL -o ${.TARGET} $< - -clean:: - rm -f aic7xxx_asm -.endif diff --git a/sys/dev/microcode/aic7xxx/aic7xxx.reg b/sys/dev/microcode/aic7xxx/aic7xxx.reg new file mode 100644 index 00000000000..e85bfc7943e --- /dev/null +++ b/sys/dev/microcode/aic7xxx/aic7xxx.reg @@ -0,0 +1,1485 @@ +/* $NetBSD$ */ + +/* + * Aic7xxx register and scratch ram definitions. + * + * Copyright (c) 1994-2000 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * the GNU Public License ("GPL"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.reg,v 1.20 2000/02/09 21:24:59 gibbs Exp $ + */ + +/* + * This file is processed by the aic7xxx_asm utility for use in assembling + * firmware for the aic7xxx family of SCSI host adapters as well as to generate + * a C header file for use in the kernel portion of the Aic7xxx driver. + * + * All page numbers refer to the Adaptec AIC-7770 Data Book available from + * Adaptec's Technical Documents Department 1-800-934-2766 + */ + +/* + * SCSI Sequence Control (p. 3-11). + * Each bit, when set starts a specific SCSI sequence on the bus + */ +register SCSISEQ { + address 0x000 + access_mode RW + bit TEMODE 0x80 + bit ENSELO 0x40 + bit ENSELI 0x20 + bit ENRSELI 0x10 + bit ENAUTOATNO 0x08 + bit ENAUTOATNI 0x04 + bit ENAUTOATNP 0x02 + bit SCSIRSTO 0x01 +} + +/* + * SCSI Transfer Control 0 Register (pp. 3-13). + * Controls the SCSI module data path. + */ +register SXFRCTL0 { + address 0x001 + access_mode RW + bit DFON 0x80 + bit DFPEXP 0x40 + bit FAST20 0x20 + bit CLRSTCNT 0x10 + bit SPIOEN 0x08 + bit SCAMEN 0x04 + bit CLRCHN 0x02 +} + +/* + * SCSI Transfer Control 1 Register (pp. 3-14,15). + * Controls the SCSI module data path. + */ +register SXFRCTL1 { + address 0x002 + access_mode RW + bit BITBUCKET 0x80 + bit SWRAPEN 0x40 + bit ENSPCHK 0x20 + mask STIMESEL 0x18 + bit ENSTIMER 0x04 + bit ACTNEGEN 0x02 + bit STPWEN 0x01 /* Powered Termination */ +} + +/* + * SCSI Control Signal Read Register (p. 3-15). + * Reads the actual state of the SCSI bus pins + */ +register SCSISIGI { + address 0x003 + access_mode RO + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + bit ATNI 0x10 + bit SELI 0x08 + bit BSYI 0x04 + bit REQI 0x02 + bit ACKI 0x01 +/* + * Possible phases in SCSISIGI + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Control 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. + */ +register SCSISIGO { + address 0x003 + access_mode WO + bit CDO 0x80 + bit IOO 0x40 + bit MSGO 0x20 + bit ATNO 0x10 + bit SELO 0x08 + bit BSYO 0x04 + bit REQO 0x02 + bit ACKO 0x01 +/* + * Possible phases to write into SCSISIG0 + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Rate Control (p. 3-17). + * Contents of this register determine the Synchronous SCSI data transfer + * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the + * SOFS (3:0) bits disables synchronous data transfers. Any offset value + * greater than 0 enables synchronous transfers. + */ +register SCSIRATE { + address 0x004 + access_mode RW + bit WIDEXFER 0x80 /* Wide transfer control */ + bit ENABLE_CRC 0x40 /* CRC for D-Phases */ + bit SINGLE_EDGE 0x10 /* Disable DT Transfers */ + mask SXFR 0x70 /* Sync transfer rate */ + mask SXFR_ULTRA2 0x0f /* Sync transfer rate */ + mask SOFS 0x0f /* Sync offset */ +} + +/* + * SCSI ID (p. 3-18). + * Contains the ID of the board and the current target on the + * selected channel. + */ +register SCSIID { + address 0x005 + access_mode RW + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ + /* + * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book) + * The aic7890/91 allow an offset of up to 127 transfers in both wide + * and narrow mode. + */ + alias SCSIOFFSET + mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */ +} + +/* + * SCSI Latched Data (p. 3-19). + * Read/Write latches used to transfer data on the SCSI bus during + * Automatic or Manual PIO mode. SCSIDATH can be used for the + * upper byte of a 16bit wide asynchronouse data phase transfer. + */ +register SCSIDATL { + address 0x006 + access_mode RW +} + +register SCSIDATH { + address 0x007 + access_mode RW +} + +/* + * SCSI Transfer Count (pp. 3-19,20) + * These registers count down the number of bytes transferred + * across the SCSI bus. The counter is decremented only once + * the data has been safely transferred. SDONE in SSTAT0 is + * set when STCNT goes to 0 + */ +register STCNT { + address 0x008 + size 3 + access_mode RW +} + +/* ALT_MODE register on Ultra160 chips */ +register OPTIONMODE { + address 0x008 + access_mode RW + bit AUTORATEEN 0x80 + bit AUTOACKEN 0x40 + bit ATNMGMNTEN 0x20 + bit BUSFREEREV 0x10 + bit EXPPHASEDIS 0x08 + bit SCSIDATL_IMGEN 0x04 + bit AUTO_MSGOUT_DE 0x02 + bit DIS_MSGIN_DUALEDGE 0x01 + mask OPTIONMODE_DEFAULTS AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE +} + +/* ALT_MODE register on Ultra160 chips */ +register TARGCRCCNT { + address 0x00a + size 2 + access_mode RW +} + +/* + * Clear SCSI Interrupt 0 (p. 3-20) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0. + */ +register CLRSINT0 { + address 0x00b + access_mode WO + bit CLRSELDO 0x40 + bit CLRSELDI 0x20 + bit CLRSELINGO 0x10 + bit CLRSWRAP 0x08 + bit CLRSPIORDY 0x02 +} + +/* + * SCSI Status 0 (p. 3-21) + * Contains one set of SCSI Interrupt codes + * These are most likely of interest to the sequencer + */ +register SSTAT0 { + address 0x00b + access_mode RO + bit TARGET 0x80 /* Board acting as target */ + bit SELDO 0x40 /* Selection Done */ + bit SELDI 0x20 /* Board has been selected */ + bit SELINGO 0x10 /* Selection In Progress */ + bit SWRAP 0x08 /* 24bit counter wrap */ + bit IOERR 0x08 /* LVD Tranceiver mode changed */ + bit SDONE 0x04 /* STCNT = 0x000000 */ + bit SPIORDY 0x02 /* SCSI PIO Ready */ + bit 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. + */ +register CLRSINT1 { + address 0x00c + access_mode WO + bit CLRSELTIMEO 0x80 + bit CLRATNO 0x40 + bit CLRSCSIRSTI 0x20 + bit CLRBUSFREE 0x08 + bit CLRSCSIPERR 0x04 + bit CLRPHASECHG 0x02 + bit CLRREQINIT 0x01 +} + +/* + * SCSI Status 1 (p. 3-24) + */ +register SSTAT1 { + address 0x00c + access_mode RO + bit SELTO 0x80 + bit ATNTARG 0x40 + bit SCSIRSTI 0x20 + bit PHASEMIS 0x10 + bit BUSFREE 0x08 + bit SCSIPERR 0x04 + bit PHASECHG 0x02 + bit REQINIT 0x01 +} + +/* + * SCSI Status 2 (pp. 3-25,26) + */ +register SSTAT2 { + address 0x00d + access_mode RO + bit OVERRUN 0x80 + bit EXP_ACTIVE 0x10 /* SCSI Expander Active */ + mask SFCNT 0x1f +} + +/* + * SCSI Status 3 (p. 3-26) + */ +register SSTAT3 { + address 0x00e + access_mode RO + mask SCSICNT 0xf0 + mask OFFCNT 0x0f +} + +/* + * SCSI ID for the aic7890/91 chips + */ +register SCSIID_ULTRA2 { + address 0x00f + access_mode RW + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ +} + +/* + * SCSI Interrupt Mode 1 (p. 3-28) + * Setting any bit will enable the corresponding function + * in SIMODE0 to interrupt via the IRQ pin. + */ +register SIMODE0 { + address 0x010 + access_mode RW + bit ENSELDO 0x40 + bit ENSELDI 0x20 + bit ENSELINGO 0x10 + bit ENSWRAP 0x08 + bit ENIOERR 0x08 /* LVD Tranceiver mode changes */ + bit ENSDONE 0x04 + bit ENSPIORDY 0x02 + bit ENDMADONE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (pp. 3-28,29) + * Setting any bit will enable the corresponding function + * in SIMODE1 to interrupt via the IRQ pin. + */ +register SIMODE1 { + address 0x011 + access_mode RW + bit ENSELTIMO 0x80 + bit ENATNTARG 0x40 + bit ENSCSIRST 0x20 + bit ENPHASEMIS 0x10 + bit ENBUSFREE 0x08 + bit ENSCSIPERR 0x04 + bit ENPHASECHG 0x02 + bit ENREQINIT 0x01 +} + +/* + * SCSI Data Bus (High) (p. 3-29) + * This register reads data on the SCSI Data bus directly. + */ +register SCSIBUSL { + address 0x012 + access_mode RO +} + +register SCSIBUSH { + address 0x013 + access_mode RO +} + +/* + * SCSI/Host Address (p. 3-30) + * These registers hold the host address for the byte about to be + * transferred on the SCSI bus. They are counted up in the same + * manner as STCNT is counted down. SHADDR should always be used + * to determine the address of the last byte transferred since HADDR + * can be skewed by write ahead. + */ +register SHADDR { + address 0x014 + size 4 + access_mode RO +} + +/* + * Selection Timeout Timer (p. 3-30) + */ +register SELTIMER { + address 0x018 + access_mode RW + bit STAGE6 0x20 + bit STAGE5 0x10 + bit STAGE4 0x08 + bit STAGE3 0x04 + bit STAGE2 0x02 + bit STAGE1 0x01 + alias TARGIDIN +} + +/* + * 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. + */ +register SELID { + address 0x019 + access_mode RW + mask SELID_MASK 0xf0 + bit ONEBIT 0x08 +} + +register SCAMCTL { + address 0x01a + access_mode RW + bit ENSCAMSELO 0x80 + bit CLRSCAMSELID 0x40 + bit ALTSTIM 0x20 + bit DFLTTID 0x10 + mask SCAMLVL 0x03 +} + +/* + * Target Mode Selecting in ID bitmask (aic7890/91/96/97) + */ +register TARGID { + address 0x01b + size 2 + access_mode RW +} + +/* + * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book) + * Indicates if external logic has been attached to the chip to + * perform the tasks of accessing a serial eeprom, testing termination + * strength, and performing cable detection. On the aic7860, most of + * these features are handled on chip, but on the aic7855 an attached + * aic3800 does the grunt work. + */ +register SPIOCAP { + address 0x01b + access_mode RW + bit SOFT1 0x80 + bit SOFT0 0x40 + bit SOFTCMDEN 0x20 + bit HAS_BRDCTL 0x10 /* External Board control */ + bit SEEPROM 0x08 /* External serial eeprom logic */ + bit EEPROM 0x04 /* Writable external BIOS ROM */ + bit ROM 0x02 /* Logic for accessing external ROM */ + bit SSPIOCPS 0x01 /* Termination and cable detection */ +} + +register BRDCTL { + address 0x01d + bit BRDDAT7 0x80 + bit BRDDAT6 0x40 + bit BRDDAT5 0x20 + bit BRDSTB 0x10 + bit BRDCS 0x08 + bit BRDRW 0x04 + bit BRDCTL1 0x02 + bit BRDCTL0 0x01 + /* 7890 Definitions */ + bit BRDDAT4 0x10 + bit BRDDAT3 0x08 + bit BRDDAT2 0x04 + bit BRDRW_ULTRA2 0x02 + bit BRDSTB_ULTRA2 0x01 +} + +/* + * Serial EEPROM Control (p. 4-92 in 7870 Databook) + * Controls the reading and writing of an external serial 1-bit + * EEPROM Device. In order to access the serial EEPROM, you must + * first set the SEEMS bit that generates a request to the memory + * port for access to the serial EEPROM device. When the memory + * port is not busy servicing another request, it reconfigures + * to allow access to the serial EEPROM. When this happens, SEERDY + * gets set high to verify that the memory port access has been + * granted. + * + * After successful arbitration for the memory port, the SEECS bit of + * the SEECTL register is connected to the chip select. The SEECK, + * SEEDO, and SEEDI are connected to the clock, data out, and data in + * lines respectively. The SEERDY bit of SEECTL is useful in that it + * gives us an 800 nsec timer. After a write to the SEECTL register, + * the SEERDY goes high 800 nsec later. The one exception to this is + * when we first request access to the memory port. The SEERDY goes + * high to signify that access has been granted and, for this case, has + * no implied timing. + * + * See 93cx6.c for detailed information on the protocol necessary to + * read the serial EEPROM. + */ +register SEECTL { + address 0x01e + bit EXTARBACK 0x80 + bit EXTARBREQ 0x40 + bit SEEMS 0x20 + bit SEERDY 0x10 + bit SEECS 0x08 + bit SEECK 0x04 + bit SEEDO 0x02 + bit SEEDI 0x01 +} +/* + * 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. + */ +register SBLKCTL { + address 0x01f + access_mode RW + bit DIAGLEDEN 0x80 /* Aic78X0 only */ + bit DIAGLEDON 0x40 /* Aic78X0 only */ + bit AUTOFLUSHDIS 0x20 + bit SELBUSB 0x08 + bit ENAB40 0x08 /* LVD transceiver active */ + bit ENAB20 0x04 /* SE/HVD transceiver active */ + bit SELWIDE 0x02 + bit XCVR 0x01 /* External transceiver active */ +} + +/* + * Sequencer Control (p. 3-33) + * Error detection mode and speed configuration + */ +register SEQCTL { + address 0x060 + access_mode RW + bit PERRORDIS 0x80 + bit PAUSEDIS 0x40 + bit FAILDIS 0x20 + bit FASTMODE 0x10 + bit BRKADRINTEN 0x08 + bit STEP 0x04 + bit SEQRESET 0x02 + bit 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 succession. The SEQADDRs will increment after the most + * significant byte is written + */ +register SEQRAM { + address 0x061 + access_mode RW +} + +/* + * Sequencer Address Registers (p. 3-35) + * Only the first bit of SEQADDR1 holds addressing information + */ +register SEQADDR0 { + address 0x062 + access_mode RW +} + +register SEQADDR1 { + address 0x063 + access_mode RW + mask SEQADDR1_MASK 0x01 +} + +/* + * Accumulator + * We cheat by passing arguments in the Accumulator up to the kernel driver + */ +register ACCUM { + address 0x064 + access_mode RW + accumulator +} + +register SINDEX { + address 0x065 + access_mode RW + sindex +} + +register DINDEX { + address 0x066 + access_mode RW +} + +register ALLONES { + address 0x069 + access_mode RO + allones +} + +register ALLZEROS { + address 0x06a + access_mode RO + allzeros +} + +register NONE { + address 0x06a + access_mode WO + none +} + +register FLAGS { + address 0x06b + access_mode RO + bit ZERO 0x02 + bit CARRY 0x01 +} + +register SINDIR { + address 0x06c + access_mode RO +} + +register DINDIR { + address 0x06d + access_mode WO +} + +register FUNCTION1 { + address 0x06e + access_mode RW +} + +register STACK { + address 0x06f + access_mode RO +} + +/* + * Board Control (p. 3-43) + */ +register BCTL { + address 0x084 + access_mode RW + bit ACE 0x08 + bit ENABLE 0x01 +} + +/* + * On the aic78X0 chips, Board Control is replaced by the DSCommand + * register (p. 4-64) + */ +register DSCOMMAND0 { + address 0x084 + access_mode RW + bit CACHETHEN 0x80 /* Cache Threshold enable */ + bit DPARCKEN 0x40 /* Data Parity Check Enable */ + bit MPARCKEN 0x20 /* Memory Parity Check Enable */ + bit EXTREQLCK 0x10 /* External Request Lock */ + /* aic7890/91/96/97 only */ + bit INTSCBRAMSEL 0x08 /* Internal SCB RAM Select */ + bit RAMPS 0x04 /* External SCB RAM Present */ + bit USCBSIZE32 0x02 /* Use 32byte SCB Page Size */ + bit CIOPARCKEN 0x01 /* Internal bus parity error enable */ +} + +/* + * Bus On/Off Time (p. 3-44) + */ +register BUSTIME { + address 0x085 + access_mode RW + mask BOFF 0xf0 + mask BON 0x0f +} + +/* + * Bus Speed (p. 3-45) aic7770 only + */ +register BUSSPD { + address 0x086 + access_mode RW + mask DFTHRSH 0xc0 + mask STBOFF 0x38 + mask STBON 0x07 + mask DFTHRSH_100 0xc0 +} + +/* aic7850/55/60/70/80/95 only */ +register DSPCISTATUS { + address 0x086 + mask DFTHRSH_100 0xc0 +} + +/* aic7890/91/96/97 only */ +register HS_MAILBOX { + address 0x086 + mask HOST_MAILBOX 0xF0 + mask SEQ_MAILBOX 0x0F +} + +const HOST_MAILBOX_SHIFT 4 +const SEQ_MAILBOX_SHIFT 0 + +/* + * Host Control (p. 3-47) R/W + * Overall host control of the device. + */ +register HCNTRL { + address 0x087 + access_mode RW + bit POWRDN 0x40 + bit SWINT 0x10 + bit IRQMS 0x08 + bit PAUSE 0x04 + bit INTEN 0x02 + bit CHIPRST 0x01 + bit CHIPRSTACK 0x01 +} + +/* + * Host Address (p. 3-48) + * This register contains the address of the byte about + * to be transferred across the host bus. + */ +register HADDR { + address 0x088 + size 4 + access_mode RW +} + +register HCNT { + address 0x08c + size 3 + access_mode RW +} + +/* + * SCB Pointer (p. 3-49) + * Gate one of the four SCBs into the SCBARRAY window. + */ +register SCBPTR { + address 0x090 + access_mode RW +} + +/* + * Interrupt Status (p. 3-50) + * Status for system interrupts + */ +register INTSTAT { + address 0x091 + access_mode RW + bit BRKADRINT 0x08 + bit SCSIINT 0x04 + bit CMDCMPLT 0x02 + bit SEQINT 0x01 + mask BAD_PHASE SEQINT /* unknown scsi bus phase */ + mask SEND_REJECT 0x10|SEQINT /* sending a message reject */ + mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/ + mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */ + mask UPDATE_TMSG_REQ 0x60|SEQINT /* Update TMSG_REQ values */ + mask BAD_STATUS 0x70|SEQINT /* Bad status from target */ + mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */ + mask TRACE_POINT 0x90|SEQINT + mask HOST_MSG_LOOP 0xa0|SEQINT /* + * The bus is ready for the + * host to perform another + * message transaction. This + * mechanism is used for things + * like sync/wide negotiation + * that require a kernel based + * message state engine. + */ + mask PERR_DETECTED 0xb0|SEQINT /* + * Either the phase_lock + * or inb_next routine has + * noticed a parity error. + */ + mask TRACEPOINT 0xd0|SEQINT + mask MSGIN_PHASEMIS 0xe0|SEQINT /* + * Target changed phase on us + * when we were expecting + * another msgin byte. + */ + mask DATA_OVERRUN 0xf0|SEQINT /* + * Target attempted to write + * beyond the bounds of its + * command. + */ + + mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */ + mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT) +} + +/* + * Hard Error (p. 3-53) + * Reporting of catastrophic errors. You usually cannot recover from + * these without a full board reset. + */ +register ERROR { + address 0x092 + access_mode RO + bit CIOPARERR 0x80 /* Ultra2 only */ + bit PCIERRSTAT 0x40 /* PCI only */ + bit MPARERR 0x20 /* PCI only */ + bit DPARERR 0x10 /* PCI only */ + bit SQPARERR 0x08 + bit ILLOPCODE 0x04 + bit ILLSADDR 0x02 + bit ILLHADDR 0x01 +} + +/* + * Clear Interrupt Status (p. 3-52) + */ +register CLRINT { + address 0x092 + access_mode WO + bit CLRPARERR 0x10 /* PCI only */ + bit CLRBRKADRINT 0x08 + bit CLRSCSIINT 0x04 + bit CLRCMDINT 0x02 + bit CLRSEQINT 0x01 +} + +register DFCNTRL { + address 0x093 + access_mode RW + bit PRELOADEN 0x80 /* aic7890 only */ + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 +} + +register DFSTATUS { + address 0x094 + access_mode RO + bit PRELOAD_AVAIL 0x80 + bit DWORDEMP 0x20 + bit MREQPEND 0x10 + bit HDONE 0x08 + bit DFTHRESH 0x04 + bit FIFOFULL 0x02 + bit FIFOEMP 0x01 +} + +register DFWADDR { + address 0x95 + access_mode RW +} + +register DFRADDR { + address 0x97 + access_mode RW +} + +register DFDAT { + address 0x099 + access_mode RW +} + +/* + * 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 + */ +register SCBCNT { + address 0x09a + access_mode RW + bit SCBAUTO 0x80 + mask SCBCNT_MASK 0x1f +} + +/* + * Queue In FIFO (p. 3-60) + * Input queue for queued SCBs (commands that the seqencer has yet to start) + */ +register QINFIFO { + address 0x09b + access_mode RW +} + +/* + * Queue In Count (p. 3-60) + * Number of queued SCBs + */ +register QINCNT { + address 0x09c + access_mode RO +} + +/* + * Queue Out FIFO (p. 3-61) + * Queue of SCBs that have completed and await the host + */ +register QOUTFIFO { + address 0x09d + access_mode WO +} + +register CRCCONTROL1 { + address 0x09d + access_mode RW + bit CRCONSEEN 0x80 + bit CRCVALCHKEN 0x40 + bit CRCENDCHKEN 0x20 + bit CRCREQCHKEN 0x10 + bit TARGCRCENDEN 0x08 + bit TARGCRCCNTEN 0x04 +} + + +/* + * Queue Out Count (p. 3-61) + * Number of queued SCBs in the Out FIFO + */ +register QOUTCNT { + address 0x09e + access_mode RO +} + +register SCSIPHASE { + address 0x09e + access_mode RO + bit STATUS_PHASE 0x20 + bit COMMAND_PHASE 0x10 + bit MSG_IN_PHASE 0x08 + bit MSG_OUT_PHASE 0x04 + bit DATA_IN_PHASE 0x02 + bit DATA_OUT_PHASE 0x01 +} + +/* + * Special Function + */ +register SFUNCT { + address 0x09f + access_mode RW + bit ALT_MODE 0x80 +} + +/* + * SCB Definition (p. 5-4) + */ +scb { + address 0x0a0 + SCB_CONTROL { + size 1 + bit TARGET_SCB 0x80 + bit DISCENB 0x40 + bit TAG_ENB 0x20 + bit MK_MESSAGE 0x10 + bit ULTRAENB 0x08 + bit DISCONNECTED 0x04 + mask SCB_TAG_TYPE 0x03 + } + SCB_TCL { + size 1 + bit SELBUSB 0x08 + mask TID 0xf0 + mask LID 0x07 + } + SCB_TARGET_STATUS { + size 1 + } + SCB_SGCOUNT { + size 1 + } + SCB_SGPTR { + size 4 + } + SCB_RESID_SGCNT { + size 1 + } + SCB_RESID_DCNT { + size 3 + } + SCB_DATAPTR { + size 4 + } + SCB_DATACNT { + /* + * Really only 3 bytes, but padded to make + * the kernel's job easier. + */ + size 4 + } + SCB_CMDPTR { + alias SCB_TARGET_PHASES + bit TARGET_DATA_IN 0x1 /* In the second byte */ + size 4 + } + SCB_CMDLEN { + alias SCB_INITIATOR_TAG + size 1 + } + SCB_TAG { + size 1 + } + SCB_NEXT { + size 1 + } + SCB_SCSIRATE { + size 1 + } + SCB_SCSIOFFSET { + size 1 + } + SCB_SPARE { + size 3 + } + SCB_CMDSTORE { + size 16 + } + SCB_CMDSTORE_BUSADDR { + size 4 + } + SCB_64BYTE_SPARE { + size 12 + } +} + +const SCB_32BYTE_SIZE 28 +const SCB_64BYTE_SIZE 48 + +const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */ + +/* --------------------- AHA-2840-only definitions -------------------- */ + +register SEECTL_2840 { + address 0x0c0 + access_mode RW + bit CS_2840 0x04 + bit CK_2840 0x02 + bit DO_2840 0x01 +} + +register STATUS_2840 { + address 0x0c1 + access_mode RW + bit EEPROM_TF 0x80 + mask BIOS_SEL 0x60 + mask ADSEL 0x1e + bit DI_2840 0x01 +} + +/* --------------------- AIC-7870-only definitions -------------------- */ + +register CCHADDR { + address 0x0E0 + size 8 +} + +register CCHCNT { + address 0x0E8 +} + +register CCSGRAM { + address 0x0E9 +} + +register CCSGADDR { + address 0x0EA +} + +register CCSGCTL { + address 0x0EB + bit CCSGDONE 0x80 + bit CCSGEN 0x08 + bit FLAG 0x02 + bit CCSGRESET 0x01 +} + +register CCSCBCNT { + address 0xEF +} + +register CCSCBCTL { + address 0x0EE + bit CCSCBDONE 0x80 + bit ARRDONE 0x40 /* SCB Array prefetch done */ + bit CCARREN 0x10 + bit CCSCBEN 0x08 + bit CCSCBDIR 0x04 + bit CCSCBRESET 0x01 +} + +register CCSCBADDR { + address 0x0ED +} + +register CCSCBRAM { + address 0xEC +} + +/* + * SCB bank address (7895/7896/97 only) + */ +register SCBBADDR { + address 0x0F0 + access_mode RW +} + +register CCSCBPTR { + address 0x0F1 +} + +register HNSCB_QOFF { + address 0x0F4 +} + +register SNSCB_QOFF { + address 0x0F6 +} + +register SDSCB_QOFF { + address 0x0F8 +} + +register QOFF_CTLSTA { + address 0x0FA + bit SCB_AVAIL 0x40 + bit SNSCB_ROLLOVER 0x20 + bit SDSCB_ROLLOVER 0x10 + mask SCB_QSIZE 0x07 + mask SCB_QSIZE_256 0x06 +} + +register DFF_THRSH { + address 0x0FB + mask WR_DFTHRSH 0x70 + mask RD_DFTHRSH 0x07 + mask RD_DFTHRSH_MIN 0x00 + mask RD_DFTHRSH_25 0x01 + mask RD_DFTHRSH_50 0x02 + mask RD_DFTHRSH_63 0x03 + mask RD_DFTHRSH_75 0x04 + mask RD_DFTHRSH_85 0x05 + mask RD_DFTHRSH_90 0x06 + mask RD_DFTHRSH_MAX 0x07 + mask WR_DFTHRSH_MIN 0x00 + mask WR_DFTHRSH_25 0x10 + mask WR_DFTHRSH_50 0x20 + mask WR_DFTHRSH_63 0x30 + mask WR_DFTHRSH_75 0x40 + mask WR_DFTHRSH_85 0x50 + mask WR_DFTHRSH_90 0x60 + mask WR_DFTHRSH_MAX 0x70 +} + +register SG_CACHEPTR { + access_mode RW + address 0x0fc + mask SG_USER_DATA 0xfc + bit LAST_SEG 0x02 + bit LAST_SEG_DONE 0x01 +} + +/* ---------------------- Scratch RAM Offsets ------------------------- */ +/* These offsets are either to values that are initialized by the board's + * BIOS or are specified by the sequencer code. + * + * 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 regardless of + * whether the bios has been installed. + */ + +scratch_ram { + address 0x020 + + /* + * 1 byte per target starting at this address for configuration values + */ + TARG_SCSIRATE { + alias CMDSIZE_TABLE + size 16 + } + /* + * Bit vector of targets that have ULTRA enabled. + */ + ULTRA_ENB { + size 2 + } + /* + * Bit vector of targets that have disconnection disabled. + */ + DISC_DSB { + size 2 + } + /* + * Single byte buffer used to designate the type or message + * to send to a target. + */ + MSG_OUT { + size 1 + } + /* Parameters for DMA Logic */ + DMAPARAMS { + size 1 + bit PRELOADEN 0x80 + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 + } + SEQ_FLAGS { + size 1 + bit IDENTIFY_SEEN 0x80 + bit SCBPTR_VALID 0x40 + bit DPHASE 0x20 + /* Target flags */ + bit TARG_CMD_PENDING 0x10 + bit CMDPHASE_PENDING 0x08 + bit DPHASE_PENDING 0x04 + bit SPHASE_PENDING 0x02 + bit NO_DISCONNECT 0x01 + } + /* + * Temporary storage for the + * target/channel/lun of a + * reconnecting target + */ + SAVED_TCL { + size 1 + } + /* Working value of the number of SG segments left */ + SG_COUNT { + size 1 + } + /* Working value of SG pointer */ + SG_NEXT { + size 4 + } + /* + * The last bus phase as seen by the sequencer. + */ + LASTPHASE { + size 1 + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI + mask P_BUSFREE 0x01 + } + /* + * head of list of SCBs awaiting + * selection + */ + WAITING_SCBH { + size 1 + } + /* + * head of list of SCBs that are + * disconnected. Used for SCB + * paging. + */ + DISCONNECTED_SCBH { + size 1 + } + /* + * head of list of SCBs that are + * not in use. Used for SCB paging. + */ + FREE_SCBH { + size 1 + } + /* + * Address of the hardware scb array in the host. + */ + HSCB_ADDR { + size 4 + } + /* + * Address of the 256 byte array storing the SCBID of outstanding + * untagged SCBs indexed by TCL. + */ + SCBID_ADDR { + size 4 + } + /* + * Address of the array of command descriptors used to store + * information about incoming selections. + */ + TMODE_CMDADDR { + size 4 + } + KERNEL_QINPOS { + size 1 + } + QINPOS { + size 1 + } + QOUTPOS { + size 1 + } + /* + * Kernel and sequencer offsets into the queue of + * incoming target mode command descriptors. The + * queue is full when the KERNEL_TQINPOS == TQINPOS. + */ + KERNEL_TQINPOS { + size 1 + } + TQINPOS { + size 1 + } + ARG_1 { + size 1 + mask SEND_MSG 0x80 + mask SEND_SENSE 0x40 + mask SEND_REJ 0x20 + mask MSGOUT_PHASEMIS 0x10 + mask EXIT_MSG_LOOP 0x08 + mask CONT_MSG_LOOP 0x04 + mask CONT_TARG_SESSION 0x02 + alias RETURN_1 + } + ARG_2 { + size 1 + alias RETURN_2 + } + + /* + * Snapshot of MSG_OUT taken after each message is sent. + */ + LAST_MSG { + size 1 + } + + /* + * Number of times we have filled the CCSGRAM with prefetched + * SG elements. + */ + PREFETCH_CNT { + size 1 + } + + /* + * Interrupt kernel for a message to this target on + * the next transaction. This is usually used for + * negotiation requests. + */ + TARGET_MSG_REQUEST { + size 2 + } + + /* + * Sequences the kernel driver has okayed for us. This allows + * the driver to do things like prevent initiator or target + * operations. + */ + SCSISEQ_TEMPLATE { + size 1 + bit ENSELO 0x40 + bit ENSELI 0x20 + bit ENRSELI 0x10 + bit ENAUTOATNO 0x08 + bit ENAUTOATNI 0x04 + bit ENAUTOATNP 0x02 + } + + /* + * Track whether the transfer byte count for + * the current data phase is odd. + */ + DATA_COUNT_ODD { + size 1 + } + + /* + * The initiator specified tag for this target mode transaction. + */ + INITIATOR_TAG { + size 1 + } + + /* + * These are reserved registers in the card's scratch ram. Some of + * the values are specified in the AHA2742 technical reference manual + * and are initialized by the BIOS at boot time. + */ + SCSICONF { + address 0x05a + size 1 + bit TERM_ENB 0x80 + bit RESET_SCSI 0x40 + bit ENSPCHK 0x20 + mask HSCSIID 0x07 /* our SCSI ID */ + mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */ + } + HOSTCONF { + address 0x05d + size 1 + } + HA_274_BIOSCTRL { + address 0x05f + size 1 + mask BIOSMODE 0x30 + mask BIOSDISABLED 0x30 + bit CHANNEL_B_PRIMARY 0x08 + } + /* + * Per target SCSI offset values for Ultra2 controllers. + */ + TARG_OFFSET { + address 0x070 + size 16 + } +} + +const SCB_LIST_NULL 0xff +const TARGET_CMD_CMPLT 0xfe + +const CCSGADDR_MAX 0x80 +const CCSGRAM_MAXSEGS 16 + +/* Offsets into the SCBID array where different data is stored */ +const QOUTFIFO_OFFSET 0 +const QINFIFO_OFFSET 1 +const UNTAGGEDSCB_OFFSET 2 + +/* WDTR Message values */ +const BUS_8_BIT 0x00 +const BUS_16_BIT 0x01 +const BUS_32_BIT 0x02 + +/* Offset maximums */ +const MAX_OFFSET_8BIT 0x0f +const MAX_OFFSET_16BIT 0x08 +const MAX_OFFSET_ULTRA2 0x7f +const HOST_MSG 0xff + +/* Target mode command processing constants */ +const CMD_GROUP_CODE_SHIFT 0x05 + +const TCL_TARGET_SHIFT 4 +/* The update interval must be a power of 2 */ +const TQINFIFO_UPDATE_CNT 32 + +const STATUS_BUSY 0x08 +const STATUS_QUEUE_FULL 0x28 + +/* + * Downloaded (kernel inserted) constants + */ + +/* + * Number of command descriptors in the command descriptor array. + * No longer used, but left here as an example for how downloaded + * constantants can be defined. +const TMODE_NUMCMDS download + */ diff --git a/sys/dev/microcode/aic7xxx/aic7xxx.seq b/sys/dev/microcode/aic7xxx/aic7xxx.seq index 97c3f615375..6f5210b2497 100644 --- a/sys/dev/microcode/aic7xxx/aic7xxx.seq +++ b/sys/dev/microcode/aic7xxx/aic7xxx.seq @@ -1,71 +1,45 @@ -/* $OpenBSD: aic7xxx.seq,v 1.6 1996/12/03 11:28:32 niklas Exp $ */ -/* $NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $ */ - -/*+M*********************************************************************** - *Adaptec 274x/284x/294x device driver for Linux and FreeBSD. - * - *Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * All rights reserved. +/* + * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. * - *FreeBSD, Twin, Wide, 2 command per target support, tagged queuing, - *SCB paging and other optimizations: - *Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved. + * Copyright (c) 1994-2000 Justin Gibbs. + * All rights reserved. * - *Redistribution and use in source and binary forms, with or without - *modification, are permitted provided that the following conditions - *are met: - *1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer. - *2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of Calgary - * Department of Computer Science and its contributors. - *4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. * - *THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - *ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - *ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - *OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - *HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - *LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - *OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - *SUCH DAMAGE. + * Alternatively, this software may be distributed under the terms of the + * the GNU Public License ("GPL"). * - * from Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. * - *-M************************************************************************/ - -VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $" + * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.93 2000/01/07 23:08:20 gibbs Exp $ + */ -#if defined(__OpenBSD__) -#include +#include #include -#elif defined(__NetBSD__) -#include "../../ic/aic7xxxreg.h" -#include "../../../scsi/scsi_message.h" -#elif defined(__FreeBSD__) -#include "../../dev/aic7xxx/aic7xxx_reg.h" -#include "../../scsi/scsi_message.h" -#endif - /* - * We can't just use ACCUM in the sequencer code because it - * must be treated specially by the assembler, and it currently - * looks for the symbol 'A'. This is the only register defined in - * the assembler's symbol space. - */ -A = ACCUM +#include +*/ -/* After starting the selection hardware, we check for reconnecting targets +/* + * A few words on the waiting SCB list: + * After starting the selection hardware, we check for reconnecting targets * as well as for our selection to complete just in case the reselection wins * bus arbitration. The problem with this is that we must keep track of the * SCB that we've already pulled from the QINFIFO and started the selection @@ -74,311 +48,762 @@ A = ACCUM * in scratch ram since a reconnecting target can request sense and this will * create yet another SCB waiting for selection. The solution used here is to * use byte 27 of the SCB as a psuedo-next pointer and to thread a list - * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, - * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must - * add an entry to this list everytime a request sense occurs. The sequencer - * will automatically consume the entries. + * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, + * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to + * this list everytime a request sense occurs or after completing a non-tagged + * command for which a second SCB has been queued. The sequencer will + * automatically consume the entries. */ -/* - * We assume that the kernel driver may reset us at any time, even in the - * middle of a DMA, so clear DFCNTRL too. - */ reset: - clr DFCNTRL - clr SCSISIGO /* De-assert BSY */ -/* - * We jump to start after every bus free. - */ -start: - and FLAGS,0x0f /* clear target specific flags */ - mvi SCSISEQ,ENRSELI /* Always allow reselection */ - clr SCSIRATE /* - * We don't know the target we will - * connect to, so default to narrow - * transfers to avoid parity problems. - */ + clr SCSISIGO; /* De-assert BSY */ + mvi MSG_OUT, MSG_NOOP; /* No message to send */ + and SXFRCTL1, ~BITBUCKET; + /* Always allow reselection */ + and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + /* Ensure that no DMA operations are in progress */ + clr CCSGCTL; + clr CCSCBCTL; + } + poll_for_work: - /* - * Are we a twin channel device? - * For fairness, we check the other bus first, - * since we just finished a transaction on the - * current channel. - */ - test FLAGS,TWIN_BUS jz start2 - xor SBLKCTL,SELBUSB /* Toggle to the other bus */ - test SSTAT0,SELDI jnz reselect - xor SBLKCTL,SELBUSB /* Toggle to the original bus */ -start2: - test SSTAT0,SELDI jnz reselect - cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting - mov A, QCNTMASK - test QINCNT,A jz poll_for_work + call clear_target_state; + and SXFRCTL0, ~SPIOEN; + if ((ahc->features & AHC_QUEUE_REGS) == 0) { + mov A, QINPOS; + } +poll_for_work_loop: + if ((ahc->features & AHC_QUEUE_REGS) == 0) { + and SEQCTL, ~PAUSEDIS; + } + test SSTAT0, SELDO|SELDI jnz selection; + test SCSISEQ, ENSELO jnz poll_for_work; + if ((ahc->features & AHC_TWIN) != 0) { + /* + * Twin channel devices cannot handle things like SELTO + * interrupts on the "background" channel. So, if we + * are selecting, keep polling the current channel util + * either a selection or reselection occurs. + */ + xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + test SSTAT0, SELDO|SELDI jnz selection; + test SCSISEQ, ENSELO jnz poll_for_work; + xor SBLKCTL,SELBUSB; /* Toggle back */ + } + cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; +test_queue: + /* Has the driver posted any work for us? */ + if ((ahc->features & AHC_QUEUE_REGS) != 0) { + test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; + mov NONE, SNSCB_QOFF; + inc QINPOS; + } else { + or SEQCTL, PAUSEDIS; + cmp KERNEL_QINPOS, A je poll_for_work_loop; + inc QINPOS; + and SEQCTL, ~PAUSEDIS; + } /* * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. Set the SCB - * pointer from the FIFO so we see the right bank of SCB - * registers. + * SCBs in the list of SCBs awaiting selection. If we have + * any SCBs available for use, pull the tag from the QINFIFO + * and get to work on it. */ - mov SCBPTR,QINFIFO + if ((ahc->flags & AHC_PAGESCBS) != 0) { + mov ALLZEROS call get_free_or_disc_scb; + } +dequeue_scb: + add A, -1, QINPOS; + mvi QINFIFO_OFFSET call fetch_byte; + + if ((ahc->flags & AHC_PAGESCBS) == 0) { + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, RETURN_2; + } +dma_queued_scb: /* - * 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 SELTO. Tagged commands - * don't set the active bits since you can queue more than one command - * at a time. We do, however, look to see if there are any non-tagged - * I/Os in progress, and requeue the command if there are. Tagged and - * non-tagged commands cannot be mixed to a single target. + * DMA the SCB from host ram into the current SCB location. */ - -test_busy: - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */ - - test ACTIVE_B,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_B,A - jmp start_scb - -/* Place the currently active SCB back on the queue for later processing */ -requeue: - mov QINFIFO, SCBPTR - jmp poll_for_work + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov RETURN_2 call dma_scb; /* - * Pull the first entry off of the waiting for selection list - * We don't have to "test_busy" because only transactions that - * have passed that test can be in the waiting_scb list. + * Preset the residual fields in case we never go through a data phase. + * This isn't done by the host so we can avoid a DMA to clear these + * fields for the normal case of I/O that completes without underrun + * or overrun conditions. */ -start_waiting: - mov SCBPTR,WAITING_SCBH - jmp start_scb2 - -test_a: - test ACTIVE_A,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_A,A + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov SCB_RESID_DCNT, SCB_DATACNT, 3; + } else { + mov SCB_RESID_DCNT[0],SCB_DATACNT[0]; + mov SCB_RESID_DCNT[1],SCB_DATACNT[1]; + mov SCB_RESID_DCNT[2],SCB_DATACNT[2]; + } + mov SCB_RESID_SGCNT, SCB_SGCOUNT; start_scb: - mov SCB_NEXT,WAITING_SCBH - mov WAITING_SCBH, SCBPTR -start_scb2: - and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */ - and A,0x08,SCB_TCL /* Get new channel bit */ - or SINDEX,A - mov SBLKCTL,SINDEX /* select channel */ - mov SCB_TCL call initialize_scsiid + /* + * Place us on the waiting list in case our selection + * doesn't win during bus arbitration. + */ + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; +start_waiting: + /* + * Pull the first entry off of the waiting SCB list. + */ + mov SCBPTR, WAITING_SCBH; + call start_selection; + jmp poll_for_work; -/* - * Enable selection phase as an initiator, and do automatic ATN - * after the selection. We do this now so that we can overlap the - * rest of our work to set up this target with the arbitration and - * selection bus phases. - */ start_selection: - mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */ - -/* - * 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. - * - * Messages are stored in scratch RAM starting with a length byte - * followed by the message itself. - */ - -mk_identify: - and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ - - and MSG0,0x7,SCB_TCL /* lun */ - or MSG0,A /* or in disconnect privledge */ - or MSG0,MSG_IDENTIFYFLAG - mvi MSG_LEN, 1 - -/* - * Send a tag message if TAG_ENB is set in the SCB control block. - * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. - */ -mk_tag: - test SCB_CONTROL,TAG_ENB jz mk_message - mvi DINDEX, MSG1 - and DINDIR,0x23,SCB_CONTROL - mov DINDIR,SCB_TAG - - add MSG_LEN,COMP_MSG0,DINDEX /* update message length */ - + if ((ahc->features & AHC_TWIN) != 0) { + and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ + and A,SELBUSB,SCB_TCL; /* Get new channel bit */ + or SINDEX,A; + mov SBLKCTL,SINDEX; /* select channel */ + } +initialize_scsiid: + mov SINDEX, SCSISEQ_TEMPLATE; + if ((ahc->flags & AHC_TARGETMODE) != 0) { + test SCB_CONTROL, TARGET_SCB jz . + 4; + if ((ahc->features & AHC_ULTRA2) != 0) { + mov SCSIID_ULTRA2, SCB_CMDPTR[2]; + } else { + mov SCSIID, SCB_CMDPTR[2]; + } + or SINDEX, TEMODE; + jmp initialize_scsiid_fini; + } + if ((ahc->features & AHC_ULTRA2) != 0) { + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID_ULTRA2, OID; /* Clear old target */ + or SCSIID_ULTRA2, A; + } else { + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID, OID; /* Clear old target */ + or SCSIID, A; + } +initialize_scsiid_fini: + mov SCSISEQ, SINDEX ret; + +/* + * Initialize transfer settings and clear the SCSI channel. + * SINDEX should contain any additional bit's the client wants + * set in SXFRCTL0. We also assume that the current SCB is + * a valid SCB for the target we wish to talk to. + */ +initialize_channel: + or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX; +set_transfer_settings: + if ((ahc->features & AHC_ULTRA) != 0) { + test SCB_CONTROL, ULTRAENB jz . + 2; + or SXFRCTL0, FAST20; + } /* - * Interrupt the driver, and allow it to tweak the message buffer - * if it asks. + * Initialize SCSIRATE with the appropriate value for this target. */ -mk_message: - test SCB_CONTROL,MK_MESSAGE jz wait_for_selection - - mvi INTSTAT,AWAITING_MSG - -wait_for_selection: - test SSTAT0,SELDO jnz select - test SSTAT0,SELDI jz wait_for_selection - + if ((ahc->features & AHC_ULTRA2) != 0) { + bmov SCSIRATE, SCB_SCSIRATE, 2 ret; + } else { + mov SCSIRATE, SCB_SCSIRATE ret; + } + +selection: + test SSTAT0,SELDO jnz select_out; + mvi CLRSINT0, CLRSELDI; +select_in: + if ((ahc->flags & AHC_TARGETMODE) != 0) { + if ((ahc->flags & AHC_INITIATORMODE) != 0) { + test SSTAT0, TARGET jz initiator_reselect; + } + + /* + * We've just been selected. Assert BSY and + * setup the phase for receiving messages + * from the target. + */ + mvi SCSISIGO, P_MESGOUT|BSYO; + mvi CLRSINT1, CLRBUSFREE; + + /* + * Setup the DMA for sending the identify and + * command information. + */ + or SEQ_FLAGS, CMDPHASE_PENDING; + + mov A, TQINPOS; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi TMODE_CMDADDR call set_32byte_addr; + mvi CCSCBCTL, CCSCBRESET; + } else { + mvi DINDEX, HADDR; + mvi TMODE_CMDADDR call set_32byte_addr; + mvi DFCNTRL, FIFORESET; + } + + /* Initiator that selected us */ + and SAVED_TCL, SELID_MASK, SELID; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov CCSCBRAM, SAVED_TCL; + } else { + mov DFDAT, SAVED_TCL; + } + + /* The Target ID we were selected at */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + if ((ahc->features & AHC_MULTI_TID) != 0) { + and CCSCBRAM, OID, TARGIDIN; + } else if ((ahc->features & AHC_ULTRA2) != 0) { + and CCSCBRAM, OID, SCSIID_ULTRA2; + } else { + and CCSCBRAM, OID, SCSIID; + } + } else { + if ((ahc->features & AHC_MULTI_TID) != 0) { + and DFDAT, OID, TARGIDIN; + } else if ((ahc->features & AHC_ULTRA2) != 0) { + and DFDAT, OID, SCSIID_ULTRA2; + } else { + and DFDAT, OID, SCSIID; + } + } + + /* No tag yet */ + mvi INITIATOR_TAG, SCB_LIST_NULL; + + /* + * If ATN isn't asserted, the target isn't interested + * in talking to us. Go directly to bus free. + */ + test SCSISIGI, ATNI jz target_busfree; + + /* + * Watch ATN closely now as we pull in messages from the + * initiator. We follow the guidlines from section 6.5 + * of the SCSI-2 spec for what messages are allowed when. + */ + call target_inb; + + /* + * Our first message must be one of IDENTIFY, ABORT, or + * BUS_DEVICE_RESET. + */ + /* XXX May need to be more lax here for older initiators... */ + test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop; + /* Store for host */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov CCSCBRAM, DINDEX; + } else { + mov DFDAT, DINDEX; + } + + /* Remember for disconnection decision */ + test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2; + /* XXX Honor per target settings too */ + or SEQ_FLAGS, NO_DISCONNECT; + + test SCSISIGI, ATNI jz ident_messages_done; + call target_inb; + /* + * If this is a tagged request, the tagged message must + * immediately follow the identify. We test for a valid + * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and + * < MSG_IGN_WIDE_RESIDUE. + */ + add A, -MSG_SIMPLE_Q_TAG, DINDEX; + jnc ident_messages_done; + add A, -MSG_IGN_WIDE_RESIDUE, DINDEX; + jc ident_messages_done; + /* Store for host */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov CCSCBRAM, DINDEX; + } else { + mov DFDAT, DINDEX; + } + + /* + * If the initiator doesn't feel like providing a tag number, + * we've got a failed selection and must transition to bus + * free. + */ + test SCSISIGI, ATNI jz target_busfree; + + /* + * Store the tag for the host. + */ + call target_inb; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov CCSCBRAM, DINDEX; + } else { + mov DFDAT, DINDEX; + } + mov INITIATOR_TAG, DINDEX; + jmp ident_messages_done; + + /* + * Pushed message loop to allow the kernel to + * run it's own target mode message state engine. + */ +host_target_message_loop: + mvi INTSTAT, HOST_MSG_LOOP; + nop; + cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop; + test SSTAT0, SPIORDY jz .; + jmp host_target_message_loop; + +ident_messages_done: + /* If ring buffer is full, return busy or queue full */ + mov A, KERNEL_TQINPOS; + cmp TQINPOS, A jne tqinfifo_has_space; + mvi P_STATUS|BSYO call change_phase; + cmp INITIATOR_TAG, SCB_LIST_NULL je . + 3; + mvi STATUS_QUEUE_FULL call target_outb; + jmp target_busfree_wait; + mvi STATUS_BUSY call target_outb; + jmp target_busfree_wait; +tqinfifo_has_space: + /* Terminate the ident list */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mvi CCSCBRAM, SCB_LIST_NULL; + } else { + mvi DFDAT, SCB_LIST_NULL; + } + or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN; + test SCSISIGI, ATNI jnz target_mesgout_pending_msg; + jmp target_ITloop; + +/* + * We carefully toggle SPIOEN to allow us to return the + * message byte we receive so it can be checked prior to + * driving REQ on the bus for the next byte. + */ +target_inb: + /* + * Drive REQ on the bus by enabling SCSI PIO. + */ + or SXFRCTL0, SPIOEN; + /* Wait for the byte */ + test SSTAT0, SPIORDY jz .; + /* Prevent our read from triggering another REQ */ + and SXFRCTL0, ~SPIOEN; + /* Save latched contents */ + mov DINDEX, SCSIDATL ret; + } + +if ((ahc->flags & AHC_INITIATORMODE) != 0) { /* * Reselection has been initiated by a target. Make a note that we've been - * reselected, but haven't seen an IDENTIFY message from the target - * yet. - */ -reselect: - clr MSG_LEN /* Don't have anything in the mesg buffer */ - mov SELID call initialize_scsiid - or FLAGS,RESELECTED - jmp select2 + * reselected, but haven't seen an IDENTIFY message from the target yet. + */ +initiator_reselect: + /* XXX test for and handle ONE BIT condition */ + and SAVED_TCL, SELID_MASK, SELID; + if ((ahc->features & AHC_TWIN) != 0) { + test SBLKCTL, SELBUSB jz . + 2; + or SAVED_TCL, SELBUSB; + } + or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN; + mvi CLRSINT1,CLRBUSFREE; + or SIMODE1, ENBUSFREE; /* + * We aren't expecting a + * bus free, so interrupt + * the kernel driver if it + * happens. + */ + jmp ITloop; +} /* - * After the selection, remove this SCB from the "waiting for selection" + * After the selection, remove this SCB from the "waiting SCB" * list. This is achieved by simply moving our "next" pointer into * WAITING_SCBH. Our next pointer will be set to null the next time this * SCB is used, so don't bother with it now. */ -select: - mov WAITING_SCBH,SCB_NEXT - or FLAGS,SELECTED -select2: -/* - * 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. - */ - or SXFRCTL0,CLRCHN -/* - * Initialize SCSIRATE with the appropriate value for this target. - */ - call ndx_dtr - mov SCSIRATE,SINDIR +select_out: + /* Turn off the selection hardware */ + and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP,SCSISEQ_TEMPLATE; + mvi CLRSINT0, CLRSELDO; + mov SCBPTR, WAITING_SCBH; + mov WAITING_SCBH,SCB_NEXT; + mov SAVED_TCL, SCB_TCL; + if ((ahc->flags & AHC_TARGETMODE) != 0) { + test SSTAT0, TARGET jz initiator_select; + + /* + * We've just re-selected an initiator. + * Assert BSY and setup the phase for + * sending our identify messages. + */ + mvi P_MESGIN|BSYO call change_phase; + mvi CLRSINT1,CLRBUSFREE; + + /* + * Start out with a simple identify message. + */ + and A, LID, SCB_TCL; + or A, MSG_IDENTIFYFLAG call target_outb; + + /* + * If we are the result of a tagged command, send + * a simple Q tag and the tag id. + */ + test SCB_CONTROL, TAG_ENB jz . + 3; + mvi MSG_SIMPLE_Q_TAG call target_outb; + mov SCB_INITIATOR_TAG call target_outb; + mov INITIATOR_TAG, SCB_INITIATOR_TAG; +target_synccmd: + /* + * Now determine what phases the host wants us + * to go through. + */ + mov SEQ_FLAGS, SCB_TARGET_PHASES; + + +target_ITloop: + /* + * Start honoring ATN signals now that + * we properly identified ourselves. + */ + test SCSISIGI, ATNI jnz target_mesgout; + test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase; + test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase; + test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase; + + /* + * No more work to do. Either disconnect or not depending + * on the state of NO_DISCONNECT. + */ + test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; + if ((ahc->flags & AHC_PAGESCBS) != 0) { + mov ALLZEROS call get_free_or_disc_scb; + } + mov RETURN_1, ALLZEROS; + call complete_target_cmd; + cmp RETURN_1, CONT_MSG_LOOP jne .; + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov SCB_TAG call dma_scb; + jmp target_synccmd; + +target_mesgout: + mvi SCSISIGO, P_MESGOUT|BSYO; + call target_inb; + /* Local Processing goes here... */ +target_mesgout_pending_msg: + jmp host_target_message_loop; + +target_disconnect: + mvi P_MESGIN|BSYO call change_phase; + test SEQ_FLAGS, DPHASE jz . + 2; + mvi MSG_SAVEDATAPOINTER call target_outb; + mvi MSG_DISCONNECT call target_outb; + +target_busfree_wait: + /* Wait for preceeding I/O session to complete. */ + test SCSISIGI, ACKI jnz .; +target_busfree: + clr SCSISIGO; + mvi LASTPHASE, P_BUSFREE; + call complete_target_cmd; + jmp poll_for_work; + +target_cmdphase: + mvi P_COMMAND|BSYO call change_phase; + call target_inb; + mov A, DINDEX; + /* Store for host */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov CCSCBRAM, A; + } else { + mov DFDAT, A; + } + + /* + * Determine the number of bytes to read + * based on the command group code via table lookup. + * We reuse the first 8 bytes of the TARG_SCSIRATE + * BIOS array for this table. Count is one less than + * the total for the command since we've already fetched + * the first byte. + */ + shr A, CMD_GROUP_CODE_SHIFT; + add SINDEX, TARG_SCSIRATE, A; + mov A, SINDIR; + + test A, 0xFF jz command_phase_done; +command_loop: + or SXFRCTL0, SPIOEN; + test SSTAT0, SPIORDY jz .; + cmp A, 1 jne . + 2; + and SXFRCTL0, ~SPIOEN; /* Last Byte */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov CCSCBRAM, SCSIDATL; + } else { + mov DFDAT, SCSIDATL; + } + dec A; + test A, 0xFF jnz command_loop; + +command_phase_done: + and SEQ_FLAGS, ~CMDPHASE_PENDING; + jmp target_ITloop; + +target_dphase: + /* + * Data direction flags are from the + * perspective of the initiator. + */ + test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4; + mvi LASTPHASE, P_DATAOUT; + mvi P_DATAIN|BSYO call change_phase; + jmp . + 3; + mvi LASTPHASE, P_DATAIN; + mvi P_DATAOUT|BSYO call change_phase; + mov ALLZEROS call initialize_channel; + jmp p_data; + +target_sphase: + mvi P_STATUS|BSYO call change_phase; + mvi LASTPHASE, P_STATUS; + mov SCB_TARGET_STATUS call target_outb; + /* XXX Watch for ATN or parity errors??? */ + mvi SCSISIGO, P_MESGIN|BSYO; + /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */ + mov ALLZEROS call target_outb; + jmp target_busfree_wait; + +complete_target_cmd: + test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2; + mov SCB_TAG jmp complete_post; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + /* Set the valid byte */ + mvi CCSCBADDR, 24; + mov CCSCBRAM, ALLONES; + mvi CCHCNT, 28; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + clr CCSCBCTL; + } else { + /* Set the valid byte */ + or DFCNTRL, FIFORESET; + mvi DFWADDR, 3; /* Third 64bit word or byte 24 */ + mov DFDAT, ALLONES; + mvi HCNT[0], 28; + clr HCNT[1]; + clr HCNT[2]; + or DFCNTRL, HDMAEN|FIFOFLUSH; + call dma_finish; + } + inc TQINPOS; + mvi INTSTAT,CMDCMPLT ret; + } + +if ((ahc->flags & AHC_INITIATORMODE) != 0) { +initiator_select: + mvi SPIOEN call initialize_channel; -/* - * Initialize Ultra mode setting. - */ - mov FUNCTION1,SCSIID - mov A,FUNCTION1 - and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ - test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ - test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ - test ULTRA_ENB,A jz set_sxfrctl0 - or SINDEX, ULTRAEN jmp set_sxfrctl0 -ultra_b: - test ULTRA_ENB_B,A jz set_sxfrctl0 - or SINDEX, ULTRAEN - -set_sxfrctl0: - mov SXFRCTL0,SINDEX - - mvi SCSISEQ,ENAUTOATNP /* - * ATN on parity errors - * for "in" phases - */ - mvi CLRSINT1,CLRBUSFREE - mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */ -/* - * 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 aren't expecting a bus free, so interrupt + * the kernel driver if it happens. + */ + mvi CLRSINT1,CLRBUSFREE; + or SIMODE1, ENBUSFREE; + + /* + * As soon as we get a successful selection, the target + * should go into the message out phase since we have ATN + * asserted. + */ + mvi MSG_OUT, MSG_IDENTIFYFLAG; + or SEQ_FLAGS, IDENTIFY_SEEN; + + /* + * Main loop for information transfer phases. Wait for the + * target to assert REQ before checking MSG, C/D and I/O for + * the bus phase. + */ ITloop: - test SSTAT1,BUSFREE jnz p_busfree - test SSTAT1,REQINIT jz ITloop - - and A,PHASE_MASK,SCSISIGI - mov LASTPHASE,A - mov SCSISIGO,A - - cmp ALLZEROS,A je p_dataout - cmp A,P_DATAIN je p_datain - cmp A,P_COMMAND je p_command - cmp A,P_MESGOUT je p_mesgout - cmp A,P_STATUS je p_status - cmp A,P_MESGIN je p_mesgin - - mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */ - jmp ITloop /* Try reading the bus again. */ - -p_dataout: - mvi DMAPARAMS,0x7d /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * DIRECTION|FIFORESET - */ - jmp data_phase_init + call phase_lock; + + mov A, LASTPHASE; + + test A, ~P_DATAIN jz p_data; + cmp A,P_COMMAND je p_command; + cmp A,P_MESGOUT je p_mesgout; + cmp A,P_STATUS je p_status; + cmp A,P_MESGIN je p_mesgin; + + mvi INTSTAT,BAD_PHASE; + jmp ITloop; /* Try reading the bus again. */ + +await_busfree: + and SIMODE1, ~ENBUSFREE; + mov NONE, SCSIDATL; /* Ack the last byte */ + and SXFRCTL0, ~SPIOEN; + test SSTAT1,REQINIT|BUSFREE jz .; + test SSTAT1, BUSFREE jnz poll_for_work; + mvi INTSTAT, BAD_PHASE; +} + +clear_target_state: + /* + * We assume that the kernel driver may reset us + * at any time, even in the middle of a DMA, so + * clear DFCNTRL too. + */ + clr DFCNTRL; + + /* + * We don't know the target we will connect to, + * so default to narrow transfers to avoid + * parity problems. + */ + if ((ahc->features & AHC_ULTRA2) != 0) { + bmov SCSIRATE, ALLZEROS, 2; + } else { + clr SCSIRATE; + and SXFRCTL0, ~(FAST20); + } + mvi LASTPHASE, P_BUSFREE; + /* clear target specific flags */ + clr SEQ_FLAGS ret; /* * If we re-enter the data phase after going through another phase, the * STCNT may have been cleared, so restore it from the residual field. */ data_phase_reinit: - mov STCNT0,SCB_RESID_DCNT0 - mov STCNT1,SCB_RESID_DCNT1 - mov STCNT2,SCB_RESID_DCNT2 - jmp data_phase_loop - -p_datain: - mvi DMAPARAMS,0x79 /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * !DIRECTION|FIFORESET + if ((ahc->features & AHC_ULTRA2) != 0) { + /* + * The preload circuitry requires us to + * reload the address too, so pull it from + * the shaddow address. + */ + bmov HADDR, SHADDR, 4; + bmov HCNT, SCB_RESID_DCNT, 3; + } else if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov STCNT, SCB_RESID_DCNT, 3; + } else { + mvi DINDEX, STCNT; + mvi SCB_RESID_DCNT call bcopy_3; + } + and DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0]; + jmp data_phase_loop; + +p_data: + if ((ahc->features & AHC_ULTRA2) != 0) { + mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; + } else { + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; + } + test LASTPHASE, IOI jnz . + 2; + or DMAPARAMS, DIRECTION; + call assert; /* + * Ensure entering a data + * phase is okay - seen identify, etc. */ -data_phase_init: - call assert + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mvi CCSGADDR, CCSGADDR_MAX; + } + test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + + /* We have seen a data phase */ + or SEQ_FLAGS, DPHASE; - test FLAGS, DPHASE jnz data_phase_reinit - call sg_scb2ram - or FLAGS, DPHASE /* We have seen a data phase */ + /* + * Initialize the DMA address and counter from the SCB. + * Also set SG_COUNT and SG_NEXT in memory since we cannot + * modify the values in the SCB itself until we see a + * save data pointers message. + */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov HADDR, SCB_DATAPTR, 7; + } else { + mvi DINDEX, HADDR; + mvi SCB_DATAPTR call bcopy_7; + } + and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0]; + + if ((ahc->features & AHC_ULTRA2) == 0) { + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov STCNT, HCNT, 3; + } else { + call set_stcnt_from_hcnt; + } + } + + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov SG_COUNT, SCB_SGCOUNT, 5; + } else { + mvi DINDEX, SG_COUNT; + mvi SCB_SGCOUNT call bcopy_5; + } data_phase_loop: /* Guard against overruns */ - test SG_COUNT, 0xff jnz data_phase_inbounds + test SG_COUNT, 0xff jnz data_phase_inbounds; /* * Turn on 'Bit Bucket' mode, set the transfer count to * 16meg and let the target run until it changes phase. * When the transfer completes, notify the host that we * had an overrun. */ - or SXFRCTL1,BITBUCKET - mvi STCNT0,0xff - mvi STCNT1,0xff - mvi STCNT2,0xff - + or SXFRCTL1,BITBUCKET; + and DMAPARAMS, ~(HDMAEN|SDMAEN); + if ((ahc->features & AHC_ULTRA2) != 0) { + bmov HCNT, ALLONES, 3; + } else if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov STCNT, ALLONES, 3; + } else { + mvi STCNT[0], 0xFF; + mvi STCNT[1], 0xFF; + mvi STCNT[2], 0xFF; + } data_phase_inbounds: -/* If we are the last SG block, don't set wideodd. */ - cmp SG_COUNT,0x01 jne data_phase_wideodd - and DMAPARAMS, 0xbf /* Turn off WIDEODD */ +/* If we are the last SG block, tell the hardware. */ + cmp SG_COUNT,0x01 jne data_phase_wideodd; + if ((ahc->features & AHC_ULTRA2) != 0) { + or SG_CACHEPTR, LAST_SEG; + } else { + if ((ahc->flags & AHC_TARGETMODE) != 0) { + test SSTAT0, TARGET jz . + 2; + test DMAPARAMS, DIRECTION jz data_phase_wideodd; + } + and DMAPARAMS, ~WIDEODD; + } data_phase_wideodd: - mov DMAPARAMS call dma - + if ((ahc->features & AHC_ULTRA2) != 0) { + mov SINDEX, ALLONES; + mov DFCNTRL, DMAPARAMS; + test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ +data_phase_dma_loop: + test SSTAT0, SDONE jnz data_phase_dma_done; + test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ + } else { + mov DMAPARAMS call dma; + } + +data_phase_dma_done: /* Go tell the host about any overruns */ - test SXFRCTL1,BITBUCKET jnz data_phase_overrun + test SXFRCTL1,BITBUCKET jnz data_phase_overrun; -/* Exit if we had an underrun */ - test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */ +/* See if we completed this segment */ + test STCNT[0], 0xff jnz data_phase_finish; + test STCNT[1], 0xff jnz data_phase_finish; + test STCNT[2], 0xff jnz data_phase_finish; /* * Advance the scatter-gather pointers if needed */ sg_advance: - dec SG_COUNT /* one less segment to go */ - - test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */ - - clr A /* add sizeof(struct scatter) */ - add SG_NEXT0,SG_SIZEOF,SG_NEXT0 - adc SG_NEXT1,A,SG_NEXT1 + dec SG_COUNT; /* one less segment to go */ + test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ /* * Load a struct scatter and set up the data address and length. * If the working value of the SG count is nonzero, then @@ -387,351 +812,475 @@ sg_advance: * This, like all DMA's, assumes little-endian host data storage. */ sg_load: - clr HCNT2 - clr HCNT1 - mvi HCNT0,SG_SIZEOF - - mov HADDR0,SG_NEXT0 - mov HADDR1,SG_NEXT1 - mov HADDR2,SG_NEXT2 - mov HADDR3,SG_NEXT3 - - or 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. - */ -dma_finish: - test DFSTATUS,HDONE jz dma_finish - /* Turn off DMA preserving WIDEODD */ - and DFCNTRL,WIDEODD -dma_finish2: - test DFCNTRL,HDMAENACK jnz dma_finish2 - -/* - * 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 for the Linux driver): - * - * struct scatterlist { - * char *address; four bytes, little-endian order - * ... four bytes, ignored - * unsigned short length; two bytes, little-endian order - * } - * - * - * In FreeBSD, the scatter list entry is only 8 bytes. - * - * struct ahc_dma_seg { - * physaddr addr; four bytes, little-endian order - * long len; four bytes, little endian order - * }; - */ - - mov HADDR0,DFDAT - mov HADDR1,DFDAT - mov HADDR2,DFDAT - mov HADDR3,DFDAT -/* - * For Linux, we must throw away four bytes since there is a 32bit gap - * in the middle of a struct scatterlist. - */ -#ifdef __linux__ - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT -#endif - mov HCNT0,DFDAT - mov HCNT1,DFDAT - mov HCNT2,DFDAT - -/* Load STCNT as well. It is a mirror of HCNT */ - mov STCNT0,HCNT0 - mov STCNT1,HCNT1 - mov STCNT2,HCNT2 - test SSTAT1,PHASEMIS jz data_phase_loop + if ((ahc->features & AHC_CMD_CHAN) != 0) { + /* + * Do we have any prefetch left??? + */ + cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail; + + /* + * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. + */ + add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT; + mvi A, CCSGADDR_MAX; + jc . + 2; + shl A, 3, SG_COUNT; + mov CCHCNT, A; + bmov CCHADDR, SG_NEXT, 4; + mvi CCSGCTL, CCSGEN|CCSGRESET; + test CCSGCTL, CCSGDONE jz .; + and CCSGCTL, ~CCSGEN; + test CCSGCTL, CCSGEN jnz .; + mvi CCSGCTL, CCSGRESET; +prefetched_segs_avail: + bmov HADDR, CCSGRAM, 8; + } else { + mvi DINDEX, HADDR; + mvi SG_NEXT call bcopy_4; + + mvi HCNT[0],SG_SIZEOF; + clr HCNT[1]; + clr HCNT[2]; + + or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + + call dma_finish; + + /* + * Copy data from FIFO into SCB data pointer and data count. + * This assumes that the SG segments are of the form: + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi HADDR call dfdat_in_7; + } + + /* Track odd'ness */ + test HCNT[0], 0x1 jz . + 2; + xor DATA_COUNT_ODD, 0x1; + + if ((ahc->features & AHC_ULTRA2) == 0) { + /* Load STCNT as well. It is a mirror of HCNT */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov STCNT, HCNT, 3; + } else { + call set_stcnt_from_hcnt; + } + } + +/* Advance the SG pointer */ + clr A; /* add sizeof(struct scatter) */ + add SG_NEXT[0],SG_SIZEOF; + adc SG_NEXT[1],A; + + if ((ahc->flags & AHC_TARGETMODE) != 0) { + test SSTAT0, TARGET jnz data_phase_loop; + } + test SSTAT1, REQINIT jz .; + test SSTAT1,PHASEMIS jz data_phase_loop; + + /* Ensure the last seg is visable at the shaddow layer */ + if ((ahc->features & AHC_ULTRA2) != 0) { + mov DFCNTRL, DMAPARAMS; + test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ + } data_phase_finish: + if ((ahc->features & AHC_ULTRA2) != 0) { + call ultra2_dmafinish; + } /* * After a DMA finishes, save the SG and STCNT residuals back into the SCB * We use STCNT instead of HCNT, since it's a reflection of how many bytes * were transferred on the SCSI (as opposed to the host) bus. */ - mov SCB_RESID_DCNT0,STCNT0 - mov SCB_RESID_DCNT1,STCNT1 - mov SCB_RESID_DCNT2,STCNT2 - mov SCB_RESID_SGCNT, SG_COUNT - jmp ITloop + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov SCB_RESID_DCNT, STCNT, 3; + } else { + mov SCB_RESID_DCNT[0],STCNT[0]; + mov SCB_RESID_DCNT[1],STCNT[1]; + mov SCB_RESID_DCNT[2],STCNT[2]; + } + mov SCB_RESID_SGCNT, SG_COUNT; + + if ((ahc->features & AHC_ULTRA2) != 0) { + or SXFRCTL0, CLRSTCNT|CLRCHN; + } + + if ((ahc->flags & AHC_TARGETMODE) != 0) { + test SEQ_FLAGS, DPHASE_PENDING jz ITloop; + and SEQ_FLAGS, ~DPHASE_PENDING; + /* + * For data-in phases, wait for any pending acks from the + * initiator before changing phase. + */ + test DFCNTRL, DIRECTION jz target_ITloop; + test SSTAT1, REQINIT jnz .; + jmp target_ITloop; + } + jmp ITloop; data_phase_overrun: + if ((ahc->features & AHC_ULTRA2) != 0) { + call ultra2_dmafinish; + or SXFRCTL0, CLRSTCNT|CLRCHN; + } /* * Turn off BITBUCKET mode and notify the host */ - and SXFRCTL1,0x7f /* ~BITBUCKET */ - mvi INTSTAT,DATA_OVERRUN - jmp ITloop - + and SXFRCTL1, ~BITBUCKET; + mvi INTSTAT,DATA_OVERRUN; + jmp ITloop; + +ultra2_dmafinish: + if ((ahc->features & AHC_ULTRA2) != 0) { + test DFCNTRL, DIRECTION jnz ultra2_dmafifoempty; + and DFCNTRL, ~SCSIEN; + test DFCNTRL, SCSIEN jnz .; +ultra2_dmafifoflush: + or DFCNTRL, FIFOFLUSH; + /* + * The FIFOEMP status bit on the Ultra2 class + * of controllers seems to be a bit flaky. + * It appears that if the FIFO is full and the + * transfer ends with some data in the REQ/ACK + * FIFO, FIFOEMP will fall temporarily + * as the data is transferred to the PCI bus. + * This glitch lasts for fewer than 5 clock cycles, + * so we work around the problem by ensuring the + * status bit stays false through a full glitch + * window. + */ + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + +ultra2_dmafifoempty: + /* Don't clobber an inprogress host data transfer */ + test DFSTATUS, MREQPEND jnz ultra2_dmafifoempty; + +ultra2_dmahalt: + and DFCNTRL, ~(SCSIEN|HDMAEN); + test DFCNTRL, HDMAEN jnz .; + ret; + } + +if ((ahc->flags & AHC_INITIATORMODE) != 0) { /* * Command phase. Set up the DMA registers and let 'er rip. */ p_command: - call assert - -/* - * Load HADDR and HCNT. - */ - mov HADDR0, SCB_CMDPTR0 - mov HADDR1, SCB_CMDPTR1 - mov HADDR2, SCB_CMDPTR2 - mov HADDR3, SCB_CMDPTR3 - mov HCNT0, SCB_CMDLEN - clr HCNT1 - clr HCNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - jmp ITloop + call assert; + + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mov HCNT[0], SCB_CMDLEN; + bmov HCNT[1], ALLZEROS, 2; + if ((ahc->features & AHC_ULTRA2) == 0) { + bmov STCNT, HCNT, 3; + } + add NONE, -17, SCB_CMDLEN; + jc dma_cmd_data; + /* + * The data fifo seems to require 4 byte alligned + * transfers from the sequencer. Force this to + * be the case by clearing HADDR[0] even though + * we aren't going to touch host memeory. + */ + bmov HADDR[0], ALLZEROS, 1; + if ((ahc->features & AHC_ULTRA2) != 0) { + mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION); + } else { + mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET); + } + bmov DFDAT, SCB_CMDSTORE, 16; + jmp cmd_loop; +dma_cmd_data: + bmov HADDR, SCB_CMDPTR, 4; + } else { + mvi DINDEX, HADDR; + mvi SCB_CMDPTR call bcopy_5; + clr HCNT[1]; + clr HCNT[2]; + } + + if ((ahc->features & AHC_ULTRA2) == 0) { + if ((ahc->features & AHC_CMD_CHAN) == 0) { + call set_stcnt_from_hcnt; + } + mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET); + } else { + mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION); + } +cmd_loop: + test SSTAT0, SDONE jnz . + 2; + test SSTAT1, PHASEMIS jz cmd_loop; + /* + * Wait for our ACK to go-away on it's own + * instead of being killed by SCSIEN getting cleared. + */ + test SCSISIGI, ACKI jnz .; + and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); + test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .; + jmp ITloop; /* * Status phase. Wait for the data byte to appear, then read it * and store it into the SCB. */ p_status: - mvi SCB_TARGET_STATUS call inb_first - jmp mesgin_done + call assert; + + mov SCB_TARGET_STATUS, SCSIDATL; + jmp ITloop; /* - * Message out phase. If there is not an active message, but the target - * took us into this phase anyway, build a no-op message and send it. + * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full + * indentify message sequence and send it to the target. The host may + * override this behavior by setting the MK_MESSAGE bit in the SCB + * control byte. This will cause us to interrupt the host and allow + * it to handle the message phase completely on its own. If the bit + * associated with this target is set, we will also interrupt the host, + * thereby allowing it to send a message on the next selection regardless + * of the transaction being sent. + * + * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message. + * This is done to allow the host to send messages outside of an identify + * sequence while protecting the seqencer from testing the MK_MESSAGE bit + * on an SCB that might not be for the current nexus. (For example, a + * BDR message in responce to a bad reselection would leave us pointed to + * an SCB that doesn't have anything to do with the current target). + * + * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag, + * bus device reset). + * + * When there are no messages to send, MSG_OUT should be set to MSG_NOOP, + * in case the target decides to put us in this phase for some strange + * reason. */ +p_mesgout_retry: + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ p_mesgout: - test MSG_LEN, 0xff jnz p_mesgout_start - mvi MSG_NOOP call mk_mesg /* build NOP message */ - -p_mesgout_start: + mov SINDEX, MSG_OUT; + cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; + test SCB_CONTROL,MK_MESSAGE jnz host_message_loop; + mov FUNCTION1, SCB_TCL; + mov A, FUNCTION1; + if ((ahc->features & AHC_HS_MAILBOX) != 0) { + /* + * Work around a pausing bug in at least the aic7890. + * If the host needs to update the TARGET_MSG_REQUEST + * bit field, it will set the HS_MAILBOX to 1. In + * response, we pause with a specific interrupt code + * asking for the mask to be updated before we continue. + * Ugh. + */ + test HS_MAILBOX, 0xF0 jz . + 2; + mvi INTSTAT, UPDATE_TMSG_REQ; + nop; + } + mov SINDEX, TARGET_MSG_REQUEST[0]; + if ((ahc->features & AHC_TWIN) != 0) { + /* Second Channel uses high byte bits */ + test SCB_TCL, SELBUSB jz . + 2; + mov SINDEX, TARGET_MSG_REQUEST[1]; + } else if ((ahc->features & AHC_WIDE) != 0) { + test SCB_TCL, 0x80 jz . + 2; /* target > 7 */ + mov SINDEX, TARGET_MSG_REQUEST[1]; + } + test SINDEX, A jnz host_message_loop; +p_mesgout_identify: + and SINDEX,LID,SCB_TCL; /* lun */ + and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ + or SINDEX,A; /* or in disconnect privledge */ + or SINDEX,MSG_IDENTIFYFLAG; /* - * Set up automatic PIO transfer from MSG0. Bit 3 in - * SXFRCTL0 (SPIOEN) is already on. + * Send a tag message if TAG_ENB is set in the SCB control block. + * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. */ - mvi SINDEX,MSG0 - mov DINDEX,MSG_LEN - +p_mesgout_tag: + test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte; + mov SCSIDATL, SINDEX; /* Send the identify message */ + call phase_lock; + cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; + and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; + call phase_lock; + cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; + mov SCB_TAG jmp p_mesgout_onebyte; /* - * 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. - * - * Keep an eye out for a phase change, in case the target issues - * a MESSAGE REJECT. + * Interrupt the driver, and allow it to handle this message + * phase and any required retries. */ -p_mesgout_loop: - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - test SSTAT0,SPIORDY jz p_mesgout_loop - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - cmp DINDEX,1 jne p_mesgout_outb /* last byte? */ - mvi CLRSINT1,CLRATNO /* drop ATN */ -p_mesgout_outb: - dec DINDEX - or CLRSINT0, CLRSPIORDY - mov SCSIDATL,SINDIR - -p_mesgout4: - test DINDEX,0xff jnz p_mesgout_loop +p_mesgout_from_host: + cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; + jmp host_message_loop; + +p_mesgout_onebyte: + mvi CLRSINT1, CLRATNO; + mov SCSIDATL, SINDEX; /* - * If the next bus phase after ATN drops is a message out, it means + * If the next bus phase after ATN drops is message out, it means * that the target is requesting that the last message(s) be resent. */ -p_mesgout_snoop: - test SSTAT1,BUSFREE jnz p_mesgout_done - test SSTAT1,REQINIT jz p_mesgout_snoop - - test SSTAT1,PHASEMIS jnz p_mesgout_done - - or SCSISIGO,ATNO /* turn on ATNO */ + call phase_lock; + cmp LASTPHASE, P_MESGOUT je p_mesgout_retry; - jmp ITloop - -p_mesgout_phasemis: - mvi CLRSINT1,CLRATNO /* Be sure to turn ATNO off */ p_mesgout_done: - clr MSG_LEN /* no active msg */ - jmp ITloop + mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ + mov LAST_MSG, MSG_OUT; + mvi MSG_OUT, MSG_NOOP; /* No message left */ + jmp ITloop; /* * Message in phase. Bytes are read using Automatic PIO mode. */ p_mesgin: - mvi A call inb_first /* read the 1st message byte */ - mov REJBYTE,A /* save it for the driver */ - - test A,MSG_IDENTIFYFLAG jnz mesgin_identify - cmp A,MSG_DISCONNECT je mesgin_disconnect - cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs - cmp ALLZEROS,A je mesgin_complete - cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs - cmp A,MSG_EXTENDED je mesgin_extended - cmp A,MSG_MESSAGE_REJECT je mesgin_reject - -rej_mesgin: -/* - * 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. - */ - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,SEND_REJECT /* let driver know */ - - mvi MSG_MESSAGE_REJECT call mk_mesg + mvi ACCUM call inb_first; /* read the 1st message byte */ + + test A,MSG_IDENTIFYFLAG jnz mesgin_identify; + cmp A,MSG_DISCONNECT je mesgin_disconnect; + cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; + cmp ALLZEROS,A je mesgin_complete; + cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; + cmp A,MSG_NOOP je mesgin_done; + +/* + * Pushed message loop to allow the kernel to + * run it's own message state engine. To avoid an + * extra nop instruction after signaling the kernel, + * we perform the phase_lock before checking to see + * if we should exit the loop and skip the phase_lock + * in the ITloop. Performing back to back phase_locks + * shouldn't hurt, but why do it twice... + */ +host_message_loop: + mvi INTSTAT, HOST_MSG_LOOP; + call phase_lock; + cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1; + jmp host_message_loop; mesgin_done: - call inb_last /*ack & turn auto PIO back on*/ - jmp ITloop + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ + jmp ITloop; mesgin_complete: /* - * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT, - * 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, modify the "waiting for selection" SCB list - * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the - * sequencer imediately jumps to main loop where it will run down the waiting - * SCB list and process the sense request. If the kernel driver does not - * wish to request sense, it need only clear RETURN_1, 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. - * - * First check for residuals + * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, + * and trigger a completion interrupt. Before doing so, check to see if there + * is a residual or the status byte is something other than STATUS_GOOD (0). + * In either of these conditions, we upload the SCB back to the host so it can + * process this information. In the case of a non zero status byte, we + * additionally interrupt the kernel driver synchronously, allowing it to + * decide if sense should be retrieved. If the kernel driver wishes to request + * sense, it will fill the kernel SCB with a request sense command and set + * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload + * the SCB, and process it as the next command by adding it to the waiting list. + * If the kernel driver does not wish to request sense, it need only clear + * RETURN_1, and the command is allowed to complete normally. We don't bother + * to post to the QOUTFIFO in the error cases 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 SCB_RESID_SGCNT,0xff jz check_status -/* - * If we have a residual count, interrupt and tell the host. Other - * alternatives are to pause the sequencer on all command completes (yuck), - * dma the resid directly to the host (slick, we may have space to do it now) - * or have the sequencer pause itself when it encounters a non-zero resid - * (unecessary pause just to flag the command -yuck-, but takes one instruction - * and since it shouldn't happen that often is good enough for our purposes). - */ -resid: - mvi INTSTAT,RESIDUAL -check_status: - test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */ - mvi INTSTAT,BAD_STATUS /* let driver know */ - cmp RETURN_1, SEND_SENSE jne status_ok - jmp mesgin_done - -status_ok: -/* First, mark this target as free. */ - test SCB_CONTROL,TAG_ENB jnz test_immediate /* - * Tagged commands - * don't busy the - * target. - */ - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz clear_a - xor ACTIVE_B,A - jmp test_immediate - -clear_a: - xor ACTIVE_A,A - -test_immediate: - test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */ /* - * Pause the sequencer until the driver gets around to handling the command - * complete. This is so that any action that might require carefull timing - * with the completion of this command can occur. - */ - mvi INTSTAT,IMMEDDONE - jmp start -complete: - mov QOUTFIFO,SCB_TAG - mvi INTSTAT,CMDCMPLT - jmp mesgin_done - - -/* - * Is it an extended message? Copy the message to our message buffer and - * notify the host. The host will tell us whether to reject this message, - * respond to it with the message that the host placed in our message buffer, - * or simply to do nothing. - */ -mesgin_extended: - mvi MSGIN_EXT_LEN call inb_next - mvi MSGIN_EXT_OPCODE call inb_next - mov A, MSGIN_EXT_LEN - dec A /* Length counts the op code */ - mvi SINDEX, MSGIN_EXT_BYTE0 -mesgin_extended_loop: - test A, 0xFF jz mesgin_extended_intr - cmp SINDEX, MSGIN_EXT_LASTBYTE je mesgin_extended_dump - call inb_next - dec A -/* - * We pass the arg to inb in SINDEX, but DINDEX is the one incremented - * so update SINDEX with DINDEX's value before looping again. + * First check for residuals */ - mov DINDEX jmp mesgin_extended_loop -mesgin_extended_dump: -/* We have no more storage space, so dump the rest */ - test A, 0xFF jz mesgin_extended_intr - mvi NONE call inb_next - dec A - jmp mesgin_extended_dump -mesgin_extended_intr: - mvi INTSTAT,EXTENDED_MSG /* let driver know */ - cmp RETURN_1,SEND_REJ je rej_mesgin - cmp RETURN_1,SEND_MSG jne mesgin_done -/* The kernel has setup a message to be sent */ - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done + test SCB_RESID_SGCNT,0xff jnz upload_scb; + test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */ +upload_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +check_status: + test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */ + mvi INTSTAT,BAD_STATUS; /* let driver know */ + nop; + cmp RETURN_1, SEND_SENSE jne complete; + /* This SCB becomes the next to execute as it will retrieve sense */ + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov SCB_TAG call dma_scb; +add_to_waiting_list: + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; + /* + * Prepare our selection hardware before the busfree so we have a + * high probability of winning arbitration. + */ + call start_selection; + jmp await_busfree; +complete: + /* If we are untagged, clear our address up in host ram */ + test SCB_CONTROL, TAG_ENB jnz complete_queue; + mov A, SAVED_TCL; + /* fvdl - let ahc_intr clear this to avoid race conditions */ + /* mvi UNTAGGEDSCB_OFFSET call post_byte_setup; */ + /* mvi SCB_LIST_NULL call post_byte; */ + +complete_queue: + mov SCB_TAG call complete_post; + jmp await_busfree; +} + +complete_post: + /* Post the SCBID in SINDEX and issue an interrupt */ + call add_scb_to_free_list; + mov ARG_1, SINDEX; + if ((ahc->features & AHC_QUEUE_REGS) != 0) { + mov A, SDSCB_QOFF; + } else { + mov A, QOUTPOS; + } + mvi QOUTFIFO_OFFSET call post_byte_setup; + mov ARG_1 call post_byte; + if ((ahc->features & AHC_QUEUE_REGS) == 0) { + inc QOUTPOS; + } + mvi INTSTAT,CMDCMPLT ret; + +if ((ahc->flags & AHC_INITIATORMODE) != 0) { /* * Is it a disconnect message? Set a flag in the SCB to remind us * and await the bus going free. */ mesgin_disconnect: - or SCB_CONTROL,DISCONNECTED - test FLAGS, PAGESCBS jz mesgin_done -/* - * Link this SCB into the DISCONNECTED list. This list holds the - * candidates for paging out an SCB if one is needed for a new command. - * Modifying the disconnected list is a critical(pause dissabled) section. - */ - mvi SCB_PREV, SCB_LIST_NULL - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - mov SCB_NEXT, DISCONNECTED_SCBH - mov DISCONNECTED_SCBH, SCBPTR - cmp SCB_NEXT,SCB_LIST_NULL je linkdone - mov SCBPTR,SCB_NEXT - mov SCB_PREV,DISCONNECTED_SCBH - mov SCBPTR,DISCONNECTED_SCBH -linkdone: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - jmp mesgin_done + or SCB_CONTROL,DISCONNECTED; + call add_scb_to_disc_list; + jmp await_busfree; /* - * Save data pointers message? Copy working values into the SCB, - * usually in preparation for a disconnect. + * Save data pointers message: + * Copying RAM values back to SCB, for Save Data Pointers message, but + * only if we've actually been into a data phase to change them. This + * protects against bogus data in scratch ram and the residual counts + * since they are only initialized when we go into data_in or data_out. */ mesgin_sdptrs: - call sg_ram2scb - jmp mesgin_done + test SEQ_FLAGS, DPHASE jz mesgin_done; + + /* + * The SCB SGPTR becomes the next one we'll download, + * and the SCB DATAPTR becomes the current SHADDR. + * Use the residual number since STCNT is corrupted by + * any message transfer. + */ + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov SCB_SGCOUNT, SG_COUNT, 5; + bmov SCB_DATAPTR, SHADDR, 4; + bmov SCB_DATACNT, SCB_RESID_DCNT, 3; + } else { + mvi DINDEX, SCB_SGCOUNT; + mvi SG_COUNT call bcopy_5; + + mvi DINDEX, SCB_DATAPTR; + mvi SHADDR call bcopy_4; + mvi SCB_RESID_DCNT call bcopy_3; + } + jmp mesgin_done; /* * Restore pointers message? Data pointers are recopied from the @@ -740,11 +1289,12 @@ mesgin_sdptrs: * code do the rest. */ mesgin_rdptrs: - and FLAGS,0xef /* - * !DPHASE we'll reload them + and SEQ_FLAGS, ~DPHASE; /* + * We'll reload them * the next time through + * the dataphase. */ - jmp mesgin_done + jmp mesgin_done; /* * Identify message? For a reconnecting target, this tells us the lun @@ -752,118 +1302,83 @@ mesgin_rdptrs: * clearing the "disconnected" bit so we don't "find" it by accident later. */ mesgin_identify: - test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/ - - and A,0x07 /* lun in lower three bits */ - or SAVED_TCL,A,SELID - and SAVED_TCL,0xf7 - and A,SELBUSB,SBLKCTL /* B Channel?? */ - or SAVED_TCL,A - call inb_last /* ACK */ - + if ((ahc->features & AHC_WIDE) != 0) { + and A,0x0f; /* lun in lower four bits */ + } else { + and A,0x07; /* lun in lower three bits */ + } + or SAVED_TCL,A; /* SAVED_TCL should be complete now */ + + mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */ + call get_untagged_SCBID; + cmp ARG_1, SCB_LIST_NULL je snoop_tag; + if ((ahc->flags & AHC_PAGESCBS) != 0) { + test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; + } + /* + * If the SCB was found in the disconnected list (as is + * always the case in non-paging scenarios), SCBPTR is already + * set to the correct SCB. So, simply setup the SCB and get + * on with things. + */ + call rem_scb_from_disc_list; + jmp setup_SCB; /* * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. - * If we get one, we use the tag returned to switch to find the proper - * SCB. With SCB paging, this requires using findSCB for both tagged + * If we get one, we use the tag returned to find the proper + * SCB. With SCB paging, this requires using search for both tagged * and non-tagged transactions since the SCB may exist in any slot. * If we're not using SCB paging, we can use the tag as the direct * index to the SCB. */ - mvi ARG_1,SCB_LIST_NULL /* Default to no-tag */ +snoop_tag: + mov NONE,SCSIDATL; /* ACK Identify MSG */ snoop_tag_loop: - test SSTAT1,BUSFREE jnz use_findSCB - test SSTAT1,REQINIT jz snoop_tag_loop - test SSTAT1,PHASEMIS jnz use_findSCB - mvi A call inb_first - cmp A,MSG_SIMPLE_Q_TAG jne use_findSCB + call phase_lock; + cmp LASTPHASE, P_MESGIN jne not_found; + cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; get_tag: - mvi ARG_1 call inb_next /* tag value */ -/* - * See if the tag is in range. The tag is < SCBCOUNT if we add - * the complement of SCBCOUNT to the incomming tag and there is - * no carry. - */ - mov A,COMP_SCBCOUNT - add SINDEX,A,ARG_1 - jc abort_tag + mvi ARG_1 call inb_next; /* tag value */ -/* - * Ensure that the SCB the tag points to is for an SCB transaction - * to the reconnecting target. - */ - test FLAGS, PAGESCBS jz index_by_tag - call inb_last /* Ack Tag */ -use_findSCB: - mov ALLZEROS call findSCB /* Have to search */ + /* + * Ensure that the SCB the tag points to is for + * an SCB transaction to the reconnecting target. + */ +use_retrieveSCB: + call retrieveSCB; setup_SCB: - and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */ - or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */ - jmp ITloop -index_by_tag: - mov SCBPTR,ARG_1 - mov A,SAVED_TCL - cmp SCB_TCL,A jne abort_tag - test SCB_CONTROL,TAG_ENB jz abort_tag - call inb_last /* Ack Successful tag */ - jmp setup_SCB - -abort_tag: - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,ABORT_TAG /* let driver know */ - mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */ - jmp mesgin_done - -/* - * 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. - */ -mesgin_reject: - mvi INTSTAT, REJECT_MSG - jmp mesgin_done + mov A, SAVED_TCL; + cmp SCB_TCL, A jne not_found_cleanup_scb; + test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb; + and SCB_CONTROL,~DISCONNECTED; + or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ + call set_transfer_settings; + /* See if the host wants to send a message upon reconnection */ + test SCB_CONTROL, MK_MESSAGE jz mesgin_done; + and SCB_CONTROL, ~MK_MESSAGE; + mvi HOST_MSG call mk_mesg; + jmp mesgin_done; + +not_found_cleanup_scb: + test SCB_CONTROL, DISCONNECTED jz . + 3; + call add_scb_to_disc_list; + jmp not_found; + call add_scb_to_free_list; +not_found: + mvi INTSTAT, NO_MATCH; + jmp mesgin_done; /* * [ ADD MORE MESSAGE HANDLING HERE ] */ -/* - * 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,CLRATNO - clr LASTPHASE - -/* - * if this is an immediate command, perform a psuedo command complete to - * notify the driver. - */ - test SCB_CMDLEN,0xff jz status_ok - jmp start - /* * 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 MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */ - - /* - * Hmmm. For some reason the mesg buffer is in use. - * Tell the driver. It should look at SINDEX to find - * out what we wanted to use the buffer for and resolve - * the conflict. - */ - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - mvi INTSTAT,MSG_BUFFER_BUSY - -mk_mesg1: - mvi MSG_LEN,1 /* length = 1 */ - mov MSG0,SINDEX /* 1-byte message */ - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ + mov MSG_OUT,SINDEX ret; /* * Functions to read data in Automatic PIO mode. @@ -880,26 +1395,75 @@ mk_mesg1: * and that REQ is already set when inb_first is called. inb_{first,next} * use the same calling convention as inb. */ - +inb_next_wait_perr: + mvi INTSTAT, PERR_DETECTED; + jmp inb_next_wait; inb_next: - or CLRSINT0, CLRSPIORDY - mov NONE,SCSIDATL /*dummy read from latch to ACK*/ + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ inb_next_wait: - test SSTAT1,PHASEMIS jnz mesgin_phasemis - test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */ + /* + * If there is a parity error, wait for the kernel to + * see the interrupt and prepare our message response + * before continuing. + */ + test SSTAT1, REQINIT jz inb_next_wait; + test SSTAT1, SCSIPERR jnz inb_next_wait_perr; +inb_next_check_phase: + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; inb_first: - mov DINDEX,SINDEX - test SSTAT1,PHASEMIS jnz mesgin_phasemis - mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/ + mov DINDEX,SINDEX; + mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ inb_last: - mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/ + mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ +} +if ((ahc->flags & AHC_TARGETMODE) != 0) { +/* + * Change to a new phase. If we are changing the state of the I/O signal, + * from out to in, wait an additional data release delay before continuing. + */ +change_phase: + /* Wait for preceeding I/O session to complete. */ + test SCSISIGI, ACKI jnz .; + + /* Change the phase */ + and DINDEX, IOI, SCSISIGI; + mov SCSISIGO, SINDEX; + and A, IOI, SINDEX; + + /* + * If the data direction has changed, from + * out (initiator driving) to in (target driving), + * we must waitat least a data release delay plus + * the normal bus settle delay. [SCSI III SPI 10.11.0] + */ + cmp DINDEX, A je change_phase_wait; + test SINDEX, IOI jz change_phase_wait; + call change_phase_wait; +change_phase_wait: + nop; + nop; + nop; + nop ret; + +/* + * Send a byte to an initiator in Automatic PIO mode. + */ +target_outb: + or SXFRCTL0, SPIOEN; + test SSTAT0, SPIORDY jz .; + mov SCSIDATL, SINDEX; + test SSTAT0, SPIORDY jz .; + and SXFRCTL0, ~SPIOEN ret; +} + mesgin_phasemis: /* * We expected to receive another byte, but the target changed phase */ - mvi INTSTAT, MSGIN_PHASEMIS - jmp ITloop + mvi INTSTAT, MSGIN_PHASEMIS; + jmp ITloop; /* * DMA data transfer. HADDR and HCNT must be loaded first, and @@ -908,10 +1472,11 @@ mesgin_phasemis: * during initialization. */ dma: - mov DFCNTRL,SINDEX -dma1: - test SSTAT0,DMADONE jnz dma3 - test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */ + mov DFCNTRL,SINDEX; +dma_loop: + test SSTAT0,DMADONE jnz dma_dmadone; + test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ +dma_phasemis: /* * We will be "done" DMAing when the transfer count goes to zero, or @@ -921,157 +1486,378 @@ dma1: * magically on STCNT=0 or a phase change, so just wait for FIFO empty * status. */ -dma3: - test SINDEX,DIRECTION jnz dma5 -dma4: - test DFSTATUS,FIFOEMP jz dma4 +dma_checkfifo: + test DFCNTRL,DIRECTION jnz dma_fifoempty; +dma_fifoflush: + test DFSTATUS,FIFOEMP jz dma_fifoflush; +dma_fifoempty: + /* Don't clobber an inprogress host data transfer */ + test DFSTATUS, MREQPEND jnz dma_fifoempty; /* * Now shut the DMA enables off and make sure that the DMA enables are * actually off first lest we get an ILLSADDR. */ -dma5: - /* disable DMA, but maintain WIDEODD */ - and DFCNTRL,WIDEODD -dma6: - test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ +dma_dmadone: + and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); +dma_halt: + /* + * Some revisions of the aic7880 have a problem where, if the + * data fifo is full, but the PCI input latch is not empty, + * HDMAEN cannot be cleared. The fix used here is to attempt + * to drain the data fifo until there is space for the input + * latch to drain and HDMAEN de-asserts. + */ + if ((ahc->features & AHC_ULTRA2) == 0) { + mov NONE, DFDAT; + } + test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; return: - 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_scsiid: - and SINDEX,0xf0 /* Get target ID */ - and A,0x0f,SCSIID - or SINDEX,A - mov SCSIID,SINDEX ret + ret; /* * Assert that if we've been reselected, then we've seen an IDENTIFY * message. */ assert: - test FLAGS,RESELECTED jz return /* reselected? */ - test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */ + test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ - mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */ + mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ /* - * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag - * value in ARG_1. If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged - * SCB. Have the kernel print a warning message if it can't be found, and - * generate an ABORT/ABORT_TAG message to the target. SINDEX should be - * cleared on call. + * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) + * or by the SCBID ARG_1. The search begins at the SCB index passed in + * via SINDEX which is an SCB that must be on the disconnected list. If + * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR + * is set to the proper SCB. */ findSCB: - mov A,SAVED_TCL - mov SCBPTR,SINDEX /* switch to next SCB */ - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */ - test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/ - test SCB_CONTROL,TAG_ENB jnz findTaggedSCB - cmp ARG_1,SCB_LIST_NULL je foundSCB - jmp findSCB1 -findTaggedSCB: - mov A, ARG_1 /* Tag passed in ARG_1 */ - cmp SCB_TAG,A jne findSCB1 /* Found it? */ -foundSCB: - test FLAGS,PAGESCBS jz foundSCB_ret + mov SCBPTR,SINDEX; /* Initialize SCBPTR */ + cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID; + mov A, SAVED_TCL; + mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */ +findSCB_by_SCBID: + mov A, ARG_1; /* Tag passed in ARG_1 */ + mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */ +findSCB_next: + mov ARG_2, SCBPTR; + cmp SCB_NEXT, SCB_LIST_NULL je notFound; + mov SCBPTR,SCB_NEXT; + dec SINDEX; /* Last comparison moved us too far */ +findSCB_loop: + cmp SINDIR, A jne findSCB_next; + mov SINDEX, SCBPTR ret; +notFound: + mvi SINDEX, SCB_LIST_NULL ret; + +/* + * Retrieve an SCB by SCBID first searching the disconnected list falling + * back to DMA'ing the SCB down from the host. This routine assumes that + * ARG_1 is the SCBID of interrest and that SINDEX is the position in the + * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL, + * we go directly to the host for the SCB. + */ +retrieveSCB: + test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host; + mov SCBPTR call findSCB; /* Continue the search */ + cmp SINDEX, SCB_LIST_NULL je retrieve_from_host; + +/* + * This routine expects SINDEX to contain the index of the SCB to be + * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the + * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL + * if it is at the head. + */ +rem_scb_from_disc_list: /* Remove this SCB from the disconnection list */ - cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev - mov SAVED_LINKPTR, SCB_PREV - mov SCBPTR, SCB_NEXT - mov SCB_PREV, SAVED_LINKPTR - mov SCBPTR, SINDEX -unlink_prev: - cmp SCB_PREV,SCB_LIST_NULL je rHead/* At the head of the list */ - mov SAVED_LINKPTR, SCB_NEXT - mov SCBPTR, SCB_PREV - mov SCB_NEXT, SAVED_LINKPTR - mov SCBPTR, SINDEX - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ + cmp ARG_2, SCB_LIST_NULL je rHead; + mov DINDEX, SCB_NEXT; + mov SCBPTR, ARG_2; + mov SCB_NEXT, DINDEX; + mov SCBPTR, SINDEX ret; rHead: - mov DISCONNECTED_SCBH,SCB_NEXT -foundSCB_ret: - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -findSCB1: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - inc SINDEX - mov A,SCBCOUNT - cmp SINDEX,A jne findSCB - - mvi INTSTAT,NO_MATCH /* not found - signal kernel */ - cmp RETURN_1,SCB_PAGEDIN je return - or SCSISIGO,ATNO /* assert ATNO */ - cmp ARG_1,SCB_LIST_NULL jne find_abort_tag - mvi MSG_ABORT call mk_mesg - jmp ITloop -find_abort_tag: - mvi MSG_ABORT_TAG call mk_mesg - jmp ITloop - -/* - * Make a working copy of the scatter-gather parameters from the SCB. - */ -sg_scb2ram: - mov HADDR0, SCB_DATAPTR0 - mov HADDR1, SCB_DATAPTR1 - mov HADDR2, SCB_DATAPTR2 - mov HADDR3, SCB_DATAPTR3 - mov HCNT0, SCB_DATACNT0 - mov HCNT1, SCB_DATACNT1 - mov HCNT2, SCB_DATACNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mov SG_COUNT,SCB_SGCOUNT - - mov SG_NEXT0, SCB_SGPTR0 - mov SG_NEXT1, SCB_SGPTR1 - mov SG_NEXT2, SCB_SGPTR2 - mov SG_NEXT3, SCB_SGPTR3 ret + mov DISCONNECTED_SCBH,SCB_NEXT ret; + +retrieve_from_host: +/* + * We didn't find it. Pull an SCB and DMA down the one we want. + * We should never get here in the non-paging case. + */ + mov ALLZEROS call get_free_or_disc_scb; + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + /* Jump instead of call as we want to return anyway */ + mov ARG_1 jmp dma_scb; + +/* + * Determine whether a target is using tagged or non-tagged transactions + * by first looking for a matching transaction based on the TCL and if + * that fails, looking up this device in the host's untagged SCB array. + * The TCL to search for is assumed to be in SAVED_TCL. The value is + * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged). + * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information + * in an SCB instead of having to go to the host. + */ +get_untagged_SCBID: + cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host; + mvi ARG_1, SCB_LIST_NULL; + mov DISCONNECTED_SCBH call findSCB; + cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host; + or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */ + test SCB_CONTROL, TAG_ENB jnz . + 2; + mov ARG_1, SCB_TAG ret; + mvi ARG_1, SCB_LIST_NULL ret; + +/* + * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) + * and a base address of SCBID_ADDR. The byte is returned in RETURN_2. + */ +fetch_byte: + mov ARG_2, SINDEX; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi CCHCNT, 1; + mvi CCSGCTL, CCSGEN|CCSGRESET; + test CCSGCTL, CCSGDONE jz .; + mvi CCSGCTL, CCSGRESET; + bmov RETURN_2, CCSGRAM, 1 ret; + } else { + mvi DINDEX, HADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi HCNT[0], 1; + clr HCNT[1]; + clr HCNT[2]; + mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + call dma_finish; + mov RETURN_2, DFDAT ret; + } + +/* + * Prepare the hardware to post a byte to host memory given an + * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR. + */ +post_byte_setup: + mov ARG_2, SINDEX; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi CCHCNT, 1; + mvi CCSCBCTL, CCSCBRESET ret; + } else { + mvi DINDEX, HADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi HCNT[0], 1; + clr HCNT[1]; + clr HCNT[2]; + mvi DFCNTRL, FIFORESET ret; + } + +post_byte: + if ((ahc->features & AHC_CMD_CHAN) != 0) { + bmov CCSCBRAM, SINDEX, 1; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + clr CCSCBCTL ret; + } else { + mov DFDAT, SINDEX; + or DFCNTRL, HDMAEN|FIFOFLUSH; + jmp dma_finish; + } + +get_SCBID_from_host: + mov A, SAVED_TCL; + mvi UNTAGGEDSCB_OFFSET call fetch_byte; + mov RETURN_1, RETURN_2 ret; + +phase_lock_perr: + mvi INTSTAT, PERR_DETECTED; +phase_lock: + /* + * If there is a parity error, wait for the kernel to + * see the interrupt and prepare our message response + * before continuing. + */ + test SSTAT1, REQINIT jz phase_lock; + test SSTAT1, SCSIPERR jnz phase_lock_perr; +phase_lock_latch_phase: + and SCSISIGO, PHASE_MASK, SCSISIGI; + and LASTPHASE, PHASE_MASK, SCSISIGI ret; + +if ((ahc->features & AHC_CMD_CHAN) == 0) { +set_stcnt_from_hcnt: + mov STCNT[0], HCNT[0]; + mov STCNT[1], HCNT[1]; + mov STCNT[2], HCNT[2] ret; + +bcopy_7: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; +bcopy_5: + mov DINDIR, SINDIR; +bcopy_4: + mov DINDIR, SINDIR; +bcopy_3: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; + mov DINDIR, SINDIR ret; +} + +if ((ahc->flags & AHC_TARGETMODE) != 0) { +/* + * Setup addr assuming that A is an index into + * an array of 32byte objects, SINDEX contains + * the base address of that array, and DINDEX + * contains the base address of the location + * to store the indexed address. + */ +set_32byte_addr: + shr ARG_2, 3, A; + shl A, 5; + jmp set_1byte_addr; +} + +/* + * Setup addr assuming that A is an index into + * an array of 64byte objects, SINDEX contains + * the base address of that array, and DINDEX + * contains the base address of the location + * to store the indexed address. + */ +set_64byte_addr: + shr ARG_2, 2, A; + shl A, 6; + +/* + * Setup addr assuming that A + (ARG_1 * 256) is an + * index into an array of 1byte objects, SINDEX contains + * the base address of that array, and DINDEX contains + * the base address of the location to store the computed + * address. + */ +set_1byte_addr: + add DINDIR, A, SINDIR; + mov A, ARG_2; + adc DINDIR, A, SINDIR; + clr A; + adc DINDIR, A, SINDIR; + adc DINDIR, A, SINDIR ret; + +/* + * Either post or fetch and SCB from host memory based on the + * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX. + */ +dma_scb: + mov A, SINDEX; + if ((ahc->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi HSCB_ADDR call set_64byte_addr; + mov CCSCBPTR, SCBPTR; + test DMAPARAMS, DIRECTION jz dma_scb_tohost; + mvi CCHCNT, SCB_64BYTE_SIZE; + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; + jmp dma_scb_finish; +dma_scb_tohost: + mvi CCHCNT, SCB_32BYTE_SIZE; + if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { + mvi CCSCBCTL, CCSCBRESET; + bmov CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + } else { + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; + } +dma_scb_finish: + clr CCSCBCTL; + test CCSCBCTL, CCARREN|CCSCBEN jnz .; + ret; + } else { + mvi DINDEX, HADDR; + mvi HSCB_ADDR call set_64byte_addr; + mvi HCNT[0], SCB_32BYTE_SIZE; + clr HCNT[1]; + clr HCNT[2]; + mov DFCNTRL, DMAPARAMS; + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; + /* Fill it with the SCB data */ +copy_scb_tofifo: + mvi SINDEX, SCB_CONTROL; + add A, SCB_32BYTE_SIZE, SINDEX; +copy_scb_tofifo_loop: + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + cmp SINDEX, A jne copy_scb_tofifo_loop; + or DFCNTRL, HDMAEN|FIFOFLUSH; +dma_scb_fromhost: + call dma_finish; + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return; + mvi SCB_CONTROL call dfdat_in_7; + call dfdat_in_7_continued; + call dfdat_in_7_continued; + jmp dfdat_in_7_continued; +dfdat_in_7: + mov DINDEX,SINDEX; +dfdat_in_7_continued: + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT ret; + } -/* - * Copying RAM values back to SCB, for Save Data Pointers message, but - * only if we've actually been into a data phase to change them. This - * protects against bogus data in scratch ram and the residual counts - * since they are only initialized when we go into data_in or data_out. - */ -sg_ram2scb: - test FLAGS, DPHASE jz return - mov SCB_SGCOUNT,SG_COUNT - - mov SCB_SGPTR0,SG_NEXT0 - mov SCB_SGPTR1,SG_NEXT1 - mov SCB_SGPTR2,SG_NEXT2 - mov SCB_SGPTR3,SG_NEXT3 - - mov SCB_DATAPTR0,SHADDR0 - mov SCB_DATAPTR1,SHADDR1 - mov SCB_DATAPTR2,SHADDR2 - mov SCB_DATAPTR3,SHADDR3 /* - * Use the residual number since STCNT is corrupted by any message transfer + * Wait for DMA from host memory to data FIFO to complete, then disable + * DMA and wait for it to acknowledge that it's off. */ - mov SCB_DATACNT0,SCB_RESID_DCNT0 - mov SCB_DATACNT1,SCB_RESID_DCNT1 - mov SCB_DATACNT2,SCB_RESID_DCNT2 ret - +dma_finish: + test DFSTATUS,HDONE jz dma_finish; + /* Turn off DMA */ + and DFCNTRL, ~HDMAEN; + test DFCNTRL, HDMAEN jnz .; + ret; + +add_scb_to_free_list: + if ((ahc->flags & AHC_PAGESCBS) != 0) { + mov SCB_NEXT, FREE_SCBH; + mvi SCB_TAG, SCB_LIST_NULL; + mov FREE_SCBH, SCBPTR ret; + } else { + mvi SCB_TAG, SCB_LIST_NULL ret; + } + +if ((ahc->flags & AHC_PAGESCBS) != 0) { +get_free_or_disc_scb: + cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; + cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; +return_error: + mvi SINDEX, SCB_LIST_NULL ret; +dequeue_disc_scb: + mov SCBPTR, DISCONNECTED_SCBH; +dma_up_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +unlink_disc_scb: + mov DISCONNECTED_SCBH, SCB_NEXT ret; +dequeue_free_scb: + mov SCBPTR, FREE_SCBH; + mov FREE_SCBH, SCB_NEXT ret; +} + +add_scb_to_disc_list: /* - * Add the array base TARG_SCRATCH 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. + * Link this SCB into the DISCONNECTED list. This list holds the + * candidates for paging out an SCB if one is needed for a new command. + * Modifying the disconnected list is a critical(pause dissabled) section. */ -ndx_dtr: - shr A,SCSIID,4 - test SBLKCTL,SELBUSB jz ndx_dtr_2 - or A,0x08 /* Channel B entries add 8 */ -ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A ret + mov SCB_NEXT, DISCONNECTED_SCBH; + mov DISCONNECTED_SCBH, SCBPTR ret; diff --git a/sys/dev/microcode/aic7xxx/aic7xxx_asm.1 b/sys/dev/microcode/aic7xxx/aic7xxx_asm.1 deleted file mode 100644 index 6ba7a14b327..00000000000 --- a/sys/dev/microcode/aic7xxx/aic7xxx_asm.1 +++ /dev/null @@ -1,71 +0,0 @@ -.\" $OpenBSD: aic7xxx_asm.1,v 1.6 1999/07/09 13:35:34 aaron Exp $ -.\" -.\" Copyright (c) 1994, 1995 -.\" Justin T. Gibbs. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. The name of the author may not be used to endorse or promote products -.\" derived from this software without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" -.Dd November 11, 1994 -.Dt AIC7XXX_ASM 1 -.Os -.Sh NAME -.Nm aic7xxx_asm -.Nd Assembler for the Adaptec aic7xxx family of asics -.Sh SYNOPSIS -.Nm aic7xxx_asm -.Op Fl d -.Op Fl D Ar variable=value -.Op Fl v -.Op Fl o Ar file -.Ar source-file -.Sh DESCRIPTION -The Adaptec aic7xxx family of asics are single chip SCSI controllers with a -RISC like command processor. This assembler parses the language outlined -in the Adaptec technical document -.%T "AIC-7770 (EISA/ISA Bus Master Single-Chip SCSI Host Adaptor) Data Book" -and produces ascii output intended for a C byte array. -.Pp -The aic7xxx assembler is required to compile kernels with aic7xxx SCSI -adaptor support (AHA-274x, AHA-284x, AHA-294x controllers) and is compiled, -installed, and used automatically in the kernel compile directory when -necessary. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl D Ar variable=value -Define -.Ar variable -to be -.Ar value -in the global context -.It Fl d -Turn on debugging -.It Fl v -Print version information -.It Fl o Ar file -Redirect assembler output to -.Ar file -.Sh AUTHOR -This aic7770 assembler was written by -John Aycock (aycock@cpsc.ucalgary.ca) diff --git a/sys/dev/microcode/aic7xxx/aic7xxx_asm.c b/sys/dev/microcode/aic7xxx/aic7xxx_asm.c deleted file mode 100644 index 39df041368a..00000000000 --- a/sys/dev/microcode/aic7xxx/aic7xxx_asm.c +++ /dev/null @@ -1,687 +0,0 @@ -/* $OpenBSD: aic7xxx_asm.c,v 1.7 1997/01/15 23:42:08 millert Exp $ */ - -/*+M************************************************************************* - * Adaptec AIC7770/AIC7870 sequencer code assembler. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of Calgary - * Department of Computer Science and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Comments are started by `#' and continue to the end of the line; lines - * may be of the form: - *