diff options
author | gibbs <gibbs@FreeBSD.org> | 1996-10-25 06:42:53 +0000 |
---|---|---|
committer | gibbs <gibbs@FreeBSD.org> | 1996-10-25 06:42:53 +0000 |
commit | b6d2b0f5fdb75b2ed252bc2c7c78fd53e058bed7 (patch) | |
tree | 7af51a4cef901166db7d0af93f7a5f8f663ba3ea /sys | |
parent | 4aa7bc8985d47c52d58949c770c9ce9667909135 (diff) | |
download | FreeBSD-src-b6d2b0f5fdb75b2ed252bc2c7c78fd53e058bed7.zip FreeBSD-src-b6d2b0f5fdb75b2ed252bc2c7c78fd53e058bed7.tar.gz |
- KNF cleanup.
- Add support for memory mapped I/O.
- Use DMA to get SCBs down to the adapters.
- Remove old paging code.
- Be much smarter about how we allocate SCB space. The old, simple method
wasted almost half a page per SCB. Ooops.
- Make command complete interrupt processing more efficient.
- Break the monolithic ahc_intr into sub-routines. The sub-routines handle
rare, special case events so the function call is not a penalty and the
removal of the code from the main routine most likely improves performance
instruction prefech will work better and less code is pushed into the cache.
- Never, ever allow tagged queueing if a device has disconnection disabled.
- Clean up and simplify timeout code. Many of the changes are to handle the
new DMA scheme.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/i386/scsi/93cx6.c | 7 | ||||
-rw-r--r-- | sys/i386/scsi/93cx6.h | 18 | ||||
-rw-r--r-- | sys/i386/scsi/aic7xxx.c | 2361 | ||||
-rw-r--r-- | sys/i386/scsi/aic7xxx.h | 171 |
4 files changed, 1100 insertions, 1457 deletions
diff --git a/sys/i386/scsi/93cx6.c b/sys/i386/scsi/93cx6.c index 17eef78..c3bf2c4 100644 --- a/sys/i386/scsi/93cx6.c +++ b/sys/i386/scsi/93cx6.c @@ -18,7 +18,7 @@ * 4. Modifications may be freely made to this file if the above conditions * are met. * - * $Id: 93cx6.c,v 1.4 1995/11/20 12:14:02 phk Exp $ + * $Id: 93cx6.c,v 1.5 1996/05/30 07:19:54 gibbs Exp $ */ /* @@ -90,13 +90,14 @@ read_seeprom(sd, buf, start_addr, count) u_int16_t *buf; #if defined(__FreeBSD__) u_int start_addr; - int count; + u_int count; #elif defined(__NetBSD__) bus_io_size_t start_addr; bus_io_size_t count; #endif { - int i = 0, k = 0; + int i = 0; + u_int k = 0; u_int16_t v; u_int8_t temp; diff --git a/sys/i386/scsi/93cx6.h b/sys/i386/scsi/93cx6.h index 94c81f8..6122dca 100644 --- a/sys/i386/scsi/93cx6.h +++ b/sys/i386/scsi/93cx6.h @@ -20,7 +20,7 @@ * 4. Modifications may be freely made to this file if the above conditions * are met. * - * $Id: 93cx6.h,v 1.2 1995/09/05 23:52:00 gibbs Exp $ + * $Id: 93cx6.h,v 1.1.2.3 1996/06/08 07:10:44 gibbs Exp $ */ #include <sys/param.h> @@ -30,7 +30,8 @@ struct seeprom_descriptor { #if defined(__FreeBSD__) - u_long sd_iobase; + u_int32_t sd_iobase; + volatile u_int8_t *sd_maddr; #elif defined(__NetBSD__) bus_chipset_tag_t sd_bc; bus_io_handle_t sd_ioh; @@ -61,8 +62,15 @@ struct seeprom_descriptor { */ #if defined(__FreeBSD__) -#define SEEPROM_INB(sd) inb(sd->sd_iobase) -#define SEEPROM_OUTB(sd, value) outb(sd->sd_iobase, value) +#define SEEPROM_INB(sd) \ + (((sd)->sd_maddr != NULL) ? \ + *((sd)->sd_maddr) : \ + inb((sd)->sd_iobase)) +#define SEEPROM_OUTB(sd, value) \ + (((sd)->sd_maddr != NULL) ? \ + *((sd)->sd_maddr) = (value) : \ + outb((sd)->sd_iobase, (value))) + #elif defined(__NetBSD__) #define SEEPROM_INB(sd) \ bus_io_read_1(sd->sd_bc, sd->sd_ioh, sd->sd_offset) @@ -72,7 +80,7 @@ struct seeprom_descriptor { #if defined(__FreeBSD__) int read_seeprom __P((struct seeprom_descriptor *sd, - u_int16_t *buf, u_int start_addr, int count)); + u_int16_t *buf, u_int start_addr, u_int count)); #elif defined(__NetBSD__) int read_seeprom __P((struct seeprom_descriptor *sd, u_int16_t *buf, bus_io_size_t start_addr, bus_io_size_t count)); diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c index b434dc4..677d146 100644 --- a/sys/i386/scsi/aic7xxx.c +++ b/sys/i386/scsi/aic7xxx.c @@ -2,7 +2,8 @@ * Generic driver for the aic7xxx based adaptec SCSI controllers * Product specific probe and attach routines can be found in: * i386/eisa/aic7770.c 27/284X and aic7770 motherboard controllers - * pci/aic7870.c 3940, 2940, aic7880, aic7870 and aic7850 controllers + * pci/aic7870.c 3940, 2940, aic7880, aic7870, aic7860, + * and aic7850 controllers * * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs. * All rights reserved. @@ -31,86 +32,60 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.c,v 1.78 1996/10/06 19:42:59 gibbs Exp $ + * $Id: aic7xxx.c,v 1.75.2.5 1996/10/06 22:48:12 gibbs Exp $ */ /* * TODO: * Implement Target Mode * - * A few notes on how SCB paging works... + * A few notes on features of the driver. * * SCB paging takes advantage of the fact that devices stay disconnected * from the bus a relatively long time and that while they're disconnected, - * having the SCBs for that device down on the host adapter is of little use. - * Instead we copy the SCB back up into kernel memory and reuse the SCB slot - * on the card to schedule another transaction. This can be a real payoff - * when doing random I/O to tagged queueing devices since there are more - * transactions active at once for the device to sort for optimal seek - * reduction. The algorithm goes like this... + * having the SCBs for these transactions down on the host adapter is of + * little use. Instead of leaving this idle SCB down on the card we copy + * it back up into kernel memory and reuse the SCB slot on the card to + * schedule another transaction. This can be a real payoff when doing random + * I/O to tagged queueing devices since there are more transactions active at + * once for the device to sort for optimal seek reduction. The algorithm goes + * like this... * - * At the sequencer level: - * 1) Disconnected SCBs are threaded onto a doubly linked list, headed by - * DISCONNECTED_SCBH using the SCB_NEXT and SCB_PREV fields. The most - * recently disconnected device is always at the head. + * The sequencer maintains two lists of its hardware SCBs. The first is the + * singly linked free list which tracks all SCBs that are not currently in + * use. The second is the doubly linked disconnected list which holds the + * SCBs of transactions that are in the disconnected state sorted most + * recently disconnected first. When the kernel queues a transaction to + * the card, a hardware SCB to "house" this transaction is retrieved from + * either of these two lists. If the SCB came from the disconnected list, + * a check is made to see if any data transfer or SCB linking (more on linking + * in a bit) information has been changed since it was copied from the host + * and if so, DMAs the SCB back up before it can be used. Once a hardware + * SCB has been obtained, the SCB is DMAed from the host. Before any work + * can begin on this SCB, the sequencer must ensure that either the SCB is + * for a tagged transaction or the target is not already working on another + * non-tagged transaction. If a conflict arises in the non-tagged case, the + * sequencer finds the SCB for the active transactions and sets the SCB_LINKED + * field in that SCB to this next SCB to execute. To facilitate finding + * active non-tagged SCBs, the last four bytes of up to the first four hardware + * SCBs serve as a storage area for the currently active SCB ID for each + * target. * - * 2) The SCB has an added field SCB_TAG that corresponds to the kernel - * SCB number (ie 0-254). + * When a device reconnects, a search is made of the hardware SCBs to find + * the SCB for this transaction. If the search fails, a hardware SCB is + * pulled from either the free or disconnected SCB list and the proper + * SCB is DMAed from the host. If the SCB_ABORTED control bit is set + * in the control byte of the SCB while it was disconnected, the sequencer + * will send an abort or abort tag message to the target during the + * reconnection and signal the kernel that the abort was successfull. * - * 3) When a command is queued, the hardware index of the SCB it was downloaded - * into is placed into the QINFIFO for easy indexing by the sequencer. + * When a command completes, a check for non-zero status and residuals is + * made. If either of these conditions exists, the SCB is DMAed back up to + * the host so that it can interpret this information. Additionally, in the + * case of bad status, the sequencer generates a special interrupt and pauses + * itself. This allows the host to setup a request sense command if it + * chooses for this target synchronously with the error so that sense + * information isn't lost. * - * 4) The tag field is used as the tag for tagged-queueing, for determining - * the related kernel SCB, and is the value put into the QOUTFIFO - * so the kernel doesn't have to upload the SCB to determine the kernel SCB - * that completed on command completes. - * - * 5) When a reconnect occurs, the sequencer must scan the SCB array (even - * in the tag case) looking for the appropriate SCB and if it can't find - * it, it interrupts the kernel so it can page the SCB in. - * - * 6) If the sequencer is successful in finding the SCB, it removes it from - * the doubly linked list of disconnected SCBS. - * - * At the kernel level: - * 1) There are four queues that a kernel SCB may reside on: - * free_scbs - SCBs that are not in use and have a hardware slot assigned - * to them. - * page_scbs - SCBs that are not in use and need to have a hardware slot - * assigned to them (i.e. they will most likely cause a page - * out event). - * waiting_scbs - SCBs that are active, don't have an assigned hardware - * slot assigned to them and are waiting for either a - * disconnection or a command complete to free up a slot. - * assigned_scbs - SCBs that were in the waiting_scbs queue, but were - * assigned a slot by ahc_free_scb. - * - * 2) When a new request comes in, an SCB is allocated from the free_scbs or - * page_scbs queue with preference to SCBs on the free_scbs queue. - * - * 3) If there are no free slots (we retrieved the SCB off of the page_scbs - * queue), the SCB is inserted onto the tail of the waiting_scbs list and - * we attempt to run this queue down. - * - * 4) ahc_run_waiing_queues() looks at both the assigned_scbs and waiting_scbs - * queues. In the case of the assigned_scbs, the commands are immediately - * downloaded and started. For waiting_scbs, we page in all that we can - * ensuring we don't create a resource deadlock (see comments in - * ahc_run_waing_queues()). - * - * 5) After we handle a bunch of command completes, we also try running the - * queues since many SCBs may have disconnected since the last command - * was started and we have at least one free slot on the card. - * - * 6) ahc_free_scb looks at the waiting_scbs queue for a transaction - * requiring a slot and moves it to the assigned_scbs queue if it - * finds one. Otherwise it puts the current SCB onto the free_scbs - * queue for later use. - * - * 7) The driver handles page-in requests from the sequencer in response to - * the NO_MATCH sequencer interrupt. For tagged commands, the approprite - * SCB is easily found since the tag is a direct index into our kernel SCB - * array. For non-tagged commands, we keep a separate array of 16 pointers - * that point to the single possible SCB that was paged out for that target. */ #include <sys/param.h> @@ -153,17 +128,14 @@ #define bootverbose 1 -#define DEBUGTARG DEBUGTARGET -#if DEBUGTARG < 0 /* Negative numbrs for disabling cause warnings */ -#undef DEBUGTARG -#define DEBUGTARG 17 +#if DEBUGTARGET < 0 /* Negative numbrs for disabling cause warnings */ +#define DEBUGTARGET 17 #endif #endif /* defined(__NetBSD__) */ #include <sys/kernel.h> -#define KVTOPHYS(x) vtophys(x) -#define MIN(a,b) ((a < b) ? a : b) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define ALL_TARGETS -1 #if defined(__FreeBSD__) @@ -189,8 +161,14 @@ int ahc_broken_cache = 1; #define HSCSIID 0x07 /* our SCSI ID */ #define HWSCSIID 0x0f /* our SCSI ID if Wide Bus */ -static void ahcminphys __P((struct buf *bp)); -static int32_t ahc_scsi_cmd __P((struct scsi_xfer *xs)); +static void ahcminphys __P((struct buf *bp)); +static int32_t ahc_scsi_cmd __P((struct scsi_xfer *xs)); +static void ahc_run_waiting_queue __P((struct ahc_data *ahc)); +static struct scb * + ahc_get_scb __P((struct ahc_data *ahc, u_int32_t flags)); +static void ahc_free_scb __P((struct ahc_data *ahc, struct scb *scb)); +static struct scb * + ahc_alloc_scb __P((struct ahc_data *ahc)); static inline void pause_sequencer __P((struct ahc_data *ahc)); static inline void unpause_sequencer __P((struct ahc_data *ahc, int unpause_always)); @@ -198,28 +176,27 @@ static inline void restart_sequencer __P((struct ahc_data *ahc)); static struct scsi_adapter ahc_switch = { - ahc_scsi_cmd, - ahcminphys, - 0, - 0, + ahc_scsi_cmd, + ahcminphys, + NULL, + NULL, #if defined(__FreeBSD__) - 0, - "ahc", - { 0, 0 } + NULL, + "ahc", + { 0, 0 } #endif }; -/* the below structure is so we have a default dev struct for our link struct */ static struct scsi_device ahc_dev = { - NULL, /* Use default error handler */ - NULL, /* have a queue, served by this */ - NULL, /* have no async handler */ - NULL, /* Use default 'done' routine */ + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ #if defined(__FreeBSD__) - "ahc", - 0, - { 0, 0 } + "ahc", + 0, + { 0, 0 } #endif }; @@ -243,7 +220,7 @@ unpause_sequencer(ahc, unpause_always) int unpause_always; { if (unpause_always - ||(AHC_INB(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0) + || (AHC_INB(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0) AHC_OUTB(ahc, HCNTRL, ahc->unpause); } @@ -256,53 +233,41 @@ restart_sequencer(ahc) { do { AHC_OUTB(ahc, SEQCTL, SEQRESET|FASTMODE); - } while((AHC_INB(ahc, SEQADDR0) != 0) - || (AHC_INB(ahc, SEQADDR1) != 0)); + } while ((AHC_INB(ahc, SEQADDR0) != 0) + || (AHC_INB(ahc, SEQADDR1) != 0)); unpause_sequencer(ahc, /*unpause_always*/TRUE); } -#if defined(__NetBSD__) -/* - * Is device which is pointed by sc_link connected on second scsi bus ? - */ +#if defined(__FreeBSD__) +#define IS_SCSIBUS_B(ahc, sc_link) \ + (((u_int32_t)(sc_link)->fordriver) & SELBUSB) +#else /* NetBSD/OpenBSD */ #define IS_SCSIBUS_B(ahc, sc_link) \ ((sc_link)->scsibus == (ahc)->sc_link_b.scsibus) - -/* - * convert FreeBSD's SCSI symbols to NetBSD's - */ -#define SCSI_NOMASK SCSI_POLL -#define opennings openings #endif -static u_char ahc_abort_wscb __P((struct ahc_data *ahc, struct scb *scbp, - u_char prev, - u_char timedout_scb, u_int32_t xs_error)); -static void ahc_add_waiting_scb __P((struct ahc_data *ahc, - struct scb *scb)); +static u_int8_t ahc_abort_wscb __P((struct ahc_data *ahc, struct scb *scbp, + u_int8_t scbpos, u_int8_t prev, + struct scb *timedout_scb, + u_int32_t xs_error)); static void ahc_done __P((struct ahc_data *ahc, struct scb *scbp)); -static void ahc_free_scb __P((struct ahc_data *ahc, struct scb *scb, - int flags)); -static inline void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb)); -static inline void ahc_fetch_scb __P((struct ahc_data *ahc, struct scb *scb)); -static inline void ahc_page_scb __P((struct ahc_data *ahc, struct scb *out_scb, - struct scb *in_scb)); -static inline void ahc_run_waiting_queues __P((struct ahc_data *ahc)); -static void ahc_handle_seqint __P((struct ahc_data *ahc, u_int8_t intstat)); -static struct scb * - ahc_get_scb __P((struct ahc_data *ahc, int flags)); +static void ahc_handle_seqint __P((struct ahc_data *ahc, u_int8_t intstat)); +static void ahc_handle_scsiint __P((struct ahc_data *ahc, + u_int8_t intstat)); static void ahc_loadseq __P((struct ahc_data *ahc)); static int ahc_match_scb __P((struct scb *scb, int target, char channel)); static int ahc_poll __P((struct ahc_data *ahc, int wait)); #ifdef AHC_DEBUG static void ahc_print_scb __P((struct scb *scb)); #endif +static u_int8_t find_scb __P((struct ahc_data *ahc, struct scb *scb)); static int ahc_reset_channel __P((struct ahc_data *ahc, char channel, - u_char timedout_scb, u_int32_t xs_error, - u_char initiate_reset)); + struct scb *timedout_scb, + u_int32_t xs_error, + int initiate_reset)); static int ahc_reset_device __P((struct ahc_data *ahc, int target, - char channel, u_char timedout_scb, + char channel, struct scb *timedout_scb, u_int32_t xs_error)); static void ahc_reset_current_bus __P((struct ahc_data *ahc)); static void ahc_run_done_queue __P((struct ahc_data *ahc)); @@ -315,15 +280,18 @@ static timeout_t #elif defined(__NetBSD__) static void ahc_timeout __P((void *)); #endif -static void ahc_busy_target __P((struct ahc_data *ahc, - int target, char channel)); + static void ahc_unbusy_target __P((struct ahc_data *ahc, int target, char channel)); + static void ahc_construct_sdtr __P((struct ahc_data *ahc, int start_byte, - u_int8_t period, u_int8_t offset)); + u_int8_t period, u_int8_t offset)); + static void ahc_construct_wdtr __P((struct ahc_data *ahc, int start_byte, u_int8_t bus_width)); +static void ahc_calc_residual __P((struct scb *scb)); + #if defined(__FreeBSD__) char *ahc_name(ahc) @@ -346,26 +314,26 @@ static void ahc_print_scb(scb) struct scb *scb; { - printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n" - ,scb - ,scb->control - ,scb->tcl - ,scb->cmdlen - ,scb->cmdpointer ); - printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n" - ,scb->datalen - ,scb->data - ,scb->SG_segment_count - ,scb->SG_list_pointer); - printf(" sg_addr:%lx sg_len:%ld\n" - ,scb->ahc_dma[0].addr - ,scb->ahc_dma[0].len); + printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n", + scb, + scb->control, + scb->tcl, + scb->cmdlen, + scb->cmdpointer ); + printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n", + scb->datalen, + scb->data, + scb->SG_segment_count, + scb->SG_list_pointer); + printf(" sg_addr:%lx sg_len:%ld\n", + scb->ahc_dma[0].addr, + scb->ahc_dma[0].len); } #endif static struct { - u_char errno; + u_int8_t errno; char *errmesg; } hard_error[] = { { ILLHADDR, "Illegal Host Access" }, @@ -381,10 +349,10 @@ static struct { * stick in the scsiscfr reg to use that transfer rate. */ static struct { - short sxfr; + int sxfr; /* Rates in Ultra mode have bit 8 of sxfr set */ #define ULTRA_SXFR 0x100 - int period; /* in ns/4 */ + u_int8_t period; /* Period to send to SCSI target */ char *rate; } ahc_syncrates[] = { { 0x100, 12, "20.0" }, @@ -410,16 +378,17 @@ static int ahc_num_syncrates = */ #if defined(__FreeBSD__) struct ahc_data * -ahc_alloc(unit, iobase, type, flags) +ahc_alloc(unit, iobase, maddr, type, flags) int unit; - u_long iobase; + u_int32_t iobase; #elif defined(__NetBSD__) void -ahc_construct(ahc, bc, ioh, type, flags) +ahc_construct(ahc, bc, ioh, maddr, type, flags) struct ahc_data *ahc; bus_chipset_tag_t bc; bus_io_handle_t ioh; #endif + vm_offset_t maddr; ahc_type type; ahc_flag flags; { @@ -443,9 +412,7 @@ ahc_construct(ahc, bc, ioh, type, flags) bzero(ahc, sizeof(struct ahc_data)); #endif STAILQ_INIT(&ahc->free_scbs); - STAILQ_INIT(&ahc->page_scbs); STAILQ_INIT(&ahc->waiting_scbs); - STAILQ_INIT(&ahc->assigned_scbs); #if defined(__FreeBSD__) ahc->unit = unit; #endif @@ -455,6 +422,7 @@ ahc_construct(ahc, bc, ioh, type, flags) ahc->sc_bc = bc; ahc->sc_ioh = ioh; #endif + ahc->maddr = (volatile u_int8_t *)maddr; ahc->type = type; ahc->flags = flags; ahc->unpause = (AHC_INB(ahc, HCNTRL) & IRQMS) | INTEN; @@ -478,7 +446,7 @@ ahc_free(ahc) void #if defined(__FreeBSD__) ahc_reset(iobase) - u_long iobase; + u_int32_t iobase; #elif defined(__NetBSD__) ahc_reset(devname, bc, ioh) char *devname; @@ -486,7 +454,7 @@ ahc_reset(devname, bc, ioh) bus_io_handle_t ioh; #endif { - u_char hcntrl; + u_int8_t hcntrl; int wait; /* Retain the IRQ type accross the chip reset */ @@ -509,7 +477,7 @@ ahc_reset(devname, bc, ioh) while (--wait && !(bus_io_read_1(bc, ioh, HCNTRL) & CHIPRSTACK)) #endif DELAY(1000); - if(wait == 0) { + if (wait == 0) { #if defined(__FreeBSD__) printf("ahc at 0x%lx: WARNING - Failed chip reset! " "Trying to initialize anyway.\n", iobase); @@ -552,8 +520,8 @@ ahc_scsirate(ahc, scsirate, period, offset, channel, target ) * Watch out for Ultra speeds when ultra is not * enabled and vice-versa. */ - if(!(ahc->type & AHC_ULTRA) - && (ahc_syncrates[i].sxfr & ULTRA_SXFR)) { + if (!(ahc->type & AHC_ULTRA) + && (ahc_syncrates[i].sxfr & ULTRA_SXFR)) { /* * This should only happen if the * drive is the first to negotiate @@ -567,9 +535,9 @@ ahc_scsirate(ahc, scsirate, period, offset, channel, target ) | (*offset & 0x0f); *period = ahc_syncrates[i].period; - if(bootverbose) { - printf("%s: target %d synchronous at %sMHz," - " offset = 0x%x\n", + if (bootverbose) { + printf("%s: target %d synchronous at " + "%sMHz, offset = 0x%x\n", ahc_name(ahc), target, ahc_syncrates[i].rate, *offset ); } @@ -591,15 +559,14 @@ ahc_scsirate(ahc, scsirate, period, offset, channel, target ) * this target. */ ultra_enb_addr = ULTRA_ENB; - if(channel == 'B' || target > 7) + if (channel == 'B' || target > 7) ultra_enb_addr++; ultra_enb = AHC_INB(ahc, ultra_enb_addr); sxfrctl0 = AHC_INB(ahc, SXFRCTL0); if (*scsirate != 0 && ahc_syncrates[i].sxfr & ULTRA_SXFR) { ultra_enb |= 0x01 << (target & 0x07); sxfrctl0 |= ULTRAEN; - } - else { + } else { ultra_enb &= ~(0x01 << (target & 0x07)); sxfrctl0 &= ~ULTRAEN; } @@ -613,13 +580,13 @@ ahcprint(aux, name) void *aux; char *name; { - if (name != NULL) printf("%s: scsibus ", name); return UNCONF; } #endif + /* * Attach all the sub-devices we can find */ @@ -649,7 +616,7 @@ ahc_attach(ahc) ahc->sc_link.device = &ahc_dev; ahc->sc_link.flags = DEBUGLEVEL; - if(ahc->type & AHC_TWIN) { + if (ahc->type & AHC_TWIN) { /* Configure the second scsi bus */ ahc->sc_link_b = ahc->sc_link; #if defined(__FreeBSD__) @@ -672,27 +639,27 @@ ahc_attach(ahc) return 0; scbus->adapter_link = (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? &ahc->sc_link_b : &ahc->sc_link; - if(ahc->type & AHC_WIDE) + if (ahc->type & AHC_WIDE) scbus->maxtarg = 15; /* * ask the adapter what subunits are present */ - if(bootverbose) + if (bootverbose) printf("ahc%d: Probing channel %c\n", ahc->unit, (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? 'B' : 'A'); scsi_attachdevs(scbus); scbus = NULL; /* Upper-level SCSI code owns this now */ - if(ahc->type & AHC_TWIN) { + if (ahc->type & AHC_TWIN) { scbus = scsi_alloc_bus(); - if(!scbus) + if (!scbus) return 0; scbus->adapter_link = (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? &ahc->sc_link : &ahc->sc_link_b; - if(ahc->type & AHC_WIDE) + if (ahc->type & AHC_WIDE) scbus->maxtarg = 15; - if(bootverbose) + if (bootverbose) printf("ahc%d: Probing Channel %c\n", ahc->unit, (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? 'A': 'B'); scsi_attachdevs(scbus); @@ -738,208 +705,6 @@ ahc_attach(ahc) } /* - * Send an SCB down to the card via PIO. - * We assume that the proper SCB is already selected in SCBPTR. - */ -static inline void -ahc_send_scb(ahc, scb) - struct ahc_data *ahc; - struct scb *scb; -{ - AHC_OUTB(ahc, SCBCNT, SCBAUTO); - if( ahc->type == AHC_284 ) - /* Can only do 8bit PIO */ - AHC_OUTSB(ahc, SCBARRAY, scb, SCB_PIO_TRANSFER_SIZE); - else - AHC_OUTSL(ahc, SCBARRAY, scb, - (SCB_PIO_TRANSFER_SIZE + 3) / 4); - AHC_OUTB(ahc, SCBCNT, 0); -} - -/* - * Retrieve an SCB from the card via PIO. - * We assume that the proper SCB is already selected in SCBPTR. - */ -static inline void -ahc_fetch_scb(ahc, scb) - struct ahc_data *ahc; - struct scb *scb; -{ - AHC_OUTB(ahc, SCBCNT, 0x80); /* SCBAUTO */ - - /* Can only do 8bit PIO for reads */ - AHC_INSB(ahc, SCBARRAY, scb, SCB_PIO_TRANSFER_SIZE); - - AHC_OUTB(ahc, SCBCNT, 0); -} - -/* - * Swap in_scbp for out_scbp down in the cards SCB array. - * We assume that the SCB for out_scbp is already selected in SCBPTR. - */ -static inline void -ahc_page_scb(ahc, out_scbp, in_scbp) - struct ahc_data *ahc; - struct scb *out_scbp; - struct scb *in_scbp; -{ - /* Page-out */ - ahc_fetch_scb(ahc, out_scbp); - out_scbp->flags |= SCB_PAGED_OUT; - if(!(out_scbp->control & TAG_ENB)) - { - /* Stick in non-tagged array */ - int index = (out_scbp->tcl >> 4) - | (out_scbp->tcl & SELBUSB); - ahc->pagedout_ntscbs[index] = out_scbp; - } - - /* Page-in */ - in_scbp->position = out_scbp->position; - out_scbp->position = SCB_LIST_NULL; - ahc_send_scb(ahc, in_scbp); - in_scbp->flags &= ~SCB_PAGED_OUT; -} - -static inline void -ahc_run_waiting_queues(ahc) - struct ahc_data *ahc; -{ - struct scb* scb; - u_char cur_scb; - - if(!(ahc->assigned_scbs.stqh_first || ahc->waiting_scbs.stqh_first)) - return; - - pause_sequencer(ahc); - cur_scb = AHC_INB(ahc, SCBPTR); - - /* - * First handle SCBs that are waiting but have been - * assigned a slot. - */ - while((scb = ahc->assigned_scbs.stqh_first) != NULL) { - STAILQ_REMOVE_HEAD(&ahc->assigned_scbs, links); - AHC_OUTB(ahc, SCBPTR, scb->position); - ahc_send_scb(ahc, scb); - - /* Mark this as an active command */ - scb->flags ^= SCB_ASSIGNEDQ|SCB_ACTIVE; - - AHC_OUTB(ahc, QINFIFO, scb->position); - if (!(scb->xs->flags & SCSI_NOMASK)) { - timeout(ahc_timeout, (caddr_t)scb, - (scb->xs->timeout * hz) / 1000); - } - SC_DEBUG(scb->xs->sc_link, SDEV_DB3, ("cmd_sent\n")); - } - /* Now deal with SCBs that require paging */ - if((scb = ahc->waiting_scbs.stqh_first) != NULL) { - u_char disc_scb = AHC_INB(ahc, DISCONNECTED_SCBH); - u_char active = AHC_INB(ahc, FLAGS) & (SELECTED|IDENTIFY_SEEN); - int count = 0; - - do { - u_char next_scb; - - /* Attempt to page this SCB in */ - if(disc_scb == SCB_LIST_NULL) - break; - - /* - * Check the next SCB on in the list. - */ - AHC_OUTB(ahc, SCBPTR, disc_scb); - next_scb = AHC_INB(ahc, SCB_NEXT); - - /* - * We have to be careful about when we allow - * an SCB to be paged out. There must always - * be at least one slot availible for a - * reconnecting target in case it references - * an SCB that has been paged out. Our - * heuristic is that either the disconnected - * list has at least two entries in it or - * there is one entry and the sequencer is - * activily working on an SCB which implies that - * it will either complete or disconnect before - * another reconnection can occur. - */ - if((next_scb != SCB_LIST_NULL) || active) - { - u_char out_scbi; - struct scb* out_scbp; - - STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links); - - /* - * Find the in-core SCB for the one - * we're paging out. - */ - out_scbi = AHC_INB(ahc, SCB_TAG); - out_scbp = ahc->scbarray[out_scbi]; - - /* Do the page out */ - ahc_page_scb(ahc, out_scbp, scb); - - /* Mark this as an active command */ - scb->flags ^= SCB_WAITINGQ|SCB_ACTIVE; - - /* Queue the command */ - AHC_OUTB(ahc, QINFIFO, scb->position); - if (!(scb->xs->flags & SCSI_NOMASK)) { - timeout(ahc_timeout, (caddr_t)scb, - (scb->xs->timeout * hz) / 1000); - } - SC_DEBUG(scb->xs->sc_link, SDEV_DB3, - ("cmd_paged-in\n")); - count++; - - /* Advance to the next disconnected SCB */ - disc_scb = next_scb; - } - else - break; - } while((scb = ahc->waiting_scbs.stqh_first) != NULL); - - if(count) { - /* - * Update the head of the disconnected list. - */ - AHC_OUTB(ahc, DISCONNECTED_SCBH, disc_scb); - if(disc_scb != SCB_LIST_NULL) { - AHC_OUTB(ahc, SCBPTR, disc_scb); - AHC_OUTB(ahc, SCB_PREV, SCB_LIST_NULL); - } - } - } - /* Restore old position */ - AHC_OUTB(ahc, SCBPTR, cur_scb); - unpause_sequencer(ahc, /*unpause_always*/FALSE); -} - -/* - * Add this SCB to the head of the "waiting for selection" list. - */ -static -void ahc_add_waiting_scb(ahc, scb) - struct ahc_data *ahc; - struct scb *scb; -{ - u_char next; - u_char curscb; - - curscb = AHC_INB(ahc, SCBPTR); - next = AHC_INB(ahc, WAITING_SCBH); - - AHC_OUTB(ahc, SCBPTR, scb->position); - AHC_OUTB(ahc, SCB_NEXT, next); - AHC_OUTB(ahc, WAITING_SCBH, scb->position); - - AHC_OUTB(ahc, SCBPTR, curscb); -} - -/* * Catch an interrupt from the adapter */ #if defined(__FreeBSD__) @@ -950,12 +715,10 @@ int ahc_intr(arg) void *arg; { - int intstat; - u_char status; - struct scb *scb; - struct scsi_xfer *xs; - struct ahc_data *ahc = (struct ahc_data *)arg; + struct ahc_data *ahc; + u_int8_t intstat; + ahc = (struct ahc_data *)arg; intstat = AHC_INB(ahc, INTSTAT); /* * Is this interrupt for me? or for @@ -969,180 +732,65 @@ ahc_intr(arg) #endif if (intstat & BRKADRINT) { - /* We upset the sequencer :-( */ + /* + * We upset the sequencer :-( + * Lookup the error message + */ + int i, error, num_errors; - /* Lookup the error message */ - int i, error = AHC_INB(ahc, ERROR); - int num_errors = sizeof(hard_error)/sizeof(hard_error[0]); - for(i = 0; error != 1 && i < num_errors; i++) + error = AHC_INB(ahc, ERROR); + num_errors = sizeof(hard_error)/sizeof(hard_error[0]); + for (i = 0; error != 1 && i < num_errors; i++) error >>= 1; panic("%s: brkadrint, %s at seqaddr = 0x%x\n", ahc_name(ahc), hard_error[i].errmesg, (AHC_INB(ahc, SEQADDR1) << 8) | AHC_INB(ahc, SEQADDR0)); } - if (intstat & SEQINT) + if (intstat & SEQINT) ahc_handle_seqint(ahc, intstat); + + if (intstat & SCSIINT) + ahc_handle_scsiint(ahc, intstat); - if (intstat & SCSIINT) { - - int scb_index = AHC_INB(ahc, SCB_TAG); - status = AHC_INB(ahc, SSTAT1); - scb = ahc->scbarray[scb_index]; - - if (status & SCSIRSTI) { - char channel; - channel = AHC_INB(ahc, SBLKCTL); - channel = channel & SELBUSB ? 'B' : 'A'; - printf("%s: Someone reset channel %c\n", - ahc_name(ahc), channel); - ahc_reset_channel(ahc, - channel, - SCB_LIST_NULL, - XS_BUSY, - /* Initiate Reset */FALSE); - scb = NULL; - } - else if (!(scb && (scb->flags & SCB_ACTIVE))){ - printf("%s: ahc_intr - referenced scb not " - "valid during scsiint 0x%x scb(%d)\n", - ahc_name(ahc), status, scb_index); - AHC_OUTB(ahc, CLRSINT1, status); - unpause_sequencer(ahc, /*unpause_always*/TRUE); - AHC_OUTB(ahc, CLRINT, CLRSCSIINT); - scb = NULL; - } - else if (status & SCSIPERR) { - /* - * Determine the bus phase and - * queue an appropriate message - */ - char *phase; - u_char mesg_out = MSG_NOOP; - u_char lastphase = AHC_INB(ahc, LASTPHASE); - - xs = scb->xs; - sc_print_addr(xs->sc_link); - - switch(lastphase) { - case P_DATAOUT: - phase = "Data-Out"; - break; - case P_DATAIN: - phase = "Data-In"; - mesg_out = MSG_INITIATOR_DET_ERR; - break; - case P_COMMAND: - phase = "Command"; - break; - case P_MESGOUT: - phase = "Message-Out"; - break; - case P_STATUS: - phase = "Status"; - mesg_out = MSG_INITIATOR_DET_ERR; - break; - case P_MESGIN: - phase = "Message-In"; - mesg_out = MSG_PARITY_ERROR; - break; - default: - phase = "unknown"; - break; - } - printf("parity error during %s phase.\n", phase); + if (intstat & CMDCMPLT) { + struct scb *scb; + u_int8_t scb_index; + u_int8_t qoutcnt; + int int_cleared; - /* - * We've set the hardware to assert ATN if we - * get a parity error on "in" phases, so all we - * need to do is stuff the message buffer with - * the appropriate message. "In" phases have set - * mesg_out to something other than MSG_NOP. - */ - if(mesg_out != MSG_NOOP) { - AHC_OUTB(ahc, MSG0, mesg_out); - AHC_OUTB(ahc, MSG_LEN, 1); - } - else + int_cleared = 0; + while (qoutcnt = (AHC_INB(ahc, QOUTCNT) & ahc->qcntmask)) { + + for (; qoutcnt > 0; qoutcnt--) { + scb_index = AHC_INB(ahc, QOUTFIFO); + scb = ahc->scbarray[scb_index]; + if (!scb || !(scb->flags & SCB_ACTIVE)) { + printf("%s: WARNING " + "no command for scb %d " + "(cmdcmplt)\nQOUTCNT == %d\n", + ahc_name(ahc), scb_index, + qoutcnt); + continue; + } + untimeout(ahc_timeout, (caddr_t)scb); /* - * Should we allow the target to make - * this decision for us? + * Save off the residual if there is one. */ - xs->error = XS_DRIVER_STUFFUP; - } - else if (status & SELTO) { - u_char waiting; - u_char flags; - - xs = scb->xs; - xs->error = XS_SELTIMEOUT; - /* - * Clear any pending messages for the timed out - * target, and mark the target as free - */ - flags = AHC_INB(ahc, FLAGS); - AHC_OUTB(ahc, MSG_LEN, 0); - ahc_unbusy_target(ahc, xs->sc_link->target, -#if defined(__FreeBSD__) - ((long)xs->sc_link->fordriver & SELBUSB) -#elif defined(__NetBSD__) - IS_SCSIBUS_B(ahc, xs->sc_link) -#endif - ? 'B' : 'A'); - /* Stop the selection */ - AHC_OUTB(ahc, SCSISEQ, 0); - - AHC_OUTB(ahc, SCB_CONTROL, 0); - - AHC_OUTB(ahc, CLRSINT1, CLRSELTIMEO); - - AHC_OUTB(ahc, CLRINT, CLRSCSIINT); - - /* Shift the waiting for selection queue forward */ - waiting = AHC_INB(ahc, WAITING_SCBH); - AHC_OUTB(ahc, SCBPTR, waiting); - waiting = AHC_INB(ahc, SCB_NEXT); - AHC_OUTB(ahc, WAITING_SCBH, waiting); - - restart_sequencer(ahc); - } - else if (!(status & BUSFREE)) { - sc_print_addr(scb->xs->sc_link); - printf("Unknown SCSIINT. Status = 0x%x\n", status); - AHC_OUTB(ahc, CLRSINT1, status); - unpause_sequencer(ahc, /*unpause_always*/TRUE); - AHC_OUTB(ahc, CLRINT, CLRSCSIINT); - scb = NULL; - } - if(scb != NULL) { - /* We want to process the command */ - untimeout(ahc_timeout, (caddr_t)scb); - ahc_done(ahc, scb); - } - } - if (intstat & CMDCMPLT) { - int scb_index; - - do { - scb_index = AHC_INB(ahc, QOUTFIFO); - scb = ahc->scbarray[scb_index]; - if (!scb || !(scb->flags & SCB_ACTIVE)) { - printf("%s: WARNING " - "no command for scb %d (cmdcmplt)\n" - "QOUTCNT == %d\n", - ahc_name(ahc), scb_index, - AHC_INB(ahc, QOUTCNT)); - AHC_OUTB(ahc, CLRINT, CLRCMDINT); - continue; + if (scb->hscb->residual_SG_segment_count != 0) + ahc_calc_residual(scb); + ahc_done(ahc, scb); } AHC_OUTB(ahc, CLRINT, CLRCMDINT); - untimeout(ahc_timeout, (caddr_t)scb); - ahc_done(ahc, scb); - - } while (AHC_INB(ahc, QOUTCNT) & ahc->qcntmask); + int_cleared++; + } - ahc_run_waiting_queues(ahc); + if (int_cleared == 0) + AHC_OUTB(ahc, CLRINT, CLRCMDINT); } + + if (ahc->waiting_scbs.stqh_first != NULL) + ahc_run_waiting_queue(ahc); #if defined(__NetBSD__) return 1; #endif @@ -1154,9 +802,9 @@ ahc_handle_seqint(ahc, intstat) u_int8_t intstat; { struct scb *scb; - u_short targ_mask; - u_char target = (AHC_INB(ahc, SCSIID) >> 4) & 0x0f; - u_char scratch_offset = target; + u_int16_t targ_mask; + u_int8_t target = (AHC_INB(ahc, SCSIID) >> 4) & 0x0f; + int scratch_offset = target; char channel = AHC_INB(ahc, SBLKCTL) & SELBUSB ? 'B': 'A'; if (channel == 'B') @@ -1165,175 +813,19 @@ ahc_handle_seqint(ahc, intstat) switch (intstat & SEQINT_MASK) { case NO_MATCH: - if (ahc->flags & AHC_PAGESCBS) { - /* SCB Page-in request */ - u_char tag; - u_char next; - u_char disc_scb; - struct scb *outscb; - u_char arg_1 = AHC_INB(ahc, ARG_1); - - /* - * We should succeed, so set this now. - * If we don't, and one of the methods - * we use to aquire an SCB calls ahc_done, - * we may wind up in our start routine - * and unpause the adapter without giving - * it the correct return value, which will - * cause a hang. - */ - AHC_OUTB(ahc, RETURN_1, SCB_PAGEDIN); - - if (arg_1 == SCB_LIST_NULL) { - /* Non-tagged command */ - int index; - - index = target|(channel == 'B' ? SELBUSB : 0); - scb = ahc->pagedout_ntscbs[index]; - } else - scb = ahc->scbarray[arg_1]; - - if (!(scb->flags & SCB_PAGED_OUT)) - panic("%s: Request to page in a non paged out " - "SCB.", ahc_name(ahc)); - /* - * Now to pick the SCB to page out. - * Either take a free SCB, an assigned SCB, - * an SCB that just completed, the first - * one on the disconnected SCB list, or - * as a last resort a queued SCB. - */ - if (ahc->free_scbs.stqh_first) { - outscb = ahc->free_scbs.stqh_first; - STAILQ_REMOVE_HEAD(&ahc->free_scbs, links); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - STAILQ_INSERT_HEAD(&ahc->page_scbs, outscb, - links); - AHC_OUTB(ahc, SCBPTR, scb->position); - ahc_send_scb(ahc, scb); - scb->flags &= ~SCB_PAGED_OUT; - goto pagein_done; - } - if (intstat & CMDCMPLT) { - int scb_index; - - AHC_OUTB(ahc, CLRINT, CLRCMDINT); - scb_index = AHC_INB(ahc, QOUTFIFO); - if (!(AHC_INB(ahc, QOUTCNT) & ahc->qcntmask)) - intstat &= ~CMDCMPLT; - - outscb = ahc->scbarray[scb_index]; - if (!outscb || !(outscb->flags & SCB_ACTIVE)) { - printf("%s: WARNING no command for " - "scb %d (cmdcmplt)\n", - ahc_name(ahc), - scb_index); - /* - * Fall through in hopes of finding - * another SCB - */ - } else { - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - AHC_OUTB(ahc, SCBPTR, scb->position); - ahc_send_scb(ahc, scb); - scb->flags &= ~SCB_PAGED_OUT; - untimeout(ahc_timeout, - (caddr_t)outscb); - ahc_done(ahc, outscb); - goto pagein_done; - } - } - disc_scb = AHC_INB(ahc, DISCONNECTED_SCBH); - if (disc_scb != SCB_LIST_NULL) { - AHC_OUTB(ahc, SCBPTR, disc_scb); - tag = AHC_INB(ahc, SCB_TAG); - outscb = ahc->scbarray[tag]; - next = AHC_INB(ahc, SCB_NEXT); - if (next != SCB_LIST_NULL) { - AHC_OUTB(ahc, SCBPTR, next); - AHC_OUTB(ahc, SCB_PREV, - SCB_LIST_NULL); - AHC_OUTB(ahc, SCBPTR, disc_scb); - } - AHC_OUTB(ahc, DISCONNECTED_SCBH, next); - ahc_page_scb(ahc, outscb, scb); - } else if (AHC_INB(ahc, QINCNT) & ahc->qcntmask) { - /* - * Pull one of our queued commands - * as a last resort - */ - disc_scb = AHC_INB(ahc, QINFIFO); - AHC_OUTB(ahc, SCBPTR, disc_scb); - tag = AHC_INB(ahc, SCB_TAG); - outscb = ahc->scbarray[tag]; - if ((outscb->control & 0x23) != TAG_ENB) { - /* - * This is not a simple tagged command - * so its position in the queue - * matters. Take the command at the - * end of the queue instead. - */ - int i; - u_char saved_queue[AHC_SCB_MAX]; - u_char queued = AHC_INB(ahc, QINCNT) - & ahc->qcntmask; - - /* - * Count the command we removed - * already - */ - saved_queue[0] = disc_scb; - queued++; - - /* Empty the input queue */ - for (i = 1; i < queued; i++) - saved_queue[i] = AHC_INB(ahc, QINFIFO); - - /* - * Put everyone back but the - * last entry - */ - queued--; - for (i = 0; i < queued; i++) - AHC_OUTB(ahc, QINFIFO, - saved_queue[i]); - - AHC_OUTB(ahc, SCBPTR, - saved_queue[queued]); - tag = AHC_INB(ahc, SCB_TAG); - outscb = ahc->scbarray[tag]; - } - untimeout(ahc_timeout, (caddr_t)outscb); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - STAILQ_INSERT_HEAD(&ahc->waiting_scbs, - outscb, links); - outscb->flags |= SCB_WAITINGQ; - ahc_send_scb(ahc, scb); - scb->flags &= ~SCB_PAGED_OUT; - } - else { - panic("Page-in request with no candidates"); - AHC_OUTB(ahc, RETURN_1, 0); - } - pagein_done: - } else { - printf("%s:%c:%d: no active SCB for reconnecting " - "target - issuing ABORT\n", - ahc_name(ahc), channel, target); - printf("SAVED_TCL == 0x%x\n", - AHC_INB(ahc, SAVED_TCL)); - ahc_unbusy_target(ahc, target, channel); - AHC_OUTB(ahc, SCB_CONTROL, 0); - AHC_OUTB(ahc, CLRSINT1, CLRSELTIMEO); - AHC_OUTB(ahc, RETURN_1, 0); - } + printf("%s:%c:%d: no active SCB for reconnecting " + "target - issuing ABORT\n", + ahc_name(ahc), channel, target); + printf("SAVED_TCL == 0x%x\n", + AHC_INB(ahc, SAVED_TCL)); + ahc_unbusy_target(ahc, target, channel); + AHC_OUTB(ahc, SCB_CONTROL, 0); + AHC_OUTB(ahc, CLRSINT1, CLRSELTIMEO); + AHC_OUTB(ahc, RETURN_1, 0); break; case SEND_REJECT: { - u_char rejbyte = AHC_INB(ahc, REJBYTE); + u_int8_t rejbyte = AHC_INB(ahc, REJBYTE); printf("%s:%c:%d: Warning - unknown message recieved from " "target (0x%x). Rejecting\n", ahc_name(ahc), channel, target, rejbyte); @@ -1356,7 +848,7 @@ ahc_handle_seqint(ahc, intstat) message_length = AHC_INB(ahc, MSGIN_EXT_LEN); message_code = AHC_INB(ahc, MSGIN_EXT_OPCODE); - switch(message_code) { + switch (message_code) { case MSG_EXT_SDTR: { u_int8_t period; @@ -1411,10 +903,7 @@ ahc_handle_seqint(ahc, intstat) /* * Send our own SDTR in reply */ -#ifdef AHC_DEBUG - if(ahc_debug & AHC_SHOWMISC) - printf("Sending SDTR!!\n"); -#endif + printf("Sending SDTR!!\n"); ahc_construct_sdtr(ahc, /*start_byte*/0, period, offset); AHC_OUTB(ahc, RETURN_1, SEND_MSG); @@ -1452,12 +941,12 @@ ahc_handle_seqint(ahc, intstat) * target, since we asked first. */ AHC_OUTB(ahc, RETURN_1, 0); - switch(bus_width){ + switch (bus_width){ case BUS_8_BIT: scratch &= 0x7f; break; case BUS_16_BIT: - if(bootverbose) + if (bootverbose) printf("%s: target %d using " "16Bit transfers\n", ahc_name(ahc), target); @@ -1480,16 +969,16 @@ ahc_handle_seqint(ahc, intstat) /* * Send our own WDTR in reply */ - switch(bus_width) { + switch (bus_width) { case BUS_8_BIT: scratch &= 0x7f; break; case BUS_32_BIT: case BUS_16_BIT: - if(ahc->type & AHC_WIDE) { + if (ahc->type & AHC_WIDE) { /* Negotiate 16_BITS */ bus_width = BUS_16_BIT; - if(bootverbose) + if (bootverbose) printf("%s: target %d " "using 16Bit " "transfers\n", @@ -1527,12 +1016,12 @@ ahc_handle_seqint(ahc, intstat) * the target is refusing negotiation. */ - u_char targ_scratch; + u_int8_t targ_scratch; targ_scratch = AHC_INB(ahc, TARG_SCRATCH + scratch_offset); - if (ahc->wdtrpending & targ_mask){ + if (ahc->wdtrpending & targ_mask) { /* note 8bit xfers and clear flag */ targ_scratch &= 0x7f; ahc->needwdtr &= ~targ_mask; @@ -1540,7 +1029,7 @@ ahc_handle_seqint(ahc, intstat) printf("%s:%c:%d: refuses WIDE negotiation. Using " "8bit transfers\n", ahc_name(ahc), channel, target); - } else if(ahc->sdtrpending & targ_mask){ + } else if (ahc->sdtrpending & targ_mask) { /* note asynch xfers and clear flag */ targ_scratch &= 0xf0; ahc->needsdtr &= ~targ_mask; @@ -1554,7 +1043,7 @@ ahc_handle_seqint(ahc, intstat) * Otherwise, we ignore it. */ #ifdef AHC_DEBUG - if(ahc_debug & AHC_SHOWMISC) + if (ahc_debug & AHC_SHOWMISC) printf("%s:%c:%d: Message reject -- ignored\n", ahc_name(ahc), channel, target); #endif @@ -1566,18 +1055,23 @@ ahc_handle_seqint(ahc, intstat) } case BAD_STATUS: { - int scb_index; - struct scsi_xfer *xs; + u_int8_t scb_index; + struct scsi_xfer *xs; + struct hardware_scb *hscb; - /* The sequencer will notify us when a command + /* + * The sequencer will notify us when a command * has an error that would be of interest to * the kernel. This allows us to leave the sequencer * running in the common case of command completes - * without error. + * without error. The sequencer will already have + * dma'd the SCB back up to us, so we can reference + * the in kernel copy directly. */ scb_index = AHC_INB(ahc, SCB_TAG); scb = ahc->scbarray[scb_index]; + hscb = scb->hscb; /* * Set the default return value to 0 (don't @@ -1597,35 +1091,32 @@ ahc_handle_seqint(ahc, intstat) xs = scb->xs; - scb->status = AHC_INB(ahc, SCB_TARGET_STATUS); - -#ifdef AHC_DEBUG - if((ahc_debug & AHC_SHOWSCBS) - && xs->sc_link->target == DEBUGTARGET) - ahc_print_scb(scb); -#endif - xs->status = scb->status; - switch(scb->status){ + xs->status = hscb->status; + switch (hscb->status){ case SCSI_OK: printf("%s: Interrupted for staus of" " 0???\n", ahc_name(ahc)); break; case SCSI_CHECK: #ifdef AHC_DEBUG - if(ahc_debug & AHC_SHOWSENSE) - { + if (ahc_debug & AHC_SHOWSENSE) { sc_print_addr(xs->sc_link); printf("requests Check Status\n"); } #endif if ((xs->error == XS_NOERROR) - && !(scb->flags & SCB_SENSE)) { + && !(scb->flags & SCB_SENSE)) { struct ahc_dma_seg *sg = scb->ahc_dma; struct scsi_sense *sc = &(scb->sense_cmd); + + /* + * Save off the residual if there is one. + */ + if (hscb->residual_SG_segment_count != 0) + ahc_calc_residual(scb); #ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOWSENSE) - { + if (ahc_debug & AHC_SHOWSENSE) { sc_print_addr(xs->sc_link); printf("Sending Sense\n"); } @@ -1639,36 +1130,20 @@ ahc_handle_seqint(ahc, intstat) sc->length = sizeof(struct scsi_sense_data); sc->control = 0; - sg->addr = KVTOPHYS(&xs->sense); + sg->addr = vtophys(&xs->sense); sg->len = sizeof(struct scsi_sense_data); - scb->control &= DISCENB; - scb->status = 0; - scb->SG_segment_count = 1; - scb->SG_list_pointer = KVTOPHYS(sg); - scb->data = sg->addr; - scb->datalen = sg->len; -#ifdef AHC_BROKEN_CACHE - if (ahc_broken_cache) - INVALIDATE_CACHE(); -#endif - scb->cmdpointer = KVTOPHYS(sc); - scb->cmdlen = sizeof(*sc); + hscb->control &= DISCENB; + hscb->status = 0; + hscb->SG_segment_count = 1; + hscb->SG_list_pointer = vtophys(sg); + hscb->data = sg->addr; + hscb->datalen &= 0xFF000000; + hscb->datalen |= sg->len; + hscb->cmdpointer = vtophys(sc); + hscb->cmdlen = sizeof(*sc); scb->flags |= SCB_SENSE; - ahc_send_scb(ahc, scb); - /* - * Ensure that the target is "BUSY" - * so we don't get overlapping - * commands if we happen to be doing - * tagged I/O. - */ - ahc_busy_target(ahc, target, channel); - - /* - * Make us the next command to run - */ - ahc_add_waiting_scb(ahc, scb); AHC_OUTB(ahc, RETURN_1, SEND_SENSE); break; } @@ -1693,76 +1168,19 @@ ahc_handle_seqint(ahc, intstat) * The upper level SCSI code will someday * handle this properly. */ - sc_print_addr(xs->sc_link); - printf("Queue Full\n"); - scb->flags |= SCB_ASSIGNEDQ; - STAILQ_INSERT_TAIL(&ahc->assigned_scbs,scb, links); - AHC_OUTB(ahc, RETURN_1, SEND_SENSE); + xs->error = XS_BUSY; break; default: sc_print_addr(xs->sc_link); - printf("unexpected targ_status: %x\n", scb->status); + printf("unexpected targ_status: %x\n", hscb->status); xs->error = XS_DRIVER_STUFFUP; break; } break; } - case RESIDUAL: - { - int scb_index; - struct scsi_xfer *xs; - - scb_index = AHC_INB(ahc, SCB_TAG); - scb = ahc->scbarray[scb_index]; - xs = scb->xs; - /* - * Don't clobber valid resid info with - * a resid coming from a check sense - * operation. - */ - if (!(scb->flags & SCB_SENSE)) { - int resid_sgs; - - /* - * Remainder of the SG where the transfer - * stopped. - */ - xs->resid = (AHC_INB(ahc, SCB_RESID_DCNT2)<<16) | - (AHC_INB(ahc, SCB_RESID_DCNT1)<<8) | - AHC_INB(ahc, SCB_RESID_DCNT0); - - /* - * Add up the contents of all residual - * SG segments that are after the SG where - * the transfer stopped. - */ - resid_sgs = AHC_INB(ahc, SCB_RESID_SGCNT) - 1; - while (resid_sgs > 0) { - int sg; - - sg = scb->SG_segment_count - resid_sgs; - xs->resid += scb->ahc_dma[sg].len; - resid_sgs--; - } - -#if defined(__FreeBSD__) - xs->flags |= SCSI_RESID_VALID; -#elif defined(__NetBSD__) - /* XXX - Update to do this right */ -#endif -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOWMISC) { - sc_print_addr(xs->sc_link); - printf("Handled Residual of %ld bytes\n" - ,xs->resid); - } -#endif - } - break; - } case ABORT_TAG: { - int scb_index; + u_int8_t scb_index; struct scsi_xfer *xs; scb_index = AHC_INB(ahc, SCB_TAG); @@ -1782,13 +1200,13 @@ ahc_handle_seqint(ahc, intstat) case AWAITING_MSG: { int scb_index; + scb_index = AHC_INB(ahc, SCB_TAG); scb = ahc->scbarray[scb_index]; /* - * This SCB had a zero length command, informing - * the sequencer that we wanted to send a special - * message to this target. We only do this for - * BUS_DEVICE_RESET messages currently. + * This SCB had MK_MESSAGE set in its control byte, + * informing the sequencer that we wanted to send a + * special message to this target. */ if (scb->flags & SCB_DEVICE_RESET) { AHC_OUTB(ahc, MSG0, @@ -1824,7 +1242,7 @@ ahc_handle_seqint(ahc, intstat) ahc_construct_sdtr(ahc, AHC_INB(ahc, MSG_LEN), ahc_syncrates[i].period, - target_scratch & WIDEXFER ? + (target_scratch & WIDEXFER) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT); } else panic("ahc_intr: AWAITING_MSG for an SCB that " @@ -1836,10 +1254,11 @@ ahc_handle_seqint(ahc, intstat) /* * Take care of device reset messages */ - u_char scbindex = AHC_INB(ahc, SCB_TAG); + u_int8_t scbindex = AHC_INB(ahc, SCB_TAG); + scb = ahc->scbarray[scbindex]; if (scb->flags & SCB_DEVICE_RESET) { - u_char targ_scratch; + u_int8_t targ_scratch; int found; /* * Go back to async/narrow transfers and @@ -1856,7 +1275,7 @@ ahc_handle_seqint(ahc, intstat) AHC_OUTB(ahc, TARG_SCRATCH + scratch_offset, targ_scratch); found = ahc_reset_device(ahc, target, - channel, SCB_LIST_NULL, + channel, NULL, XS_NOERROR); sc_print_addr(scb->xs->sc_link); printf("Bus Device Reset delivered. " @@ -1876,7 +1295,7 @@ ahc_handle_seqint(ahc, intstat) * target to complete its transfer in * BITBUCKET mode. */ - u_char scbindex = AHC_INB(ahc, SCB_TAG); + u_int8_t scbindex = AHC_INB(ahc, SCB_TAG); u_int32_t overrun; scb = ahc->scbarray[scbindex]; overrun = AHC_INB(ahc, STCNT0) @@ -1922,6 +1341,147 @@ clear: unpause_sequencer(ahc, /*unpause_always*/TRUE); } +static void +ahc_handle_scsiint(ahc, intstat) + struct ahc_data *ahc; + u_int8_t intstat; +{ + u_int8_t scb_index; + u_int8_t status; + struct scb *scb; + + scb_index = AHC_INB(ahc, SCB_TAG); + status = AHC_INB(ahc, SSTAT1); + scb = ahc->scbarray[scb_index]; + + if (status & SCSIRSTI) { + char channel; + channel = (AHC_INB(ahc, SBLKCTL) & SELBUSB) ? 'B' : 'A'; + printf("%s: Someone reset channel %c\n", + ahc_name(ahc), channel); + ahc_reset_channel(ahc, + channel, + NULL, + XS_BUSY, + /* Initiate Reset */FALSE); + scb = NULL; + } else if (!(scb && (scb->flags & SCB_ACTIVE))){ + printf("%s: ahc_intr - referenced scb not " + "valid during scsiint 0x%x scb(%d)\n", + ahc_name(ahc), status, scb_index); + AHC_OUTB(ahc, CLRSINT1, status); + unpause_sequencer(ahc, /*unpause_always*/TRUE); + AHC_OUTB(ahc, CLRINT, CLRSCSIINT); + scb = NULL; + } else if (status & SCSIPERR) { + /* + * Determine the bus phase and + * queue an appropriate message + */ + char *phase; + u_int8_t mesg_out = MSG_NOOP; + u_int8_t lastphase = AHC_INB(ahc, LASTPHASE); + struct scsi_xfer *xs; + + xs = scb->xs; + sc_print_addr(xs->sc_link); + + switch (lastphase) { + case P_DATAOUT: + phase = "Data-Out"; + break; + case P_DATAIN: + phase = "Data-In"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_COMMAND: + phase = "Command"; + break; + case P_MESGOUT: + phase = "Message-Out"; + break; + case P_STATUS: + phase = "Status"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_MESGIN: + phase = "Message-In"; + mesg_out = MSG_PARITY_ERROR; + break; + default: + phase = "unknown"; + break; + } + printf("parity error during %s phase.\n", phase); + + /* + * We've set the hardware to assert ATN if we + * get a parity error on "in" phases, so all we + * need to do is stuff the message buffer with + * the appropriate message. "In" phases have set + * mesg_out to something other than MSG_NOP. + */ + if (mesg_out != MSG_NOOP) { + AHC_OUTB(ahc, MSG0, mesg_out); + AHC_OUTB(ahc, MSG_LEN, 1); + } else + /* + * Should we allow the target to make + * this decision for us? + */ + xs->error = XS_DRIVER_STUFFUP; + } else if (status & SELTO) { + struct scsi_xfer *xs; + u_int8_t scbptr; + u_int8_t nextscb; + u_int8_t flags; + + xs = scb->xs; + xs->error = XS_SELTIMEOUT; + /* + * Clear any pending messages for the timed out + * target, and mark the target as free + */ + flags = AHC_INB(ahc, FLAGS); + AHC_OUTB(ahc, MSG_LEN, 0); + ahc_unbusy_target(ahc, xs->sc_link->target, + IS_SCSIBUS_B(ahc, xs->sc_link) ? 'B' : 'A'); + /* Stop the selection */ + AHC_OUTB(ahc, SCSISEQ, 0); + + AHC_OUTB(ahc, SCB_CONTROL, 0); + + AHC_OUTB(ahc, CLRSINT1, CLRSELTIMEO); + + AHC_OUTB(ahc, CLRINT, CLRSCSIINT); + + /* Shift the waiting Q forward. */ + scbptr = AHC_INB(ahc, WAITING_SCBH); + AHC_OUTB(ahc, SCBPTR, scbptr); + nextscb = AHC_INB(ahc, SCB_NEXT); + AHC_OUTB(ahc, WAITING_SCBH, nextscb); + + /* Put this SCB back on the free list */ + nextscb = AHC_INB(ahc, FREE_SCBH); + AHC_OUTB(ahc, SCB_NEXT, nextscb); + AHC_OUTB(ahc, FREE_SCBH, scbptr); + restart_sequencer(ahc); + } else if (!(status & BUSFREE)) { + sc_print_addr(scb->xs->sc_link); + printf("Unknown SCSIINT. Status = 0x%x\n", status); + AHC_OUTB(ahc, CLRSINT1, status); + unpause_sequencer(ahc, /*unpause_always*/TRUE); + AHC_OUTB(ahc, CLRINT, CLRSCSIINT); + scb = NULL; + } + if (scb != NULL) { + /* We want to process the command */ + untimeout(ahc_timeout, (caddr_t)scb); + ahc_done(ahc, scb); + } +} + + /* * We have a scb which has been processed by the * adaptor, now we look to see how the operation @@ -1946,9 +1506,9 @@ ahc_done(ahc, scb) xs->error = XS_DRIVER_STUFFUP; } else #endif - if(scb->flags & SCB_SENSE) + if (scb->flags & SCB_SENSE) xs->error = XS_SENSE; - if(scb->flags & SCB_SENTORDEREDTAG) + if (scb->flags & SCB_SENTORDEREDTAG) ahc->in_timeout = FALSE; #if defined(__FreeBSD__) if ((xs->flags & SCSI_ERR_OK) && !(xs->error == XS_SENSE)) { @@ -1963,28 +1523,29 @@ ahc_done(ahc, scb) #endif xs->flags |= ITSDONE; #ifdef AHC_TAGENABLE - if(xs->cmd->opcode == INQUIRY && xs->error == XS_NOERROR) - { + /* + * This functionality is provided by the generic SCSI layer + * in FreeBSD 2.2. + */ + if (xs->cmd->opcode == INQUIRY && xs->error == XS_NOERROR) { struct scsi_inquiry_data *inq_data; - u_short mask = 0x01 << (xs->sc_link->target | - (scb->tcl & 0x08)); + u_int16_t mask = 0x01 << (xs->sc_link->target | + (scb->hscb->tcl & 0x08)); /* * Sneak a look at the results of the SCSI Inquiry * command and see if we can do Tagged queing. This * should really be done by the higher level drivers. */ inq_data = (struct scsi_inquiry_data *)xs->data; - if((inq_data->flags & SID_CmdQue) && !(ahc->tagenable & mask)) - { + if ((inq_data->flags & SID_CmdQue) + && !(ahc->tagenable & mask)) { printf("%s: target %d Tagged Queuing Device\n", ahc_name(ahc), xs->sc_link->target); ahc->tagenable |= mask; - if(ahc->maxhscbs >= 16 || (ahc->flags & AHC_PAGESCBS)) { + if (ahc->maxhscbs >= 16 || (ahc->flags&AHC_PAGESCBS)) { /* Default to 8 tags */ xs->sc_link->opennings += 6; - } - else - { + } else { /* * Default to 4 tags on whimpy * cards that don't have much SCB @@ -1997,8 +1558,8 @@ ahc_done(ahc, scb) } } } -#endif - ahc_free_scb(ahc, scb, xs->flags); +#endif /* AHC_TAGENABLE */ + ahc_free_scb(ahc, scb); scsi_done(xs); } @@ -2011,7 +1572,7 @@ ahc_init(ahc) { u_int8_t scsi_conf, sblkctl, i; u_int16_t ultraenable = 0; - int max_targ = 15; + int max_targ = 15; /* * Assume we have a board at this stage and it has been reset. */ @@ -2022,11 +1583,11 @@ ahc_init(ahc) #endif /* Determine channel configuration and who we are on the scsi bus. */ - switch ( (sblkctl = AHC_INB(ahc, SBLKCTL) & 0x0a) ) { - case 0: + switch ((sblkctl = AHC_INB(ahc, SBLKCTL) & 0x0a)) { + case 0: ahc->our_id = (AHC_INB(ahc, SCSICONF) & HSCSIID); ahc->flags &= ~AHC_CHANNEL_B_PRIMARY; - if(ahc->type == AHC_394) + if (ahc->type == AHC_394) printf("Channel %c, SCSI Id=%d, ", ahc->flags & AHC_CHNLB ? 'B' : 'A', ahc->our_id); @@ -2034,10 +1595,10 @@ ahc_init(ahc) printf("Single Channel, SCSI Id=%d, ", ahc->our_id); AHC_OUTB(ahc, FLAGS, SINGLE_BUS | (ahc->flags & AHC_PAGESCBS)); break; - case 2: + case 2: ahc->our_id = (AHC_INB(ahc, SCSICONF + 1) & HWSCSIID); ahc->flags &= ~AHC_CHANNEL_B_PRIMARY; - if(ahc->type == AHC_394) + if (ahc->type == AHC_394) printf("Wide Channel %c, SCSI Id=%d, ", ahc->flags & AHC_CHNLB ? 'B' : 'A', ahc->our_id); @@ -2046,7 +1607,7 @@ ahc_init(ahc) ahc->type |= AHC_WIDE; AHC_OUTB(ahc, FLAGS, WIDE_BUS | (ahc->flags & AHC_PAGESCBS)); break; - case 8: + case 8: ahc->our_id = (AHC_INB(ahc, SCSICONF) & HSCSIID); ahc->our_id_b = (AHC_INB(ahc, SCSICONF + 1) & HSCSIID); printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, ", @@ -2054,17 +1615,17 @@ ahc_init(ahc) ahc->type |= AHC_TWIN; AHC_OUTB(ahc, FLAGS, TWIN_BUS | (ahc->flags & AHC_PAGESCBS)); break; - default: + default: printf(" Unsupported adapter type. Ignoring\n"); return(-1); } - /* Determine the number of SCBs */ + /* Determine the number of SCBs and initialize them */ { - AHC_OUTB(ahc, SCBPTR, 0); - AHC_OUTB(ahc, SCB_CONTROL, 0); - for(i = 1; i < AHC_SCB_MAX; i++) { + /* SCB 0 heads the free list */ + AHC_OUTB(ahc, FREE_SCBH, 0); + for (i = 0; i < AHC_SCB_MAX; i++) { AHC_OUTB(ahc, SCBPTR, i); AHC_OUTB(ahc, SCB_CONTROL, i); if(AHC_INB(ahc, SCB_CONTROL) != i) @@ -2072,45 +1633,55 @@ ahc_init(ahc) AHC_OUTB(ahc, SCBPTR, 0); if(AHC_INB(ahc, SCB_CONTROL) != 0) break; - /* Clear the control byte. */ AHC_OUTB(ahc, SCBPTR, i); + + /* Clear the control byte. */ AHC_OUTB(ahc, SCB_CONTROL, 0); - ahc->qcntmask |= i; /* Update the count mask. */ + /* Set the next pointer */ + AHC_OUTB(ahc, SCB_NEXT, i+1); + + /* No Busy non-tagged targets yet */ + AHC_OUTB(ahc, SCB_ACTIVE0, SCB_LIST_NULL); + AHC_OUTB(ahc, SCB_ACTIVE1, SCB_LIST_NULL); + AHC_OUTB(ahc, SCB_ACTIVE2, SCB_LIST_NULL); + AHC_OUTB(ahc, SCB_ACTIVE3, SCB_LIST_NULL); } + /* Make that the last SCB terminates the free list */ + AHC_OUTB(ahc, SCBPTR, i-1); + AHC_OUTB(ahc, SCB_NEXT, SCB_LIST_NULL); + /* Ensure we clear the 0 SCB's control byte. */ AHC_OUTB(ahc, SCBPTR, 0); AHC_OUTB(ahc, SCB_CONTROL, 0); - ahc->qcntmask |= i; ahc->maxhscbs = i; } - if((ahc->maxhscbs < AHC_SCB_MAX) && (ahc->flags & AHC_PAGESCBS)) + if ((ahc->maxhscbs < AHC_SCB_MAX) && (ahc->flags & AHC_PAGESCBS)) { ahc->maxscbs = AHC_SCB_MAX; - else { + printf("%d/%d SCBs\n", ahc->maxhscbs, ahc->maxscbs); + } else { ahc->maxscbs = ahc->maxhscbs; ahc->flags &= ~AHC_PAGESCBS; + printf("%d SCBs\n", ahc->maxhscbs); } - printf("%d SCBs\n", ahc->maxhscbs); #ifdef AHC_DEBUG - if(ahc_debug & AHC_SHOWMISC) { - struct scb test; - printf("%s: hardware scb %ld bytes; kernel scb; " + if (ahc_debug & AHC_SHOWMISC) { + printf("%s: hardware scb %d bytes; kernel scb %d bytes; " "ahc_dma %d bytes\n", ahc_name(ahc), - (u_long)&(test.next) - (u_long)(&test), - sizeof(test), + sizeof(struct hardware_scb), + sizeof(struct scb), sizeof(struct ahc_dma_seg)); } #endif /* AHC_DEBUG */ /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/ - if(ahc->type & AHC_TWIN) - { + if (ahc->type & AHC_TWIN) { /* * The device is gated to channel B after a chip reset, * so set those values first @@ -2120,14 +1691,14 @@ ahc_init(ahc) AHC_OUTB(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL)) | ENSTIMER|ACTNEGEN|STPWEN); AHC_OUTB(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR); - if(ahc->type & AHC_ULTRA) + if (ahc->type & AHC_ULTRA) AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN|ULTRAEN); else AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN); - if(scsi_conf & RESET_SCSI) { + if (scsi_conf & RESET_SCSI) { /* Reset the bus */ - if(bootverbose) + if (bootverbose) printf("%s: Reseting Channel B\n", ahc_name(ahc)); AHC_OUTB(ahc, SCSISEQ, SCSIRSTO); @@ -2147,14 +1718,14 @@ ahc_init(ahc) AHC_OUTB(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL)) | ENSTIMER|ACTNEGEN|STPWEN); AHC_OUTB(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR); - if(ahc->type & AHC_ULTRA) + if (ahc->type & AHC_ULTRA) AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN|ULTRAEN); else AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN); - if(scsi_conf & RESET_SCSI) { + if (scsi_conf & RESET_SCSI) { /* Reset the bus */ - if(bootverbose) + if (bootverbose) printf("%s: Reseting Channel A\n", ahc_name(ahc)); AHC_OUTB(ahc, SCSISEQ, SCSIRSTO); @@ -2179,35 +1750,33 @@ ahc_init(ahc) ahc->needwdtr_orig = 0; /* Grab the disconnection disable table and invert it for our needs */ - if(ahc->flags & AHC_USEDEFAULTS) { + if (ahc->flags & AHC_USEDEFAULTS) { printf("%s: Host Adapter Bios disabled. Using default SCSI " "device parameters\n", ahc_name(ahc)); ahc->discenable = 0xff; - } - else + } else ahc->discenable = ~((AHC_INB(ahc, DISC_DSB + 1) << 8) | AHC_INB(ahc, DISC_DSB)); - if(!(ahc->type & (AHC_WIDE|AHC_TWIN))) + if (!(ahc->type & (AHC_WIDE|AHC_TWIN))) max_targ = 7; - for(i = 0; i <= max_targ; i++){ - u_char target_settings; + for (i = 0; i <= max_targ; i++) { + u_int8_t target_settings; if (ahc->flags & AHC_USEDEFAULTS) { target_settings = 0; /* 10MHz */ ahc->needsdtr_orig |= (0x01 << i); ahc->needwdtr_orig |= (0x01 << i); - } - else { + } else { /* Take the settings leftover in scratch RAM. */ target_settings = AHC_INB(ahc, TARG_SCRATCH + i); - if(target_settings & 0x0f){ + if (target_settings & 0x0f) { ahc->needsdtr_orig |= (0x01 << i); /*Default to a asyncronous transfers(0 offset)*/ target_settings &= 0xf0; } - if(target_settings & 0x80){ + if (target_settings & 0x80) { ahc->needwdtr_orig |= (0x01 << i); /* * We'll set the Wide flag when we @@ -2217,19 +1786,18 @@ ahc_init(ahc) */ target_settings &= 0x7f; } - if(ahc->type & AHC_ULTRA) { + if (ahc->type & AHC_ULTRA) { /* * Enable Ultra for any target that * has a valid ultra syncrate setting. */ - u_char rate = target_settings & 0x70; - if(rate == 0x00 || rate == 0x10 || - rate == 0x20 || rate == 0x40) { - if(rate == 0x40) { + u_int8_t rate = target_settings & 0x70; + if (rate == 0x00 || rate == 0x10 || + rate == 0x20 || rate == 0x40) { + if (rate == 0x40) { /* Treat 10MHz specially */ target_settings &= ~0x70; - } - else + } else ultraenable |= (0x01 << i); } } @@ -2242,7 +1810,7 @@ ahc_init(ahc) * leave these fields cleared when the BIOS is not * installed. */ - if(!(ahc->type & AHC_WIDE)) + if ((ahc->type & AHC_WIDE) == 0) ahc->needwdtr_orig = 0; ahc->needsdtr = ahc->needsdtr_orig; ahc->needwdtr = ahc->needwdtr_orig; @@ -2256,13 +1824,13 @@ ahc_init(ahc) #ifdef AHC_DEBUG /* How did we do? */ - if(ahc_debug & AHC_SHOWMISC) + if (ahc_debug & AHC_SHOWMISC) printf("NEEDSDTR == 0x%x\nNEEDWDTR == 0x%x\n" "DISCENABLE == 0x%x\n", ahc->needsdtr, ahc->needwdtr, ahc->discenable); #endif /* - * Set the number of availible SCBs + * Set the number of availible hardware SCBs */ AHC_OUTB(ahc, SCBCOUNT, ahc->maxhscbs); @@ -2273,15 +1841,73 @@ ahc_init(ahc) AHC_OUTB(ahc, COMP_SCBCOUNT, -i & 0xff); /* + * Allocate enough "hardware scbs" to handle + * the maximum number of concurrent transactions + * we can have active. We have to use contigmalloc + * if this array crosses a page boundary since the + * sequencer depends on this array being physically + * contiguous. + */ + { + size_t array_size; + u_int32_t hscb_physaddr; + + array_size = ahc->maxscbs * sizeof(struct hardware_scb); + if (array_size > PAGE_SIZE) { + ahc->hscbs = (struct hardware_scb *) + contigmalloc(array_size, M_DEVBUF, + M_NOWAIT, 0ul, 0xffffffff, PAGE_SIZE, + 0x10000); + } else { + ahc->hscbs = (struct hardware_scb *) + malloc(array_size, M_DEVBUF, M_NOWAIT); + } + + if (ahc->hscbs == NULL) { + printf("%s: unable to allocate hardware SCB array. " + "Failing attach\n"); + return (-1); + } + + /* Tell the sequencer where it can find the hscb array. */ + hscb_physaddr = vtophys(ahc->hscbs); + AHC_OUTB(ahc, HSCB_ADDR0, hscb_physaddr & 0xFF); + AHC_OUTB(ahc, HSCB_ADDR1, (hscb_physaddr >> 8)& 0xFF); + AHC_OUTB(ahc, HSCB_ADDR2, (hscb_physaddr >> 16)& 0xFF); + AHC_OUTB(ahc, HSCB_ADDR3, (hscb_physaddr >> 24)& 0xFF); + } + + /* + * Q-Full-Count. Some cards have more Q space + * then SCBs. + */ + if (ahc->type & AHC_AIC7770) { + ahc->qfullcount = 4; + ahc->qcntmask = 0x07; + } else if (ahc->type & AHC_AIC7850) { + ahc->qfullcount = 8; + ahc->qcntmask = 0x0f; + } else if (ahc->maxhscbs == 255) { + /* 7870/7880 with external SRAM */ + ahc->qfullcount = 255; + ahc->qcntmask = 0xff; + } else { + /* 7870/7880 */ + ahc->qfullcount = 16; + ahc->qcntmask = 0x1f; + } + + /* * QCount mask to deal with broken aic7850s that * sporatically get garbage in the upper bits of * their QCount registers. + * + * QFullCount to guard against overflowing the + * QINFIFO or QOUTFIFO when we are paging SCBs. */ AHC_OUTB(ahc, QCNTMASK, ahc->qcntmask); - /* We don't have any busy targets right now */ - AHC_OUTB(ahc, ACTIVE_A, 0); - AHC_OUTB(ahc, ACTIVE_B, 0); + AHC_OUTB(ahc, QFULLCNT, ahc->qfullcount); /* We don't have any waiting selections */ AHC_OUTB(ahc, WAITING_SCBH, SCB_LIST_NULL); @@ -2296,13 +1922,13 @@ ahc_init(ahc) * Load the Sequencer program and Enable the adapter * in "fast" mode. */ - if(bootverbose) + if (bootverbose) printf("%s: Downloading Sequencer Program...", ahc_name(ahc)); ahc_loadseq(ahc); - if(bootverbose) + if (bootverbose) printf("Done\n"); AHC_OUTB(ahc, SEQCTL, FASTMODE); @@ -2344,198 +1970,159 @@ static int32_t ahc_scsi_cmd(xs) struct scsi_xfer *xs; { - struct scb *scb; - struct ahc_dma_seg *sg; - int seg; /* scatter gather seg being worked on */ - int thiskv; - physaddr thisphys, nextphys; - int bytes_this_seg, bytes_this_page, datalen, flags; - struct ahc_data *ahc; - u_short mask; - int s; + struct scb *scb; + struct hardware_scb *hscb; + struct ahc_data *ahc; + u_int16_t mask; + int flags; + int s; ahc = (struct ahc_data *)xs->sc_link->adapter_softc; mask = (0x01 << (xs->sc_link->target -#if defined(__FreeBSD__) - | ((u_long)xs->sc_link->fordriver & 0x08))); -#elif defined(__NetBSD__) - | (IS_SCSIBUS_B(ahc, xs->sc_link) ? SELBUSB : 0) )); -#endif + | (IS_SCSIBUS_B(ahc, xs->sc_link) ? SELBUSB : 0))); SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahc_scsi_cmd\n")); + flags = xs->flags; /* * get an scb to use. If the transfer * is from a buf (possibly from interrupt time) * then we can't allow it to sleep */ - flags = xs->flags; - if (flags & ITSDONE) { - printf("%s: Already done?", ahc_name(ahc)); - xs->flags &= ~ITSDONE; - } - if (!(flags & INUSE)) { - printf("%s: Not in use?", ahc_name(ahc)); - xs->flags |= INUSE; - } - if (!(scb = ahc_get_scb(ahc, flags))) { + if ((scb = ahc_get_scb(ahc, flags)) == NULL) { xs->error = XS_DRIVER_STUFFUP; return (TRY_AGAIN_LATER); } + hscb = scb->hscb; SC_DEBUG(xs->sc_link, SDEV_DB3, ("start scb(%p)\n", scb)); scb->xs = xs; - if (flags & SCSI_RESET) { - scb->flags |= SCB_DEVICE_RESET|SCB_IMMED; - scb->control |= MK_MESSAGE; - } + /* * Put all the arguments for the xfer in the scb */ - - if(ahc->tagenable & mask) { - scb->control |= TAG_ENB; - if(ahc->orderedtag & mask) { - printf("Ordered Tag sent\n"); - scb->control |= 0x02; - ahc->orderedtag &= ~mask; - } - } - if(ahc->discenable & mask) - scb->control |= DISCENB; - if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask)) - { - scb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_WDTR; + if (ahc->discenable & mask) + hscb->control |= DISCENB; + + if (flags & SCSI_RESET) { + scb->flags |= SCB_DEVICE_RESET|SCB_IMMED; + hscb->control |= MK_MESSAGE; + } else if ((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask)) { ahc->wdtrpending |= mask; - } - else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask)) - { - scb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_SDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_WDTR; + } else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask)) { ahc->sdtrpending |= mask; - } - scb->tcl = ((xs->sc_link->target << 4) & 0xF0) | -#if defined(__FreeBSD__) - ((u_long)xs->sc_link->fordriver & 0x08) | -#elif defined(__NetBSD__) - (IS_SCSIBUS_B(ahc,xs->sc_link)? SELBUSB : 0)| -#endif - (xs->sc_link->lun & 0x07); - scb->cmdlen = xs->cmdlen; - scb->cmdpointer = KVTOPHYS(xs->cmd); + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_SDTR; + } else if (ahc->orderedtag & mask) { + /* XXX this should be handled by the upper SCSI layer */ + printf("Ordered Tag sent\n"); + hscb->control |= MSG_ORDERED_Q_TAG; + ahc->orderedtag &= ~mask; + } else if (hscb->control & DISCENB) { + if (ahc->tagenable & mask) + hscb->control |= TAG_ENB; + } + hscb->tcl = ((xs->sc_link->target << 4) & 0xF0) + | (IS_SCSIBUS_B(ahc,xs->sc_link)? SELBUSB : 0) + | (xs->sc_link->lun & 0x07); + hscb->cmdlen = xs->cmdlen; + hscb->cmdpointer = vtophys(xs->cmd); xs->resid = 0; xs->status = 0; - if (xs->datalen) { /* should use S/G only if not zero length */ - scb->SG_list_pointer = KVTOPHYS(scb->ahc_dma); - sg = scb->ahc_dma; + + /* Only use S/G if non-zero length */ + if (xs->datalen) { + int seg; + u_int32_t datalen; + vm_offset_t vaddr; + u_int32_t paddr; + u_int32_t nextpaddr; + struct ahc_dma_seg *sg; + seg = 0; - /* - * Set up the scatter gather block - */ - SC_DEBUG(xs->sc_link, SDEV_DB4, - ("%ld @%p:- ", xs->datalen, xs->data)); datalen = xs->datalen; - thiskv = (int) xs->data; - thisphys = KVTOPHYS(thiskv); - - while ((datalen) && (seg < AHC_NSEG)) { - bytes_this_seg = 0; - - /* put in the base address */ - sg->addr = thisphys; + vaddr = (vm_offset_t)xs->data; + paddr = vtophys(vaddr); + sg = scb->ahc_dma; + hscb->SG_list_pointer = vtophys(sg); - SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%lx", thisphys)); + while ((datalen > 0) && (seg < AHC_NSEG)) { + /* put in the base address and length */ + sg->addr = paddr; + sg->len = 0; /* do it at least once */ - nextphys = thisphys; - while ((datalen) && (thisphys == nextphys)) { + nextpaddr = paddr; + + while ((datalen > 0) && (paddr == nextpaddr)) { + u_int32_t size; /* * This page is contiguous (physically) * with the the last, just extend the * length */ /* how far to the end of the page */ - nextphys = (thisphys & (~(PAGE_SIZE- 1))) - + PAGE_SIZE; - bytes_this_page = nextphys - thisphys; - /**** or the data ****/ - bytes_this_page = min(bytes_this_page ,datalen); - bytes_this_seg += bytes_this_page; - datalen -= bytes_this_page; - - /* get more ready for the next page */ - thiskv = (thiskv & (~(PAGE_SIZE - 1))) - + PAGE_SIZE; - if (datalen) - thisphys = KVTOPHYS(thiskv); + nextpaddr = (paddr & (~PAGE_MASK)) + PAGE_SIZE; + + /* + * Compute the maximum size + */ + size = nextpaddr - paddr; + if (size > datalen) + size = datalen; + + sg->len += size; + vaddr += size; + datalen -= size; + if (datalen > 0) + paddr = vtophys(vaddr); } /* * next page isn't contiguous, finish the seg */ - SC_DEBUGN(xs->sc_link, SDEV_DB4, - ("(0x%x)", bytes_this_seg)); - sg->len = bytes_this_seg; - sg++; seg++; + sg++; } - scb->SG_segment_count = seg; + hscb->SG_segment_count = seg; /* Copy the first SG into the data pointer area */ - scb->data = scb->ahc_dma->addr; - scb->datalen = scb->ahc_dma->len; - SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n")); + hscb->data = scb->ahc_dma->addr; + hscb->datalen = scb->ahc_dma->len | (SCB_LIST_NULL << 24); if (datalen) { /* there's still data, must have run out of segs! */ printf("%s: ahc_scsi_cmd: more than %d DMA segs\n", ahc_name(ahc), AHC_NSEG); xs->error = XS_DRIVER_STUFFUP; - ahc_free_scb(ahc, scb, flags); + ahc_free_scb(ahc, scb); return (COMPLETE); } #ifdef AHC_BROKEN_CACHE if (ahc_broken_cache) INVALIDATE_CACHE(); #endif - } - else { + } else { /* * No data xfer, use non S/G values */ - scb->SG_segment_count = 0; - scb->SG_list_pointer = 0; - scb->data = 0; - scb->datalen = 0; + hscb->SG_segment_count = 0; + hscb->SG_list_pointer = 0; + hscb->data = 0; + hscb->datalen = (SCB_LIST_NULL << 24); } #ifdef AHC_DEBUG - if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARG)) + if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARGET)) ahc_print_scb(scb); #endif s = splbio(); - if( scb->position != SCB_LIST_NULL ) - { - /* We already have a valid slot */ - u_char curscb; + STAILQ_INSERT_TAIL(&ahc->waiting_scbs, scb, links); - pause_sequencer(ahc); - curscb = AHC_INB(ahc, SCBPTR); - AHC_OUTB(ahc, SCBPTR, scb->position); - ahc_send_scb(ahc, scb); - AHC_OUTB(ahc, SCBPTR, curscb); - AHC_OUTB(ahc, QINFIFO, scb->position); - unpause_sequencer(ahc, /*unpause_always*/FALSE); - scb->flags |= SCB_ACTIVE; - if (!(flags & SCSI_NOMASK)) { - timeout(ahc_timeout, (caddr_t)scb, - (xs->timeout * hz) / 1000); - } - SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n")); - } - else { - scb->flags |= SCB_WAITINGQ; - STAILQ_INSERT_TAIL(&ahc->waiting_scbs, scb, links); - ahc_run_waiting_queues(ahc); - } - if (!(flags & SCSI_NOMASK)) { + scb->flags |= SCB_ACTIVE; + + ahc_run_waiting_queue(ahc); + + if ((flags & SCSI_NOMASK) == 0) { + timeout(ahc_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000); splx(s); return (SUCCESSFULLY_QUEUED); } @@ -2550,76 +2137,79 @@ ahc_scsi_cmd(xs) ahc_timeout(scb); break; } - } while (!(xs->flags & ITSDONE)); /* a non command complete intr */ + } while ((xs->flags & ITSDONE) == 0); /* a non command complete intr */ splx(s); return (COMPLETE); } +/* + * Look for space in the QINFIFO and queue as many SCBs in the waiting + * queue as possible. Assumes that it is called at splbio(). + */ +static void +ahc_run_waiting_queue(ahc) + struct ahc_data *ahc; +{ + struct scb *scb; + + /* + * On aic78X0 chips, we rely on Auto Access Pause (AAP) + * instead of doing an explicit pause/unpause. + */ + if ((ahc->type & AHC_AIC78X0) == 0) + pause_sequencer(ahc); + + while ((scb = ahc->waiting_scbs.stqh_first) != NULL) { + + if (ahc->curqincnt >= ahc->qfullcount) { + ahc->curqincnt = AHC_INB(ahc, QINCNT) & ahc->qcntmask; + if (ahc->curqincnt >= ahc->qfullcount) + /* Still no space */ + break; + } + STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links); + AHC_OUTB(ahc, QINFIFO, scb->hscb->tag); + + if ((ahc->flags & AHC_PAGESCBS) != 0) + /* + * We only care about this statistic when paging + * since it is impossible to overflow the qinfifo + * in the non-paging case. + */ + ahc->curqincnt++; + } + if ((ahc->type & AHC_AIC78X0) == 0) + unpause_sequencer(ahc, /*Unpause always*/FALSE); +} /* * A scb (and hence an scb entry on the board is put onto the * free list. */ static void -ahc_free_scb(ahc, scb, flags) - struct ahc_data *ahc; - int flags; - struct scb *scb; -{ - struct scb *wscb; - unsigned int opri; +ahc_free_scb(ahc, scb) + struct ahc_data *ahc; + struct scb *scb; +{ + struct hardware_scb *hscb; + int opri; + + hscb = scb->hscb; opri = splbio(); /* Clean up for the next user */ scb->flags = SCB_FREE; - scb->control = 0; - scb->status = 0; - - if(scb->position == SCB_LIST_NULL) { - STAILQ_INSERT_HEAD(&ahc->page_scbs, scb, links); - if(!scb->links.stqe_next && !ahc->free_scbs.stqh_first) - /* - * If there were no SCBs availible, wake anybody waiting - * for one to come free. - */ - wakeup((caddr_t)&ahc->free_scbs); - } - /* - * If there are any SCBS on the waiting queue, - * assign the slot of this "freed" SCB to the first - * one. We'll run the waiting queues after all command - * completes for a particular interrupt are completed - * or when we start another command. - */ - else if((wscb = ahc->waiting_scbs.stqh_first) != NULL) { - STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links); - wscb->position = scb->position; - STAILQ_INSERT_TAIL(&ahc->assigned_scbs, wscb, links); - wscb->flags ^= SCB_WAITINGQ|SCB_ASSIGNEDQ; + hscb->control = 0; + hscb->status = 0; - /* - * The "freed" SCB will need to be assigned a slot - * before being used, so put it in the page_scbs - * queue. + STAILQ_INSERT_HEAD(&ahc->free_scbs, scb, links); + if (scb->links.stqe_next == NULL) { + /* + * If there were no SCBs availible, wake anybody waiting + * for one to come free. */ - scb->position = SCB_LIST_NULL; - STAILQ_INSERT_HEAD(&ahc->page_scbs, scb, links); - if(!scb->links.stqe_next && !ahc->free_scbs.stqh_first) - /* - * If there were no SCBs availible, wake anybody waiting - * for one to come free. - */ - wakeup((caddr_t)&ahc->free_scbs); - } - else { - STAILQ_INSERT_HEAD(&ahc->free_scbs, scb, links); - if(!scb->links.stqe_next && !ahc->page_scbs.stqh_first) - /* - * If there were no SCBs availible, wake anybody waiting - * for one to come free. - */ - wakeup((caddr_t)&ahc->free_scbs); + wakeup((caddr_t)&ahc->free_scbs); } #ifdef AHC_DEBUG ahc->activescbs--; @@ -2635,11 +2225,11 @@ ahc_free_scb(ahc, scb, flags) */ static struct scb * ahc_get_scb(ahc, flags) - struct ahc_data *ahc; - int flags; + struct ahc_data *ahc; + u_int32_t flags; { - unsigned opri; struct scb *scbp; + int opri; opri = splbio(); /* @@ -2647,40 +2237,17 @@ ahc_get_scb(ahc, flags) * but only if we can't allocate a new one. */ while (1) { - if((scbp = ahc->free_scbs.stqh_first)) { + if ((scbp = ahc->free_scbs.stqh_first)) { STAILQ_REMOVE_HEAD(&ahc->free_scbs, links); + } else if(ahc->numscbs < ahc->maxscbs) { + scbp = ahc_alloc_scb(ahc); + if (scbp == NULL) + printf("%s: Can't malloc SCB\n", ahc_name(ahc)); } - else if((scbp = ahc->page_scbs.stqh_first)) { - STAILQ_REMOVE_HEAD(&ahc->page_scbs, links); - } - else if(ahc->numscbs < ahc->maxscbs) { - scbp = (struct scb *) malloc(sizeof(struct scb), - M_TEMP, M_NOWAIT); - if (scbp) { - bzero(scbp, sizeof(struct scb)); - scbp->tag = ahc->numscbs; - if( ahc->numscbs < ahc->maxhscbs ) - scbp->position = ahc->numscbs; - else - scbp->position = SCB_LIST_NULL; - ahc->numscbs++; - /* - * Place in the scbarray - * Never is removed. - */ - ahc->scbarray[scbp->tag] = scbp; - } - else { - printf("%s: Can't malloc SCB\n", - ahc_name(ahc)); - } - } - else { - if (!(flags & SCSI_NOSLEEP)) { - tsleep((caddr_t)&ahc->free_scbs, PRIBIO, - "ahcscb", 0); - continue; - } + else if ((flags & SCSI_NOSLEEP) == 0) { + tsleep((caddr_t)&ahc->free_scbs, PRIBIO, + "ahcscb", 0); + continue; } break; } @@ -2699,6 +2266,64 @@ ahc_get_scb(ahc, flags) return (scbp); } + +static struct scb * +ahc_alloc_scb(ahc) + struct ahc_data *ahc; +{ + static struct ahc_dma_seg *next_sg_array = NULL; + static int sg_arrays_free; + struct scb *newscb; + + newscb = (struct scb *) malloc(sizeof(struct scb), M_DEVBUF, M_NOWAIT); + if (newscb != NULL) { + bzero(newscb, sizeof(struct scb)); + if (next_sg_array == NULL) { + size_t alloc_size = sizeof(struct ahc_dma_seg) + * AHC_NSEG; + sg_arrays_free = PAGE_SIZE / alloc_size; + /* Don't ever allocate more than we may need */ + sg_arrays_free = MIN(ahc->maxscbs - ahc->numscbs, + sg_arrays_free); + alloc_size *= sg_arrays_free; + if (alloc_size == 0) + panic("%s: SG list doesn't fit in a page", + ahc_name(ahc)); + next_sg_array = (struct ahc_dma_seg *) + malloc(alloc_size, M_DEVBUF, M_NOWAIT); + } + if (next_sg_array != NULL) { + struct hardware_scb *hscb; + + newscb->ahc_dma = next_sg_array; + sg_arrays_free--; + if (sg_arrays_free == 0) + next_sg_array = NULL; + else + next_sg_array = &next_sg_array[AHC_NSEG]; + hscb = &ahc->hscbs[ahc->numscbs]; + newscb->hscb = hscb; + hscb->control = 0; + hscb->status = 0; + hscb->tag = ahc->numscbs; + hscb->residual_data_count[2] = 0; + hscb->residual_data_count[1] = 0; + hscb->residual_data_count[0] = 0; + hscb->residual_SG_segment_count = 0; + ahc->numscbs++; + /* + * Place in the scbarray + * Never is removed. + */ + ahc->scbarray[hscb->tag] = newscb; + } else { + free(newscb, M_DEVBUF); + newscb = NULL; + } + } + return newscb; +} + static void ahc_loadseq(ahc) struct ahc_data *ahc; { @@ -2712,8 +2337,8 @@ static void ahc_loadseq(ahc) do { AHC_OUTB(ahc, SEQCTL, SEQRESET|FASTMODE); - } while((AHC_INB(ahc, SEQADDR0) != 0) - || (AHC_INB(ahc, SEQADDR1) != 0)); + } while ((AHC_INB(ahc, SEQADDR0) != 0) + || (AHC_INB(ahc, SEQADDR1) != 0)); } /* @@ -2745,7 +2370,6 @@ ahc_timeout(arg) struct ahc_data *ahc; int s, found; u_char bus_state; - char channel; s = splbio(); @@ -2770,9 +2394,8 @@ ahc_timeout(arg) */ panic("%s: Timed-out command times out " "again\n", ahc_name(ahc)); - } - else if (!(scb->flags & SCB_ABORTED)) - { + } else if ((scb->flags & (SCB_ABORTED | SCB_DEVICE_RESET + | SCB_SENTORDEREDTAG)) == 0) { /* * This is not the SCB that started this timeout * processing. Give this scb another lifetime so @@ -2804,181 +2427,164 @@ ahc_timeout(arg) switch(bus_state & PHASE_MASK) { - case P_DATAOUT: - printf("in dataout phase"); - break; - case P_DATAIN: - printf("in datain phase"); - break; - case P_COMMAND: - printf("in command phase"); - break; - case P_MESGOUT: - printf("in message out phase"); - break; - case P_STATUS: - printf("in status phase"); - break; - case P_MESGIN: - printf("in message in phase"); - break; - default: - printf("while idle, LASTPHASE == 0x%x", - bus_state); - /* - * We aren't in a valid phase, so assume we're - * idle. - */ - bus_state = 0; - break; + case P_DATAOUT: + printf("in dataout phase"); + break; + case P_DATAIN: + printf("in datain phase"); + break; + case P_COMMAND: + printf("in command phase"); + break; + case P_MESGOUT: + printf("in message out phase"); + break; + case P_STATUS: + printf("in status phase"); + break; + case P_MESGIN: + printf("in message in phase"); + break; + default: + printf("while idle, LASTPHASE == 0x%x", + bus_state); + /* + * We aren't in a valid phase, so assume we're + * idle. + */ + bus_state = 0; + break; } printf(", SCSISIGI == 0x%x\n", AHC_INB(ahc, SCSISIGI)); /* Decide our course of action */ - if(scb->flags & SCB_ABORTED) - { + if (scb->flags & SCB_ABORTED) { /* * Been down this road before. * Do a full bus reset. */ - char channel = (scb->tcl & SELBUSB) + char channel = (scb->hscb->tcl & SELBUSB) ? 'B': 'A'; - found = ahc_reset_channel(ahc, channel, scb->tag, + found = ahc_reset_channel(ahc, channel, scb, XS_TIMEOUT, /*Initiate Reset*/TRUE); - printf("%s: Issued Channel %c Bus Reset #1. " + printf("%s: Issued Channel %c Bus Reset. " "%d SCBs aborted\n", ahc_name(ahc), channel, found); ahc->in_timeout = FALSE; - } - else if(scb->control & TAG_ENB) { + } else if ((scb->hscb->control & TAG_ENB) != 0 + && (scb->flags & SCB_SENTORDEREDTAG) == 0) { /* * We could be starving this command * try sending an ordered tag command * to the target we come from. */ - scb->flags |= SCB_ABORTED|SCB_SENTORDEREDTAG; + scb->flags |= SCB_SENTORDEREDTAG; ahc->orderedtag |= 0xFF; timeout(ahc_timeout, (caddr_t)scb, (5 * hz)); unpause_sequencer(ahc, /*unpause_always*/FALSE); printf("Ordered Tag queued\n"); - goto done; - } - else { + } else { /* - * Send a Bus Device Reset Message: + * Send an Abort Message: * The target that is holding up the bus may not * be the same as the one that triggered this timeout * (different commands have different timeout lengths). - * It is also impossible to get a message to a target - * if we are in a "frozen" data transfer phase. Our - * strategy here is to queue a bus device reset message + * Our strategy here is to queue an abort message * to the timed out target if it is disconnected. * Otherwise, if we have an active target we stuff the - * message buffer with a bus device reset message and - * assert ATN in the hopes that the target will let go - * of the bus and finally disconnect. If this fails, - * we'll get another timeout 2 seconds later which will - * cause a bus reset. - * - * XXX If the SCB is paged out, we simply reset the - * bus. We should probably queue a new command - * instead. + * message buffer with an abort message and assert ATN + * in the hopes that the target will let go of the bus + * and go to the mesgout phase. If this fails, we'll + * get another timeout 2 seconds later which will attempt + * a bus reset. */ + u_int8_t saved_scbptr; + u_int8_t active_scb_index; + struct scb *active_scb; - /* Test to see if scb is disconnected */ - if( !(scb->flags & SCB_PAGED_OUT ) ){ - u_char active_scb; - struct scb *active_scbp; + saved_scbptr = AHC_INB(ahc, SCBPTR); + active_scb_index = AHC_INB(ahc, SCB_TAG); + active_scb = ahc->scbarray[active_scb_index]; + + if (bus_state != 0) { + /* Send the abort to the active SCB */ + AHC_OUTB(ahc, MSG_LEN, 1); + AHC_OUTB(ahc, MSG0, + (active_scb->hscb->control & TAG_ENB) == 0 ? + MSG_ABORT : MSG_ABORT_TAG); + AHC_OUTB(ahc, SCSISIGO, bus_state|ATNO); + sc_print_addr(active_scb->xs->sc_link); + printf("abort message in message buffer\n"); + active_scb->flags |= SCB_ABORTED; + if (active_scb != scb) { + untimeout(ahc_timeout, + (caddr_t)active_scb); + /* Give scb a new lease on life */ + timeout(ahc_timeout, (caddr_t)scb, + (scb->xs->timeout * hz) / 1000); + } + timeout(ahc_timeout, (caddr_t)active_scb, (2 * hz)); + unpause_sequencer(ahc, /*unpause_always*/FALSE); + } else { + u_int8_t hscb_index; + int disconnected; - active_scb = AHC_INB(ahc, SCBPTR); - active_scbp = ahc->scbarray[AHC_INB(ahc, SCB_TAG)]; - AHC_OUTB(ahc, SCBPTR, scb->position); + disconnected = FALSE; + hscb_index = find_scb(ahc, scb); + if (hscb_index == SCB_LIST_NULL) + disconnected = TRUE; + else { + AHC_OUTB(ahc, SCBPTR, hscb_index); + if (AHC_INB(ahc, SCB_CONTROL) & DISCONNECTED) + disconnected = TRUE; + } - if(AHC_INB(ahc, SCB_CONTROL) & DISCONNECTED) { - if(ahc->flags & AHC_PAGESCBS) { - /* - * Pull this SCB out of the - * disconnected list. - */ - u_char prev = AHC_INB(ahc, SCB_PREV); - u_char next = AHC_INB(ahc, SCB_NEXT); - if(prev == SCB_LIST_NULL) { - /* At the head */ - AHC_OUTB(ahc, DISCONNECTED_SCBH, - next ); - } - else { - AHC_OUTB(ahc, SCBPTR, prev); - AHC_OUTB(ahc, SCB_NEXT, next); - if(next != SCB_LIST_NULL) { - AHC_OUTB(ahc, SCBPTR, - next); - AHC_OUTB(ahc, SCB_PREV, - prev); - } - AHC_OUTB(ahc, SCBPTR, - scb->position); - } - } - scb->flags |= SCB_DEVICE_RESET|SCB_ABORTED; - scb->control &= DISCENB; - scb->control |= MK_MESSAGE; - scb->cmdlen = 0; - scb->SG_segment_count = 0; - scb->SG_list_pointer = 0; - scb->data = 0; - scb->datalen = 0; - ahc_send_scb(ahc, scb); - ahc_add_waiting_scb(ahc, scb); + scb->flags |= SCB_ABORTED; + if (disconnected) { + /* Simply set the ABORT_SCB control bit */ + scb->hscb->control |= ABORT_SCB; + if (hscb_index != SCB_LIST_NULL) + AHC_OUTB(ahc, SCB_CONTROL, ABORT_SCB); timeout(ahc_timeout, (caddr_t)scb, (2 * hz)); - sc_print_addr(scb->xs->sc_link); - printf("BUS DEVICE RESET message queued.\n"); - AHC_OUTB(ahc, SCBPTR, active_scb); - unpause_sequencer(ahc, /*unpause_always*/FALSE); - goto done; - } - /* Is the active SCB really active? */ - else if((active_scbp->flags & SCB_ACTIVE) && bus_state){ - AHC_OUTB(ahc, MSG_LEN, 1); - AHC_OUTB(ahc, MSG0, MSG_BUS_DEV_RESET); - AHC_OUTB(ahc, SCSISIGO, bus_state|ATNO); - sc_print_addr(active_scbp->xs->sc_link); - printf("asserted ATN - device reset in " - "message buffer\n"); - active_scbp->flags |= SCB_DEVICE_RESET - | SCB_ABORTED; - if(active_scbp != scb) { - untimeout(ahc_timeout, - (caddr_t)active_scbp); - /* Give scb a new lease on life */ - timeout(ahc_timeout, (caddr_t)scb, - (scb->xs->timeout * hz) / 1000); - } - timeout(ahc_timeout, (caddr_t)active_scbp, - (2 * hz)); - AHC_OUTB(ahc, SCBPTR, active_scb); - unpause_sequencer(ahc, /*unpause_always*/FALSE); - goto done; } + AHC_OUTB(ahc, SCBPTR, saved_scbptr); + unpause_sequencer(ahc, /*unpause_always*/FALSE); + if (!disconnected) + /* Go "immediatly" to the bus reset */ + timeout(ahc_timeout, (caddr_t)scb, hz / 2); } - /* - * No active target or a paged out SCB. - * Try reseting the bus. - */ - channel = (scb->tcl & SELBUSB) ? 'B': 'A'; - found = ahc_reset_channel(ahc, channel, scb->tag, - XS_TIMEOUT, - /*Initiate Reset*/TRUE); - printf("%s: Issued Channel %c Bus Reset #2. " - "%d SCBs aborted\n", ahc_name(ahc), channel, - found); - ahc->in_timeout = FALSE; } -done: splx(s); } +/* + * Look through the SCB array of the card and attempt to find the + * hardware SCB that corresponds to the passed in SCB. Return + * SCB_LIST_NULL if unsuccessful. This routine assumes that the + * card is already paused. + */ +static u_int8_t +find_scb(ahc, scb) + struct ahc_data *ahc; + struct scb *scb; +{ + u_int8_t saved_scbptr; + u_int8_t curindex; + + saved_scbptr = AHC_INB(ahc, SCBPTR); + curindex = 0; + for (curindex = 0; curindex < ahc->maxhscbs; curindex++) { + AHC_OUTB(ahc, SCBPTR, curindex); + if (AHC_INB(ahc, SCB_TAG) == scb->hscb->tag) + break; + } + AHC_OUTB(ahc, SCBPTR, saved_scbptr); + if (curindex > ahc->maxhscbs) + curindex = SCB_LIST_NULL; + + return curindex; +} /* * The device at the given target/channel has been reset. Abort @@ -2989,7 +2595,7 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error) struct ahc_data *ahc; int target; char channel; - u_char timedout_scb; + struct scb *timedout_scb; u_int32_t xs_error; { struct scb *scbp; @@ -3004,22 +2610,20 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error) * Search the QINFIFO. */ { - u_char saved_queue[AHC_SCB_MAX]; - u_char queued = AHC_INB(ahc, QINCNT) & ahc->qcntmask; + u_int8_t saved_queue[AHC_SCB_MAX]; + u_int8_t queued = AHC_INB(ahc, QINCNT) & ahc->qcntmask; for (i = 0; i < (queued - found); i++) { saved_queue[i] = AHC_INB(ahc, QINFIFO); - AHC_OUTB(ahc, SCBPTR, saved_queue[i]); - scbp = ahc->scbarray[AHC_INB(ahc, SCB_TAG)]; - if (ahc_match_scb (scbp, target, channel)){ + scbp = ahc->scbarray[saved_queue[i]]; + if (ahc_match_scb (scbp, target, channel)) { /* * We found an scb that needs to be aborted. */ scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE; scbp->xs->error |= xs_error; - if(scbp->position != timedout_scb) + if(scbp != timedout_scb) untimeout(ahc_timeout, (caddr_t)scbp); - AHC_OUTB(ahc, SCB_CONTROL, 0); i--; found++; } @@ -3034,7 +2638,7 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error) * Search waiting for selection list. */ { - u_char next, prev; + u_int8_t next, prev; next = AHC_INB(ahc, WAITING_SCBH); /* Start at head of list. */ prev = SCB_LIST_NULL; @@ -3042,15 +2646,11 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error) while (next != SCB_LIST_NULL) { AHC_OUTB(ahc, SCBPTR, next); scbp = ahc->scbarray[AHC_INB(ahc, SCB_TAG)]; - /* - * Select the SCB. - */ if (ahc_match_scb(scbp, target, channel)) { - next = ahc_abort_wscb(ahc, scbp, prev, - timedout_scb, xs_error); + next = ahc_abort_wscb(ahc, scbp, next, prev, + timedout_scb, xs_error); found++; - } - else { + } else { prev = next; next = AHC_INB(ahc, SCB_NEXT); } @@ -3062,20 +2662,15 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error) * are other (most likely tagged) commands that * were disconnected when the reset occured. */ - for(i = 0; i < ahc->numscbs; i++) { + for (i = 0; i < ahc->numscbs; i++) { scbp = ahc->scbarray[i]; - if((scbp->flags & SCB_ACTIVE) + if ((scbp->flags & SCB_ACTIVE) && ahc_match_scb(scbp, target, channel)) { /* Ensure the target is "free" */ ahc_unbusy_target(ahc, target, channel); - if( !(scbp->flags & SCB_PAGED_OUT) ) - { - AHC_OUTB(ahc, SCBPTR, scbp->position); - AHC_OUTB(ahc, SCB_CONTROL, 0); - } scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE; scbp->xs->error |= xs_error; - if(scbp->tag != timedout_scb) + if (scbp != timedout_scb) untimeout(ahc_timeout, (caddr_t)scbp); found++; } @@ -3089,22 +2684,23 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error) * scb that follows the one that we remove. */ static u_char -ahc_abort_wscb (ahc, scbp, prev, timedout_scb, xs_error) - struct ahc_data *ahc; - struct scb *scbp; - u_char prev; - u_char timedout_scb; +ahc_abort_wscb (ahc, scbp, scbpos, prev, timedout_scb, xs_error) + struct ahc_data *ahc; + struct scb *scbp; + u_int8_t scbpos; + u_int8_t prev; + struct scb *timedout_scb; u_int32_t xs_error; { - u_char curscbp, next; - int target = ((scbp->tcl >> 4) & 0x0f); - char channel = (scbp->tcl & SELBUSB) ? 'B' : 'A'; + u_int8_t curscb, next; + int target = ((scbp->hscb->tcl >> 4) & 0x0f); + char channel = (scbp->hscb->tcl & SELBUSB) ? 'B' : 'A'; /* * Select the SCB we want to abort and * pull the next pointer out of it. */ - curscbp = AHC_INB(ahc, SCBPTR); - AHC_OUTB(ahc, SCBPTR, scbp->position); + curscb = AHC_INB(ahc, SCBPTR); + AHC_OUTB(ahc, SCBPTR, scbpos); next = AHC_INB(ahc, SCB_NEXT); /* Clear the necessary fields */ @@ -3113,7 +2709,7 @@ ahc_abort_wscb (ahc, scbp, prev, timedout_scb, xs_error) ahc_unbusy_target(ahc, target, channel); /* update the waiting list */ - if( prev == SCB_LIST_NULL ) + if (prev == SCB_LIST_NULL) /* First in the list */ AHC_OUTB(ahc, WAITING_SCBH, next); else { @@ -3129,56 +2725,32 @@ ahc_abort_wscb (ahc, scbp, prev, timedout_scb, xs_error) * and inform the SCSI system that the command * has been aborted. */ - AHC_OUTB(ahc, SCBPTR, curscbp); + AHC_OUTB(ahc, SCBPTR, curscb); scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE; scbp->xs->error |= xs_error; - if(scbp->tag != timedout_scb) + if (scbp != timedout_scb) untimeout(ahc_timeout, (caddr_t)scbp); return next; } static void -ahc_busy_target(ahc, target, channel) - struct ahc_data *ahc; - u_char target; - char channel; -{ - u_char active; - u_long active_port = ACTIVE_A; - - if(target > 0x07 || channel == 'B') { - /* - * targets on the Second channel or - * above id 7 store info in byte two - * of HA_ACTIVE - */ - active_port++; - } - active = AHC_INB(ahc, active_port); - active |= (0x01 << (target & 0x07)); - AHC_OUTB(ahc, active_port, active); -} - -static void ahc_unbusy_target(ahc, target, channel) - struct ahc_data *ahc; - u_char target; - char channel; + struct ahc_data *ahc; + int target; + char channel; { - u_char active; - u_long active_port = ACTIVE_A; + u_int8_t active_scb; + u_int8_t info_scb; + u_int32_t scb_offset; - if(target > 0x07 || channel == 'B') { - /* - * targets on the Second channel or - * above id 7 store info in byte two - * of HA_ACTIVE - */ - active_port++; - } - active = AHC_INB(ahc, active_port); - active &= ~(0x01 << (target & 0x07)); - AHC_OUTB(ahc, active_port, active); + info_scb = target / 4; + if (channel == 'B') + info_scb += 2; + active_scb = AHC_INB(ahc, SCBPTR); + AHC_OUTB(ahc, SCBPTR, info_scb); + scb_offset = SCB_ACTIVE0 + (target & 0x03); + AHC_OUTB(ahc, scb_offset, SCB_LIST_NULL); + AHC_OUTB(ahc, SCBPTR, active_scb); } static void @@ -3192,53 +2764,55 @@ ahc_reset_current_bus(ahc) static int ahc_reset_channel(ahc, channel, timedout_scb, xs_error, initiate_reset) - struct ahc_data *ahc; - char channel; - u_char timedout_scb; + struct ahc_data *ahc; + char channel; + struct scb *timedout_scb; u_int32_t xs_error; - u_char initiate_reset; + int initiate_reset; { - u_char sblkctl; + u_int8_t sblkctl; char cur_channel; - u_long offset, offset_max; + u_int32_t offset, offset_max; int found; + int target; + int maxtarget; + maxtarget = 8; /* * Clean up all the state information for the * pending transactions on this bus. */ found = ahc_reset_device(ahc, ALL_TARGETS, channel, timedout_scb, xs_error); - if(channel == 'B'){ + if (channel == 'B') { ahc->needsdtr |= (ahc->needsdtr_orig & 0xff00); ahc->sdtrpending &= 0x00ff; - AHC_OUTB(ahc, ACTIVE_B, 0); offset = TARG_SCRATCH + 8; offset_max = TARG_SCRATCH + 16; - } - else if (ahc->type & AHC_WIDE){ + } else if (ahc->type & AHC_WIDE){ ahc->needsdtr = ahc->needsdtr_orig; ahc->needwdtr = ahc->needwdtr_orig; ahc->sdtrpending = 0; ahc->wdtrpending = 0; - AHC_OUTB(ahc, ACTIVE_A, 0); - AHC_OUTB(ahc, ACTIVE_B, 0); + maxtarget = 16; offset = TARG_SCRATCH; offset_max = TARG_SCRATCH + 16; - } - else{ + } else { ahc->needsdtr |= (ahc->needsdtr_orig & 0x00ff); ahc->sdtrpending &= 0xff00; - AHC_OUTB(ahc, ACTIVE_A, 0); offset = TARG_SCRATCH; offset_max = TARG_SCRATCH + 8; } - for(;offset < offset_max;offset++) { + + for (target = 0; target < maxtarget; target++) + ahc_unbusy_target(ahc, target, channel); + + for (; offset < offset_max; offset++) { /* * Revert to async/narrow transfers * until we renegotiate. */ - u_char targ_scratch; + u_int8_t targ_scratch; targ_scratch = AHC_INB(ahc, offset); targ_scratch &= SXFR; @@ -3249,31 +2823,24 @@ ahc_reset_channel(ahc, channel, timedout_scb, xs_error, initiate_reset) * Reset the bus if we are initiating this reset and * restart/unpause the sequencer */ - /* Case 1: Command for another bus is active */ sblkctl = AHC_INB(ahc, SBLKCTL); cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A'; - if(cur_channel != channel) - { - /* - * Stealthily reset the other bus - * without upsetting the current bus + if (cur_channel != channel) { + /* Case 1: Command for another bus is active + * Stealthily reset the other bus without + * upsetting the current bus. */ AHC_OUTB(ahc, SBLKCTL, sblkctl ^ SELBUSB); - if( initiate_reset ) - { + if (initiate_reset) ahc_reset_current_bus(ahc); - } AHC_OUTB(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO); AHC_OUTB(ahc, CLRINT, CLRSCSIINT); AHC_OUTB(ahc, SBLKCTL, sblkctl); unpause_sequencer(ahc, /*unpause_always*/TRUE); - } - /* Case 2: A command from this bus is active or we're idle */ - else { - if( initiate_reset ) - { + } else { + /* Case 2: A command from this bus is active or we're idle */ + if (initiate_reset) ahc_reset_current_bus(ahc); - } AHC_OUTB(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO); AHC_OUTB(ahc, CLRINT, CLRSCSIINT); restart_sequencer(ahc); @@ -3289,9 +2856,9 @@ ahc_run_done_queue(ahc) int i; struct scb *scbp; - for(i = 0; i < ahc->numscbs; i++) { + for (i = 0; i < ahc->numscbs; i++) { scbp = ahc->scbarray[i]; - if(scbp->flags & SCB_QUEUED_FOR_DONE) + if (scbp->flags & SCB_QUEUED_FOR_DONE) ahc_done(ahc, scbp); } } @@ -3302,8 +2869,8 @@ ahc_match_scb (scb, target, channel) int target; char channel; { - int targ = (scb->tcl >> 4) & 0x0f; - char chan = (scb->tcl & SELBUSB) ? 'B' : 'A'; + int targ = (scb->hscb->tcl >> 4) & 0x0f; + char chan = (scb->hscb->tcl & SELBUSB) ? 'B' : 'A'; if (target == ALL_TARGETS) return (chan == channel); @@ -3311,7 +2878,6 @@ ahc_match_scb (scb, target, channel) return ((chan == channel) && (targ == target)); } - static void ahc_construct_sdtr(ahc, start_byte, period, offset) struct ahc_data *ahc; @@ -3339,3 +2905,60 @@ ahc_construct_wdtr(ahc, start_byte, bus_width) AHC_OUTB(ahc, MSG3 + start_byte, bus_width); AHC_OUTB(ahc, MSG_LEN, start_byte + 4); } + +static void +ahc_calc_residual(scb) + struct scb *scb; +{ + struct scsi_xfer *xs; + struct hardware_scb *hscb; + int resid_sgs; + + xs = scb->xs; + hscb = scb->hscb; + + if ((scb->flags & SCB_SENSE) == 0) { + /* + * Remainder of the SG where the transfer + * stopped. + */ + xs->resid = (hscb->residual_data_count[2] <<16) | + (hscb->residual_data_count[1] <<8) | + (hscb->residual_data_count[0]); + + /* + * Add up the contents of all residual + * SG segments that are after the SG where + * the transfer stopped. + */ + resid_sgs = hscb->residual_SG_segment_count - 1; + while (resid_sgs > 0) { + int sg; + + sg = hscb->SG_segment_count - resid_sgs; + xs->resid += scb->ahc_dma[sg].len; + resid_sgs--; + } +#if defined(__FreeBSD__) + xs->flags |= SCSI_RESID_VALID; +#elif defined(__NetBSD__) + /* XXX - Update to do this right */ +#endif + } + + /* + * Clean out the residual information in this SCB for the + * next consumer of this SCB. + */ + hscb->residual_data_count[2] = 0; + hscb->residual_data_count[1] = 0; + hscb->residual_data_count[0] = 0; + hscb->residual_SG_segment_count = 0; + +#ifdef AHC_DEBUG + if (ahc_debug & AHC_SHOWMISC) { + sc_print_addr(xs->sc_link); + printf("Handled Residual of %ld bytes\n" ,xs->resid); + } +#endif +} diff --git a/sys/i386/scsi/aic7xxx.h b/sys/i386/scsi/aic7xxx.h index 33d0261..7f45ddc 100644 --- a/sys/i386/scsi/aic7xxx.h +++ b/sys/i386/scsi/aic7xxx.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.h,v 1.28 1996/05/30 07:19:59 gibbs Exp $ + * $Id: aic7xxx.h,v 1.28.2.2 1996/10/06 01:31:25 gibbs Exp $ */ #ifndef _AIC7XXX_H_ @@ -56,27 +56,23 @@ #endif #if defined(__FreeBSD__) -#define AHC_INB(ahc, port) \ - inb((ahc)->baseport+(port)) -#define AHC_INSB(ahc, port, valp, size) \ - insb((ahc)->baseport+(port), valp, size) -#define AHC_OUTB(ahc, port, val) \ - outb((ahc)->baseport+(port), val) +#define AHC_INB(ahc, port) \ + (((ahc)->maddr != NULL) ? \ + ((ahc)->maddr[port]) : \ + inb((ahc)->baseport+(port))) +#define AHC_OUTB(ahc, port, val) \ + (((ahc)->maddr != NULL) ? \ + ((ahc)->maddr[port] = (val)) : \ + outb((ahc)->baseport+(port), val)) #define AHC_OUTSB(ahc, port, valp, size) \ outsb((ahc)->baseport+(port), valp, size) -#define AHC_OUTSL(ahc, port, valp, size) \ - outsl((ahc)->baseport+(port), valp, size) #elif defined(__NetBSD__) #define AHC_INB(ahc, port) \ bus_io_read_1((ahc)->sc_bc, (ahc)->sc_ioh, port) -#define AHC_INSB(ahc, port, valp, size) \ - bus_io_read_multi_1((ahc)->sc_bc, (ahc)->sc_ioh, port, valp, size) #define AHC_OUTB(ahc, port, val) \ bus_io_write_1((ahc)->sc_bc, (ahc)->sc_ioh, port, val) #define AHC_OUTSB(ahc, port, valp, size) \ bus_io_write_multi_1((ahc)->sc_bc, (ahc)->sc_ioh, port, valp, size) -#define AHC_OUTSL(ahc, port, valp, size) \ - bus_io_write_multi_4((ahc)->sc_bc, (ahc)->sc_ioh, port, valp, size) #endif #define AHC_NSEG 256 /* number of dma segments supported */ @@ -161,46 +157,56 @@ typedef enum { }scb_flag; /* - * The driver keeps up to MAX_SCB scb structures per card in memory. Only the - * first 28 bytes of the structure need to be transfered to the card during - * normal operation. The fields starting at byte 28 are used for kernel level - * bookkeeping. + * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB + * consists of a "hardware SCB" mirroring the fields availible on the card + * and additional information the kernel stores for each transaction. */ -struct scb { -/* ------------ Begin hardware supported fields ---------------- */ -/*0*/ u_char control; -/*1*/ u_char tcl; /* 4/1/3 bits */ -/*2*/ u_char status; -/*3*/ u_char SG_segment_count; -/*4*/ physaddr SG_list_pointer; -/*8*/ u_char residual_SG_segment_count; -/*9*/ u_char residual_data_count[3]; -/*12*/ physaddr data; -/*16*/ u_int32_t datalen; /* Really only three bits, but its +struct hardware_scb { +/*0*/ u_int8_t control; +/*1*/ u_int8_t tcl; /* 4/1/3 bits */ +/*2*/ u_int8_t status; +/*3*/ u_int8_t SG_segment_count; +/*4*/ physaddr SG_list_pointer; +/*8*/ u_int8_t residual_SG_segment_count; +/*9*/ u_int8_t residual_data_count[3]; +/*12*/ physaddr data; +/*16*/ u_int32_t datalen; /* Really only three bits, but its * faster to treat it as a long on * a quad boundary. */ -/*20*/ physaddr cmdpointer; -/*24*/ u_char cmdlen; -/*25*/ u_char tag; /* Index into our kernel SCB array. +/*20*/ physaddr cmdpointer; +/*24*/ u_int8_t cmdlen; +/*25*/ u_int8_t tag; /* Index into our kernel SCB array. * Also used as the tag for tagged I/O */ #define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download * via PIO to initialize a transaction. */ -/*26*/ u_char next; /* Used for threading SCBs in the +/*26*/ u_int8_t next; /* Used for threading SCBs in the * "Waiting for Selection" and * "Disconnected SCB" lists down * in the sequencer. */ -/*27*/ u_char prev; -/*-----------------end of hardware supported fields----------------*/ - STAILQ_ENTRY(scb) links; /* for chaining */ - struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ - scb_flag flags; - u_char position; /* Position in card's scbarray */ - struct ahc_dma_seg ahc_dma[AHC_NSEG] __attribute__ ((packed)); - struct scsi_sense sense_cmd; /* SCSI command block */ +/*27*/ u_int8_t prev; + +/*28*/ u_int32_t pad; /* + * Unused by the kernel, but we require + * the padding so that the array of + * hardware SCBs is alligned on 32 byte + * boundaries so the sequencer can + * index them easily. + */ +}; + +struct scb +{ + struct hardware_scb *hscb; + STAILQ_ENTRY(scb) links; /* for chaining */ + struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ + scb_flag flags; + struct ahc_dma_seg *ahc_dma;/* Pointer to SG segments */ + struct scsi_sense sense_cmd; + u_int8_t position;/* Position in card's scbarray */ }; struct ahc_data { @@ -215,54 +221,59 @@ struct ahc_data { ahc_type type; ahc_flag flags; #if defined(__FreeBSD__) - u_long baseport; + u_int32_t baseport; #endif - struct scb *scbarray[AHC_SCB_MAX]; /* Mirror boards scbarray */ - struct scb *pagedout_ntscbs[16];/* - * Paged out, non-tagged scbs - * indexed by target. - */ + volatile u_int8_t *maddr; + struct hardware_scb *hscbs; /* Array of hardware SCBs */ + struct scb *scbarray[AHC_SCB_MAX]; /* Array of kernel SCBs */ STAILQ_HEAD(, scb) free_scbs; /* - * SCBs assigned to free slots - * on the card. (no paging required) - */ - STAILQ_HEAD(, scb) page_scbs; /* - * SCBs that will require paging - * before use (no assigned slot) + * Pool of SCBs ready to be assigned + * commands to execute. */ STAILQ_HEAD(, scb) waiting_scbs;/* - * SCBs waiting to be paged in - * and started. - */ - STAILQ_HEAD(, scb)assigned_scbs;/* - * SCBs that were waiting but have - * now been assigned a slot by - * ahc_free_scb. + * SCBs waiting ready to go but + * waiting for space in the QINFIFO. */ struct scsi_link sc_link; struct scsi_link sc_link_b; /* Second bus for Twin channel cards */ - u_short needsdtr_orig; /* Targets we initiate sync neg with */ - u_short needwdtr_orig; /* Targets we initiate wide neg with */ - u_short needsdtr; /* Current list of negotiated targets */ - u_short needwdtr; /* Current list of negotiated targets */ - u_short sdtrpending; /* Pending SDTR to these targets */ - u_short wdtrpending; /* Pending WDTR to these targets */ - u_short tagenable; /* Targets that can handle tagqueing */ - u_short orderedtag; /* Targets to use ordered tag on */ - u_short discenable; /* Targets allowed to disconnect */ - u_char our_id; /* our scsi id */ - u_char our_id_b; /* B channel scsi id */ - u_char numscbs; - u_char activescbs; - u_char maxhscbs; /* Number of SCBs on the card */ - u_char maxscbs; /* + u_int16_t needsdtr_orig; /* Targets we initiate sync neg with */ + u_int16_t needwdtr_orig; /* Targets we initiate wide neg with */ + u_int16_t needsdtr; /* Current list of negotiated targets */ + u_int16_t needwdtr; /* Current list of negotiated targets */ + u_int16_t sdtrpending; /* Pending SDTR to these targets */ + u_int16_t wdtrpending; /* Pending WDTR to these targets */ + u_int16_t tagenable; /* Targets that can handle tags */ + u_int16_t orderedtag; /* Targets to use ordered tag on */ + u_int16_t discenable; /* Targets allowed to disconnect */ + u_int8_t our_id; /* our scsi id */ + u_int8_t our_id_b; /* B channel scsi id */ + u_int8_t numscbs; + u_int8_t activescbs; + u_int8_t maxhscbs; /* Number of SCBs on the card */ + u_int8_t maxscbs; /* * Max SCBs we allocate total including * any that will force us to page SCBs */ - u_char qcntmask; - u_char unpause; - u_char pause; - u_char in_timeout; + u_int8_t qcntmask; /* + * Mask of valid registers in the + * Q*CNT registers. + */ + u_int8_t qfullcount; /* + * The maximum number of entries + * storable in the Q*FIFOs. + */ + u_int8_t curqincnt; /* + * The current value we "think" the + * QINCNT has. The reason it is + * "think" is that this is a cached + * value that is only updated when + * curqincount == qfullcount to reduce + * the amount of accesses made to the + * card. + */ + u_int8_t unpause; + u_int8_t pause; + u_int8_t in_timeout; }; /* #define AHC_DEBUG */ @@ -282,8 +293,8 @@ extern int ahc_debug; /* Initialized in i386/scsi/aic7xxx.c */ char *ahc_name __P((struct ahc_data *ahc)); -void ahc_reset __P((u_long iobase)); -struct ahc_data *ahc_alloc __P((int unit, u_long io_base, ahc_type type, ahc_flag flags)); +void ahc_reset __P((u_int32_t iobase)); +struct ahc_data *ahc_alloc __P((int unit, u_int32_t io_base, vm_offset_t maddr, ahc_type type, ahc_flag flags)); #elif defined(__NetBSD__) #define ahc_name(ahc) (ahc)->sc_dev.dv_xname |