diff options
author | gibbs <gibbs@FreeBSD.org> | 2000-10-05 04:24:14 +0000 |
---|---|---|
committer | gibbs <gibbs@FreeBSD.org> | 2000-10-05 04:24:14 +0000 |
commit | 53b245ea4d63e5de1d1046e01869c44196fa5e7d (patch) | |
tree | 0cf8ec6adc0bc1fdf748acaefd18fbfaa74c0348 | |
parent | abbeb4eb790e7fad36e1d81705b63cf6d631b7ee (diff) | |
download | FreeBSD-src-53b245ea4d63e5de1d1046e01869c44196fa5e7d.zip FreeBSD-src-53b245ea4d63e5de1d1046e01869c44196fa5e7d.tar.gz |
Convert the driver to use a single DMA for fetching new commands instead
of two (one to access the circular input fifo, the other to get the SCB).
This costs us a command slot so the driver can now only queue 254
simultaneous commands.
Have the kernel driver honor critical sections in sequencer code.
When prefetching S/G segments only pull a cacheline's worth but
never less than two elements. This reduces the impact of the
prefetch on the main data transfer when compared to the 128
byte fetches the driver used to do.
Add "bootverbose" logging for transfer negotiations.
Correct a bug in ahc_set_syncrate() that would prevent an update
of the sync parameters if only the ppr_options had changed.
Correct locking for calls to ahc_free_scb(). ahc_free_scb() is no
longer protected internally to simplify ports to other platforms.
Make sure we unfreeze our SIMQ if a resource shortage has occurred
and an SCB is been freed.
ahc_pci.c:
Turn on cacheline streaming for all controllers that support it.
Clarify diagnostic messages about PCI interrupts.
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.c | 413 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.h | 14 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.reg | 28 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.seq | 89 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx_freebsd.c | 18 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx_freebsd.h | 13 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx_inline.h | 72 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx_osm.c | 18 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx_osm.h | 13 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx_pci.c | 9 |
10 files changed, 474 insertions, 213 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c index 5c54b18..cc4f9d7 100644 --- a/sys/dev/aic7xxx/aic7xxx.c +++ b/sys/dev/aic7xxx/aic7xxx.c @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/src/aic7xxx/aic7xxx.c#4 $ + * $Id: //depot/src/aic7xxx/aic7xxx.c#9 $ * * $FreeBSD$ */ @@ -134,6 +134,9 @@ static struct tmode_tstate* u_int scsi_id, char channel); static void ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force); +static void ahc_qinfifo_requeue(struct ahc_softc *ahc, + struct scb *prev_scb, + struct scb *scb); static struct ahc_syncrate* ahc_devlimited_syncrate(struct ahc_softc *ahc, u_int *period, @@ -154,10 +157,13 @@ static void ahc_setup_initiator_msgout(struct ahc_softc *ahc, static void ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo); static void ahc_construct_sdtr(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, u_int period, u_int offset); static void ahc_construct_wdtr(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, u_int bus_width); static void ahc_construct_ppr(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, u_int period, u_int offset, u_int bus_width, u_int ppr_options); static void ahc_clear_msg_state(struct ahc_softc *ahc); @@ -222,30 +228,7 @@ static int ahc_handle_target_cmd(struct ahc_softc *ahc, void restart_sequencer(struct ahc_softc *ahc) { - u_int i; - pause_sequencer(ahc); - - /* XXX Use critical code sections.... */ - /* - * Everytime we restart the sequencer, there - * is the possiblitity that we have restarted - * within a three instruction window where an - * SCB has been marked free but has not made it - * onto the free list. Since SCSI events(bus reset, - * unexpected bus free) will always freeze the - * sequencer, we cannot close this window. To - * avoid losing an SCB, we reconsitute the free - * list every time we restart the sequencer. - */ - ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL); - for (i = 0; i < ahc->scb_data->maxhscbs; i++) { - - ahc_outb(ahc, SCBPTR, i); - if (ahc_inb(ahc, SCB_TAG) == SCB_LIST_NULL) { - ahc_add_curscb_to_free_list(ahc); - } - } ahc_outb(ahc, SCSISIGO, 0); /* De-assert BSY */ ahc_outb(ahc, MSG_OUT, MSG_NOOP); /* No message to send */ ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET); @@ -287,9 +270,8 @@ ahc_run_qoutfifo(struct ahc_softc *ahc) } ahc->qoutfifonext++; - scb = &ahc->scb_data->scbarray[scb_index]; - if (scb_index >= ahc->scb_data->numscbs - || (scb->flags & SCB_ACTIVE) == 0) { + scb = ahc_lookup_scb(ahc, scb_index); + if (scb == NULL) { printf("%s: WARNING no command for scb %d " "(cmdcmplt)\nQOUTPOS = %d\n", ahc_name(ahc), scb_index, @@ -378,6 +360,14 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) { u_int scb_index; struct hardware_scb *hscb; + + /* + * Set the default return value to 0 (don't + * send sense). The sense code will change + * this if needed. + */ + ahc_outb(ahc, RETURN_1, 0); + /* * The sequencer will notify us when a command * has an error that would be of interest to @@ -388,16 +378,8 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) * the in kernel copy directly. */ scb_index = ahc_inb(ahc, SCB_TAG); - scb = &ahc->scb_data->scbarray[scb_index]; - - /* - * Set the default return value to 0 (don't - * send sense). The sense code will change - * this if needed. - */ - ahc_outb(ahc, RETURN_1, 0); - if (!(scb_index < ahc->scb_data->numscbs - && (scb->flags & SCB_ACTIVE) != 0)) { + scb = ahc_lookup_scb(ahc, scb_index); + if (scb == NULL) { printf("%s:%c:%d: ahc_intr - referenced scb " "not valid during seqint 0x%x scb(%d)\n", ahc_name(ahc), devinfo.channel, @@ -645,7 +627,11 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) u_int scb_index; scb_index = ahc_inb(ahc, SCB_TAG); - scb = &ahc->scb_data->scbarray[scb_index]; + scb = ahc_lookup_scb(ahc, scb_index); + + if (scb == NULL) + panic("HOST_MSG_LOOP with " + "invalid SCB %x\n", scb_index); if (bus_phase == P_MESGOUT) ahc_setup_initiator_msgout(ahc, @@ -717,7 +703,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) u_int lastphase = ahc_inb(ahc, LASTPHASE); u_int i; - scb = &ahc->scb_data->scbarray[scbindex]; + scb = ahc_lookup_scb(ahc, scbindex); for (i = 0; i < num_phases; i++) { if (lastphase == phase_table[i].phase) break; @@ -780,6 +766,9 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) char cur_channel; char intr_channel; + /* Make sure the sequencer is in a safe location. */ + ahc_clear_critical_section(ahc); + if ((ahc->features & AHC_TWIN) != 0 && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0)) cur_channel = 'B'; @@ -805,12 +794,9 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) } scb_index = ahc_inb(ahc, SCB_TAG); - if (scb_index < ahc->scb_data->numscbs) { - scb = &ahc->scb_data->scbarray[scb_index]; - if ((scb->flags & SCB_ACTIVE) == 0 - || (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0) - scb = NULL; - } else + scb = ahc_lookup_scb(ahc, scb_index); + if (scb != NULL + && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0) scb = NULL; if ((status & SCSIRSTI) != 0) { @@ -1020,13 +1006,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) ahc_outb(ahc, SCBPTR, scbptr); scb_index = ahc_inb(ahc, SCB_TAG); - if (scb_index < ahc->scb_data->numscbs) { - scb = &ahc->scb_data->scbarray[scb_index]; - if ((scb->flags & SCB_ACTIVE) == 0) - scb = NULL; - } else - scb = NULL; - + scb = ahc_lookup_scb(ahc, scb_index); if (scb == NULL) { printf("%s: ahc_intr - referenced scb not " "valid during SELTO scb(%d, %d)\n", @@ -1064,6 +1044,45 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) } } +void +ahc_clear_critical_section(struct ahc_softc *ahc) +{ + int stepping; + + if (ahc->num_critical_sections == 0) + return; + + stepping = FALSE; + for (;;) { + struct cs *cs; + u_int seqaddr; + u_int i; + + seqaddr = ahc_inb(ahc, SEQADDR0) + | (ahc_inb(ahc, SEQADDR1) << 8); + + cs = ahc->critical_sections; + for (i = 0; i < ahc->num_critical_sections; i++, cs++) { + + if (cs->begin < seqaddr && cs->end >= seqaddr) + break; + } + + if (i == ahc->num_critical_sections) + break; + + if (!stepping) { + ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP); + stepping = TRUE; + } + ahc_outb(ahc, HCNTRL, ahc->unpause); + do { + ahc_delay(200); + } while (!sequencer_paused(ahc)); + } + if (stepping) + ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP); +} /* * Clear any pending interrupt status. @@ -1422,6 +1441,7 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct tmode_tstate *tstate; u_int old_period; u_int old_offset; + u_int old_ppr; int active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE; if (syncrate == NULL) { @@ -1433,9 +1453,12 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, devinfo->target, &tstate); old_period = tinfo->current.period; old_offset = tinfo->current.offset; + old_ppr = tinfo->current.ppr_options; if ((type & AHC_TRANS_CUR) != 0 - && (old_period != period || old_offset != offset)) { + && (old_period != period + || old_offset != offset + || old_ppr != ppr_options)) { u_int scsirate; scsirate = tinfo->scsirate; @@ -1660,7 +1683,7 @@ ahc_update_pending_syncrates(struct ahc_softc *ahc) struct tmode_tstate *tstate; u_int control; - pending_scb = &ahc->scb_data->scbarray[scb_tag]; + pending_scb = ahc_lookup_scb(ahc, scb_tag); if (pending_scb->flags == SCB_FREE) continue; pending_hscb = pending_scb->hscb; @@ -1873,13 +1896,14 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) offset = tinfo->goal.offset; ahc_validate_offset(ahc, rate, &offset, tinfo->current.width); - if (use_ppr) - ahc_construct_ppr(ahc, period, offset, + if (use_ppr) { + ahc_construct_ppr(ahc, devinfo, period, offset, tinfo->goal.width, ppr_options); - else - ahc_construct_sdtr(ahc, period, offset); + } else { + ahc_construct_sdtr(ahc, devinfo, period, offset); + } } else { - ahc_construct_wdtr(ahc, tinfo->goal.width); + ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width); } } @@ -1888,7 +1912,8 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) * buffer based on the input parameters. */ static void -ahc_construct_sdtr(struct ahc_softc *ahc, u_int period, u_int offset) +ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, + u_int period, u_int offset) { ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN; @@ -1896,6 +1921,11 @@ ahc_construct_sdtr(struct ahc_softc *ahc, u_int period, u_int offset) ahc->msgout_buf[ahc->msgout_index++] = period; ahc->msgout_buf[ahc->msgout_index++] = offset; ahc->msgout_len += 5; + if (bootverbose) { + printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n", + ahc_name(ahc), devinfo->channel, devinfo->target, + devinfo->lun, period, offset); + } } /* @@ -1903,13 +1933,19 @@ ahc_construct_sdtr(struct ahc_softc *ahc, u_int period, u_int offset) * buffer based on the input parameters. */ static void -ahc_construct_wdtr(struct ahc_softc *ahc, u_int bus_width) +ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, + u_int bus_width) { ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR; ahc->msgout_buf[ahc->msgout_index++] = bus_width; ahc->msgout_len += 4; + if (bootverbose) { + printf("(%s:%c:%d:%d): Sending WDTR %x\n", + ahc_name(ahc), devinfo->channel, devinfo->target, + devinfo->lun, bus_width); + } } /* @@ -1917,8 +1953,9 @@ ahc_construct_wdtr(struct ahc_softc *ahc, u_int bus_width) * buffer based on the input parameters. */ static void -ahc_construct_ppr(struct ahc_softc *ahc, u_int period, u_int offset, - u_int bus_width, u_int ppr_options) +ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, + u_int period, u_int offset, u_int bus_width, + u_int ppr_options) { ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN; @@ -1929,6 +1966,12 @@ ahc_construct_ppr(struct ahc_softc *ahc, u_int period, u_int offset, ahc->msgout_buf[ahc->msgout_index++] = bus_width; ahc->msgout_buf[ahc->msgout_index++] = ppr_options; ahc->msgout_len += 8; + if (bootverbose) { + printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, " + "offset %x, ppr_options %x\n", ahc_name(ahc), + devinfo->channel, devinfo->target, devinfo->lun, + bus_width, period, offset, ppr_options); + } } /* @@ -2311,6 +2354,15 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) &ppr_options); ahc_validate_offset(ahc, syncrate, &offset, targ_scsirate & WIDEXFER); + if (bootverbose) { + printf("(%s:%c:%d:%d): Received " + "SDTR period %x, offset %x\n\t" + "Filtered to period %x, offset %x\n", + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun, + ahc->msgin_buf[3], saved_offset, + period, offset); + } ahc_set_syncrate(ahc, devinfo, syncrate, period, offset, ppr_options, @@ -2332,11 +2384,16 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) /* * Send our own SDTR in reply */ - if (bootverbose) - printf("Sending SDTR!\n"); + if (bootverbose) { + printf("(%s:%c:%d:%d): Target " + "Initiated SDTR\n", + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun); + } ahc->msgout_index = 0; ahc->msgout_len = 0; - ahc_construct_sdtr(ahc, period, offset); + ahc_construct_sdtr(ahc, devinfo, + period, offset); ahc->msgout_index = 0; response = TRUE; } @@ -2368,6 +2425,13 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) bus_width = ahc->msgin_buf[3]; saved_width = bus_width; ahc_validate_width(ahc, &bus_width); + if (bootverbose) { + printf("(%s:%c:%d:%d): Received WDTR " + "%x filtered to %x\n", + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun, + saved_width, bus_width); + } if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/TRUE)) { /* @@ -2378,9 +2442,10 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) */ if (saved_width > bus_width) { reject = TRUE; - printf("%s: target %d requested %dBit " + printf("(%s:%c:%d:%d): requested %dBit " "transfers. Rejecting...\n", - ahc_name(ahc), devinfo->target, + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun, 8 * (0x01 << bus_width)); bus_width = 0; } @@ -2388,11 +2453,15 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) /* * Send our own WDTR in reply */ - if (bootverbose) - printf("Sending WDTR!\n"); + if (bootverbose) { + printf("(%s:%c:%d:%d): Target " + "Initiated WDTR\n", + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun); + } ahc->msgout_index = 0; ahc->msgout_len = 0; - ahc_construct_wdtr(ahc, bus_width); + ahc_construct_wdtr(ahc, devinfo, bus_width); ahc->msgout_index = 0; response = TRUE; sending_reply = TRUE; @@ -2407,25 +2476,10 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) AHC_TRANS_ACTIVE, /*paused*/TRUE); if (sending_reply == FALSE && reject == FALSE) { - /* XXX functionalize */ if (tinfo->goal.period) { - struct ahc_syncrate *rate; - u_int period; - u_int ppr; - u_int offset; - - /* Start the sync negotiation */ - period = tinfo->goal.period; - ppr = 0; - rate = ahc_devlimited_syncrate(ahc, - &period, - &ppr); - offset = tinfo->goal.offset; - ahc_validate_offset(ahc, rate, &offset, - tinfo->current.width); ahc->msgout_index = 0; ahc->msgout_len = 0; - ahc_construct_sdtr(ahc, period, offset); + ahc_build_transfer_msg(ahc, devinfo); ahc->msgout_index = 0; response = TRUE; } @@ -2497,12 +2551,29 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) */ if (saved_width > bus_width || saved_offset != offset - || saved_ppr_options != ppr_options) + || saved_ppr_options != ppr_options) { reject = TRUE; + period = 0; + offset = 0; + bus_width = 0; + ppr_options = 0; + syncrate = NULL; + } } else { printf("Target Initated PPR detected!\n"); response = TRUE; } + if (bootverbose) { + printf("(%s:%c:%d:%d): Received PPR width %x, " + "period %x, offset %x,options %x\n" + "\tFiltered to width %x, period %x, " + "offset %x, options %x\n", + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun, + ahc->msgin_buf[3], saved_width, + saved_offset, saved_ppr_options, + bus_width, period, offset, ppr_options); + } ahc_set_width(ahc, devinfo, bus_width, AHC_TRANS_ACTIVE|AHC_TRANS_GOAL, /*paused*/TRUE); @@ -2604,8 +2675,7 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) int response = 0; scb_index = ahc_inb(ahc, SCB_TAG); - scb = &ahc->scb_data->scbarray[scb_index]; - + scb = ahc_lookup_scb(ahc, scb_index); tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid, devinfo->target, &tstate); @@ -2617,6 +2687,12 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) * Target does not support the PPR message. * Attempt to negotiate SPI-2 style. */ + if (bootverbose) { + printf("(%s:%c:%d:%d): PPR Rejected. " + "Trying WDTR/SDTR\n", + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun); + } tinfo->goal.ppr_options = 0; tinfo->current.transport_version = 2; tinfo->goal.transport_version = 2; @@ -2628,9 +2704,9 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) } else if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/FALSE)) { /* note 8bit xfers */ - printf("%s:%c:%d: refuses WIDE negotiation. Using " + printf("(%s:%c:%d:%d): refuses WIDE negotiation. Using " "8bit transfers\n", ahc_name(ahc), - devinfo->channel, devinfo->target); + devinfo->channel, devinfo->target, devinfo->lun); ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT, AHC_TRANS_ACTIVE|AHC_TRANS_GOAL, /*paused*/TRUE); @@ -2656,15 +2732,15 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) /*offset*/0, /*ppr_options*/0, AHC_TRANS_ACTIVE|AHC_TRANS_GOAL, /*paused*/TRUE); - printf("%s:%c:%d: refuses synchronous negotiation. " + printf("(%s:%c:%d:%d): refuses synchronous negotiation. " "Using asynchronous transfers\n", - ahc_name(ahc), - devinfo->channel, devinfo->target); + ahc_name(ahc), devinfo->channel, + devinfo->target, devinfo->lun); } else if ((scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0) { - printf("%s:%c:%d: refuses tagged commands. Performing " + printf("(%s:%c:%d:%d): refuses tagged commands. Performing " "non-tagged I/O\n", ahc_name(ahc), - devinfo->channel, devinfo->target); + devinfo->channel, devinfo->target, devinfo->lun); ahc_set_tags(ahc, devinfo, FALSE); /* @@ -2723,7 +2799,7 @@ ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) struct scb *scb; scb_index = ahc_inb(ahc, SCB_TAG); - scb = &ahc->scb_data->scbarray[scb_index]; + scb = ahc_lookup_scb(ahc, scb_index); /* * XXX Actually check data direction in the sequencer? * Perhaps add datadir to some spare bits in the hscb? @@ -3364,6 +3440,12 @@ ahc_init_scbdata(struct ahc_softc *ahc) } /* + * Tell the sequencer which SCB will be the next one it receives. + */ + ahc->next_queued_scb = ahc_get_scb(ahc); + ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag); + + /* * Note that we were successfull */ return (0); @@ -4104,12 +4186,26 @@ ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb) ahc_platform_freeze_devq(ahc, scb); } +static void +ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb, + struct scb *scb) +{ + if (prev_scb == NULL) + ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag); + else + prev_scb->hscb->next = scb->hscb->tag; + ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag; + scb->hscb->next = ahc->next_queued_scb->hscb->tag; +} + int ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, int lun, u_int tag, role_t role, uint32_t status, ahc_search_action action) { struct scb *scb; + struct scb *prev_scb; + uint8_t qinstart; uint8_t qinpos; uint8_t qintail; uint8_t next, prev; @@ -4118,9 +4214,34 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, int maxtarget; int i; - qinpos = ahc_inb(ahc, QINPOS); qintail = ahc->qinfifonext; + if ((ahc->features & AHC_QUEUE_REGS) != 0) { + qinstart = ahc_inb(ahc, SNSCB_QOFF); + ahc_outb(ahc, SNSCB_QOFF, qinstart); + } else + qinstart = ahc_inb(ahc, QINPOS); + qinpos = qinstart; + + /* + * If the next qinfifo SCB does not match the + * entry in our qinfifo, the sequencer is in + * the process of dmaing down the SCB that just + * preceeds qinstart. So, start our search in + * the qinfifo back by an entry. The sequencer + * is smart enough to check after the SCB dma + * completes to ensure that the newly DMAed + * SCB is still relevant. + */ + next = ahc_inb(ahc, NEXT_QUEUED_SCB); + if (qinstart == qintail) { + if (next != ahc->next_queued_scb->hscb->tag) + qinpos--; + } else if (next != ahc->qinfifo[qinstart]) { + qinpos--; + } + found = 0; + prev_scb = NULL; if (action == SEARCH_COMPLETE) { /* @@ -4135,9 +4256,10 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, * for removal will be re-added to the queue as we go. */ ahc->qinfifonext = qinpos; + ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag); while (qinpos != qintail) { - scb = &ahc->scb_data->scbarray[ahc->qinfifo[qinpos]]; + scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]); if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) { /* * We found an scb that needs to be acted on. @@ -4156,17 +4278,33 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, if ((scb->flags & SCB_ACTIVE) == 0) printf("Inactive SCB in qinfifo\n"); ahc_done(ahc, scb); + + /* + * The sequencer increments its position in + * the qinfifo as soon as it determines that + * an SCB needs to be DMA'ed down to the card. + * So, if we are aborting a command that is + * still in the process of being DMAed, we + * must move the sequencer's qinfifo pointer + * back as well. + */ + if (qinpos == (qinstart - 1)) { + ahc_outb(ahc, SNSCB_QOFF, qinpos); + } else { + ahc_outb(ahc, QINPOS, qinpos); + } break; } case SEARCH_COUNT: - ahc->qinfifo[ahc->qinfifonext++] = - scb->hscb->tag; + ahc_qinfifo_requeue(ahc, prev_scb, scb); + prev_scb = scb; break; case SEARCH_REMOVE: break; } } else { - ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag; + ahc_qinfifo_requeue(ahc, prev_scb, scb); + prev_scb = scb; } qinpos++; } @@ -4194,7 +4332,7 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, "SCB index == %d, yet numscbs == %d.", scb_index, ahc->scb_data->numscbs); } - scb = &ahc->scb_data->scbarray[scb_index]; + scb = ahc_lookup_scb(ahc, scb_index); if (ahc_match_scb(ahc, scb, target, channel, lun, SCB_LIST_NULL, role)) { /* @@ -4349,7 +4487,7 @@ ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel, "cur SCBPTR == %x, prev SCBPTR == %x.", next, prev); } - scbp = &ahc->scb_data->scbarray[scb_index]; + scbp = ahc_lookup_scb(ahc, scb_index); if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, ROLE_INITIATOR)) { count++; @@ -4510,7 +4648,7 @@ ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, scbid = ahc_index_busy_tcl(ahc, BUILD_TCL(i << 4, 0), /*unbusy*/FALSE); - scbp = &ahc->scb_data->scbarray[scbid]; + scbp = ahc_lookup_scb(ahc, scbid); if (scbid < ahc->scb_data->numscbs && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) { u_int minlun; @@ -4559,8 +4697,8 @@ ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, ahc_outb(ahc, SCBPTR, i); scbid = ahc_inb(ahc, SCB_TAG); - scbp = &ahc->scb_data->scbarray[scbid]; - if (scbid < ahc->scb_data->numscbs + scbp = ahc_lookup_scb(ahc, scbid); + if (scbp != NULL && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) ahc_add_curscb_to_free_list(ahc); } @@ -4631,6 +4769,9 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) channel, ROLE_UNKNOWN); pause_sequencer(ahc); + /* Make sure the sequencer is in a safe location. */ + ahc_clear_critical_section(ahc); + /* * Run our command complete fifos to ensure that we perform * completion processing on any commands that 'completed' @@ -4977,11 +5118,22 @@ ahc_dumpseq(struct ahc_softc* ahc) static void ahc_loadseq(struct ahc_softc *ahc) { - struct patch *cur_patch; - u_int i; - int downloaded; - u_int skip_addr; - uint8_t download_consts[4]; + struct cs cs_table[num_critical_sections]; + struct patch *cur_patch; + u_int cs_table_size; + u_int cur_cs; + u_int i; + int downloaded; + u_int skip_addr; + u_int sg_prefetch_cnt; + uint8_t download_consts[7]; + + /* + * Start out with 0 critical sections + * that apply to this firmware load. + */ + cs_table_size = 0; + cur_cs = 0; /* Setup downloadable constant table */ download_consts[QOUTFIFO_OFFSET] = 0; @@ -4990,6 +5142,12 @@ ahc_loadseq(struct ahc_softc *ahc) download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1; download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1; download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1); + sg_prefetch_cnt = ahc->pci_cachesize; + if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg))) + sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg); + download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt; + download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1); + download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1); cur_patch = patches; downloaded = 0; @@ -5006,9 +5164,38 @@ ahc_loadseq(struct ahc_softc *ahc) */ continue; } + /* + * Move through the CS table until we find a CS + * that might apply to this instruction. + */ + for (; cur_cs < num_critical_sections; cur_cs++) { + if (critical_sections[cur_cs].end >= i) { + if (critical_sections[cur_cs].begin == i) { + cs_table[cs_table_size].begin = + downloaded; + } + if (critical_sections[cur_cs].end == i) { + cs_table[cs_table_size].end = + downloaded; + cs_table_size++; + } + break; + } + } ahc_download_instr(ahc, i, download_consts); downloaded++; } + + ahc->num_critical_sections = cs_table_size; + if (cs_table_size != 0) { + + cs_table_size *= sizeof(struct cs); + ahc->critical_sections = + malloc(cs_table_size, M_DEVBUF, M_NOWAIT); + if (ahc->critical_sections == NULL) + panic("ahc_loadseq: Could not malloc"); + memcpy(ahc->critical_sections, cs_table, cs_table_size); + } ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE); restart_sequencer(ahc); diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h index b0b07a8..5dd129a 100644 --- a/sys/dev/aic7xxx/aic7xxx.h +++ b/sys/dev/aic7xxx/aic7xxx.h @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/src/aic7xxx/aic7xxx.h#4 $ + * $Id: //depot/src/aic7xxx/aic7xxx.h#6 $ * * $FreeBSD$ */ @@ -507,12 +507,13 @@ struct sg_map_node { }; struct scb_data { - struct hardware_scb *hscbs; /* Array of hardware SCBs */ - struct scb *scbarray; /* Array of kernel SCBs */ SLIST_HEAD(, scb) free_scbs; /* * Pool of SCBs ready to be assigned * commands to execute. */ + struct scb *scbindex[AHC_SCB_MAX + 1];/* Mapping from tag to SCB */ + struct hardware_scb *hscbs; /* Array of hardware SCBs */ + struct scb *scbarray; /* Array of kernel SCBs */ struct scsi_sense_data *sense; /* Per SCB sense data */ /* @@ -797,6 +798,8 @@ struct ahc_softc { #endif struct scb_data *scb_data; + struct scb *next_queued_scb; + /* * SCBs that have been sent to the controller */ @@ -867,6 +870,10 @@ struct ahc_softc { uint8_t *qoutfifo; uint8_t *qinfifo; + /* Critical Section Data */ + struct cs *critical_sections; + u_int num_critical_sections; + /* Links for chaining softcs */ TAILQ_ENTRY(ahc_softc) links; @@ -1031,6 +1038,7 @@ void ahc_handle_brkadrint(struct ahc_softc *ahc); void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat); void ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat); +void ahc_clear_critical_section(struct ahc_softc *ahc); /***************************** Error Recovery *********************************/ typedef enum { diff --git a/sys/dev/aic7xxx/aic7xxx.reg b/sys/dev/aic7xxx/aic7xxx.reg index d1cda86..d83ad22 100644 --- a/sys/dev/aic7xxx/aic7xxx.reg +++ b/sys/dev/aic7xxx/aic7xxx.reg @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/src/aic7xxx/aic7xxx.reg#3 $ + * $Id: //depot/src/aic7xxx/aic7xxx.reg#5 $ * * $FreeBSD$ */ @@ -1217,17 +1217,22 @@ scratch_ram { size 16 } /* - * Bit vector of targets that have ULTRA enabled as set by the BIOS. - * The Sequencer relies in a per-SCB field to control the disconnect - * priveldge. + * Partial transfer past cacheline end to be + * transferred using an extra S/G. */ - ULTRA_ENB { - size 2 + MWI_RESIDUAL { + size 1 /* - * Partial transfer past cacheline end to be - * transferred using an extra S/G. + * Bit vector of targets that have ULTRA enabled as set by * the BIOS. The Sequencer relies on a per-SCB field to + * control whether to enable Ultra transfers or not. */ - alias MWI_RESIDUAL + alias ULTRA_ENB + } + /* + * SCBID of the next SCB to be started by the controller. + */ + NEXT_QUEUED_SCB { + size 1 } /* * Bit vector of targets that have disconnection disabled as set by @@ -1329,7 +1334,7 @@ scratch_ram { } /* * Base address of our shared data with the kernel driver in host - * memory. This includes the qinfifo, qoutfifo, and target mode + * memory. This includes the qoutfifo and target mode * incoming command queue. */ SHARED_DATA_ADDR { @@ -1494,3 +1499,6 @@ const QOUTFIFO_OFFSET download const QINFIFO_OFFSET download const CACHESIZE_MASK download const INVERTED_CACHESIZE_MASK download +const SG_PREFETCH_CNT download +const SG_PREFETCH_ALIGN_MASK download +const SG_PREFETCH_ADDR_MASK download diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq index a1a2e98..ec9862e 100644 --- a/sys/dev/aic7xxx/aic7xxx.seq +++ b/sys/dev/aic7xxx/aic7xxx.seq @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/src/aic7xxx/aic7xxx.seq#4 $ + * $Id: //depot/src/aic7xxx/aic7xxx.seq#7 $ * * $FreeBSD$ */ @@ -77,45 +77,47 @@ poll_for_work_loop: cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; test_queue: /* Has the driver posted any work for us? */ +BEGIN_CRITICAL if ((ahc->features & AHC_QUEUE_REGS) != 0) { test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; mov NONE, SNSCB_QOFF; - inc QINPOS; } else { cmp KERNEL_QINPOS, A je poll_for_work_loop; inc QINPOS; } + mov ARG_1, NEXT_QUEUED_SCB; +END_CRITICAL /* * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. Pull the tag - * from the QINFIFO and get to work on it. + * SCBs in the list of SCBs awaiting selection. Allocate a + * card SCB for the host's SCB and get to work on it. */ if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ALLZEROS call get_free_or_disc_scb; - } - -dequeue_scb: - add A, -1, QINPOS; - mvi QINFIFO_OFFSET call fetch_byte; - - if ((ahc->flags & AHC_PAGESCBS) == 0) { + } else { /* In the non-paging case, the SCBID == hardware SCB index */ - mov SCBPTR, RETURN_2; + mov SCBPTR, ARG_1; } dma_queued_scb: /* * DMA the SCB from host ram into the current SCB location. */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; - mov RETURN_2 call dma_scb; -start_scb: + mov ARG_1 call dma_scb; /* - * Place us on the waiting list in case our selection - * doesn't win during bus arbitration. + * Check one last time to see if this SCB was canceled + * before we completed the DMA operation. If it was, + * the QINFIFO next pointer will not match our saved + * value. */ + mov A, ARG_1; +BEGIN_CRITICAL + cmp NEXT_QUEUED_SCB, A jne abort_qinscb; + mov NEXT_QUEUED_SCB, SCB_NEXT; mov SCB_NEXT,WAITING_SCBH; mov WAITING_SCBH, SCBPTR; +END_CRITICAL start_waiting: /* * Start the first entry on the waiting SCB list. @@ -124,6 +126,11 @@ start_waiting: call start_selection; jmp poll_for_work_loop; +abort_qinscb: + mvi INTSTAT, TRACEPOINT; + call add_scb_to_free_list; + jmp poll_for_work_loop; + start_selection: if ((ahc->features & AHC_TWIN) != 0) { and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ @@ -650,7 +657,7 @@ idle_loop: /* * Do we have any prefetch left??? */ - cmp CCSGADDR, CCSGADDR_MAX jne idle_sg_avail; + cmp CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail; /* * Need to fetch segments, but we can only do that @@ -660,19 +667,20 @@ idle_loop: test CCSCBCTL, CCSCBEN jnz return; /* - * The kernel allocates S/G space so that it is 128 byte - * aligned and ends on a 128 byte boundary. We fetch - * up to the next 128 byte boundary so we don't attempt - * to read a non-existent page. + * We fetch a "cacheline aligned" and sized amount of data + * so we don't end up referencing a non-existant page. + * Cacheline aligned is in quotes because the kernel will + * set the prefetch amount to a reasonable level if the + * cacheline size is unknown. */ - mvi CCHCNT, CCSGADDR_MAX; - and CCHADDR[0], ~(CCSGADDR_MAX - 1), SCB_RESIDUAL_SGPTR; + mvi CCHCNT, SG_PREFETCH_CNT; + and CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR; bmov CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3; mvi CCSGCTL, CCSGEN|CCSGRESET ret; idle_sgfetch_complete: clr CCSGCTL; test CCSGCTL, CCSGEN jnz .; - and CCSGADDR, (CCSGADDR_MAX - 1), SCB_RESIDUAL_SGPTR; + and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR; idle_sg_avail: if ((ahc->features & AHC_ULTRA2) != 0) { /* Does the hardware have space for another SG entry? */ @@ -767,7 +775,7 @@ p_data: */ if ((ahc->features & AHC_CMD_CHAN) != 0) { /* We don't have any valid S/G elements */ - mvi CCSGADDR, CCSGADDR_MAX; + mvi CCSGADDR, SG_PREFETCH_CNT; } test SEQ_FLAGS, DPHASE jnz data_phase_reinit; @@ -1768,29 +1776,6 @@ rHead: mov DISCONNECTED_SCBH,SCB_NEXT ret; /* - * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) - * and a base address of SHARED_DATA_ADDR. The byte is returned in RETURN_2. - */ -fetch_byte: - mov ARG_2, SINDEX; - if ((ahc->features & AHC_CMD_CHAN) != 0) { - mvi DINDEX, CCHADDR; - mvi SHARED_DATA_ADDR call set_1byte_addr; - mvi CCHCNT, 1; - mvi CCSGCTL, CCSGEN|CCSGRESET; - test CCSGCTL, CCSGDONE jz .; - mvi CCSGCTL, CCSGRESET; - bmov RETURN_2, CCSGRAM, 1 ret; - } else { - mvi DINDEX, HADDR; - mvi SHARED_DATA_ADDR call set_1byte_addr; - mvi 1 call set_hcnt; - mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - call dma_finish; - mov RETURN_2, DFDAT ret; - } - -/* * Prepare the hardware to post a byte to host memory given an * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR. */ @@ -1820,12 +1805,6 @@ post_byte: jmp dma_finish; } -get_SCBID_from_host: - mov A, SAVED_LUN; - shr SINDEX, 4, SAVED_SCSIID; - call fetch_byte; - mov RETURN_1, RETURN_2 ret; - phase_lock_perr: mvi INTSTAT, PERR_DETECTED; phase_lock: @@ -2057,9 +2036,11 @@ dma_finish: add_scb_to_free_list: if ((ahc->flags & AHC_PAGESCBS) != 0) { +BEGIN_CRITICAL mov SCB_NEXT, FREE_SCBH; mvi SCB_TAG, SCB_LIST_NULL; mov FREE_SCBH, SCBPTR ret; +END_CRITICAL } else { mvi SCB_TAG, SCB_LIST_NULL ret; } diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.c b/sys/dev/aic7xxx/aic7xxx_freebsd.c index 8654e56..0cafccc 100644 --- a/sys/dev/aic7xxx/aic7xxx_freebsd.c +++ b/sys/dev/aic7xxx/aic7xxx_freebsd.c @@ -138,7 +138,7 @@ ahc_attach(struct ahc_softc *ahc) /* * Create the device queue for our SIM(s). */ - devq = cam_simq_alloc(AHC_SCB_MAX); + devq = cam_simq_alloc(AHC_SCB_MAX - 1); if (devq == NULL) goto fail; @@ -147,7 +147,7 @@ ahc_attach(struct ahc_softc *ahc) */ sim = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, device_get_unit(ahc->dev_softc), - 1, AHC_SCB_MAX, devq); + 1, AHC_SCB_MAX - 1, devq); if (sim == NULL) { cam_simq_free(devq); goto fail; @@ -179,7 +179,7 @@ ahc_attach(struct ahc_softc *ahc) if (ahc->features & AHC_TWIN) { sim2 = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, device_get_unit(ahc->dev_softc), 1, - AHC_SCB_MAX, devq); + AHC_SCB_MAX - 1, devq); if (sim2 == NULL) { printf("ahc_attach: Unable to attach second " @@ -1073,7 +1073,9 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, ahc_set_transaction_status(scb, CAM_REQ_CMP_ERR); if (nsegments != 0) bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); + ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); + ahc_unlock(ahc, &s); xpt_done(ccb); return; } @@ -1140,7 +1142,9 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, CAM_REQ_TOO_BIG); bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); + ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); + ahc_unlock(ahc, &s); xpt_done(ccb); return; } @@ -1174,8 +1178,8 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); ahc_free_scb(ahc, scb); - xpt_done(ccb); ahc_unlock(ahc, &s); + xpt_done(ccb); return; } @@ -1246,10 +1250,14 @@ ahc_setup_data(struct ahc_softc *ahc, struct cam_sim *sim, if (hscb->cdb_len > sizeof(hscb->cdb32) || (ccb_h->flags & CAM_CDB_PHYS) != 0) { + u_long s; + ahc_set_transaction_status(scb, CAM_REQ_INVALID); - xpt_done(scb->io_ctx); + ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); + ahc_unlock(ahc, &s); + xpt_done((union ccb *)csio); return; } if (hscb->cdb_len > 12) { diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.h b/sys/dev/aic7xxx/aic7xxx_freebsd.h index 4acb128..1a2d65e 100644 --- a/sys/dev/aic7xxx/aic7xxx_freebsd.h +++ b/sys/dev/aic7xxx/aic7xxx_freebsd.h @@ -371,6 +371,19 @@ ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, return (0); } +static __inline void +ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb) +{ + /* What do we do to generically handle driver resource shortages??? */ + if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0 + && scb->io_ctx != NULL + && (scb->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { + scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; + ahc->flags &= ~AHC_RESOURCE_SHORTAGE; + } + scb->io_ctx = NULL; +} + /********************************** PCI ***************************************/ #ifdef AHC_SUPPORT_PCI static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci, diff --git a/sys/dev/aic7xxx/aic7xxx_inline.h b/sys/dev/aic7xxx/aic7xxx_inline.h index 7bf63de..8f6d2ba 100644 --- a/sys/dev/aic7xxx/aic7xxx_inline.h +++ b/sys/dev/aic7xxx/aic7xxx_inline.h @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/src/aic7xxx/aic7xxx_inline.h#3 $ + * $Id: //depot/src/aic7xxx/aic7xxx_inline.h#5 $ * * $FreeBSD$ */ @@ -242,18 +242,16 @@ ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id, static __inline struct scb * ahc_get_scb(struct ahc_softc *ahc) { - struct scb *scbp; + struct scb *scb; - if ((scbp = SLIST_FIRST(&ahc->scb_data->free_scbs))) { - SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle); - } else { + if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) { ahc_alloc_scbs(ahc); - scbp = SLIST_FIRST(&ahc->scb_data->free_scbs); - if (scbp != NULL) - SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle); + scb = SLIST_FIRST(&ahc->scb_data->free_scbs); + if (scb == NULL) + return (NULL); } - - return (scbp); + SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle); + return (scb); } /* @@ -265,19 +263,22 @@ ahc_free_scb(struct ahc_softc *ahc, struct scb *scb) struct hardware_scb *hscb; hscb = scb->hscb; -#if 0 - /* What do we do to generically handle driver resource shortages??? */ - if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0 - && (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { - scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; - ahc->flags &= ~AHC_RESOURCE_SHORTAGE; - } -#endif /* Clean up for the next user */ + ahc->scb_data->scbindex[hscb->tag] = NULL; scb->flags = SCB_FREE; hscb->control = 0; SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle); + + /* Notify the OSM that a resource is now available. */ + ahc_platform_scb_free(ahc, scb); +} + +static __inline struct scb * +ahc_lookup_scb(struct ahc_softc *ahc, u_int tag) +{ + return (ahc->scb_data->scbindex[tag]); + } /* @@ -286,6 +287,41 @@ ahc_free_scb(struct ahc_softc *ahc, struct scb *scb) static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb) { + struct hardware_scb *q_hscb; + u_int saved_tag; + + /* + * Our queuing method is a bit tricky. The card + * knows in advance which HSCB to download, and we + * can't disappoint it. To achieve this, the next + * SCB to download is saved off in ahc->next_queued_scb. + * When we are called to queue "an arbitrary scb", + * we copy the contents of the incoming HSCB to the one + * the sequencer knows about, swap HSCB pointers and + * finally assigne the SCB to the tag indexed location + * in the scb_array. This makes sure that we can still + * locate the correct SCB by SCB_TAG. + * + * Start by copying the payload without perterbing + * the tag number. Also set the hscb id for the next + * SCB to download. + */ + q_hscb = ahc->next_queued_scb->hscb; + saved_tag = q_hscb->tag; + memcpy(q_hscb, scb->hscb, 32); + q_hscb->tag = saved_tag; + q_hscb->next = scb->hscb->tag; + + /* Now swap HSCB pointers. */ + ahc->next_queued_scb->hscb = scb->hscb; + scb->hscb = q_hscb; + + /* Now define the mapping from tag to SCB in the scbindex */ + ahc->scb_data->scbindex[scb->hscb->tag] = scb; + + /* + * Keep a history of SCBs we've downloaded in the qinfifo. + */ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag; if ((ahc->features & AHC_QUEUE_REGS) != 0) { ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext); diff --git a/sys/dev/aic7xxx/aic7xxx_osm.c b/sys/dev/aic7xxx/aic7xxx_osm.c index 8654e56..0cafccc 100644 --- a/sys/dev/aic7xxx/aic7xxx_osm.c +++ b/sys/dev/aic7xxx/aic7xxx_osm.c @@ -138,7 +138,7 @@ ahc_attach(struct ahc_softc *ahc) /* * Create the device queue for our SIM(s). */ - devq = cam_simq_alloc(AHC_SCB_MAX); + devq = cam_simq_alloc(AHC_SCB_MAX - 1); if (devq == NULL) goto fail; @@ -147,7 +147,7 @@ ahc_attach(struct ahc_softc *ahc) */ sim = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, device_get_unit(ahc->dev_softc), - 1, AHC_SCB_MAX, devq); + 1, AHC_SCB_MAX - 1, devq); if (sim == NULL) { cam_simq_free(devq); goto fail; @@ -179,7 +179,7 @@ ahc_attach(struct ahc_softc *ahc) if (ahc->features & AHC_TWIN) { sim2 = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, device_get_unit(ahc->dev_softc), 1, - AHC_SCB_MAX, devq); + AHC_SCB_MAX - 1, devq); if (sim2 == NULL) { printf("ahc_attach: Unable to attach second " @@ -1073,7 +1073,9 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, ahc_set_transaction_status(scb, CAM_REQ_CMP_ERR); if (nsegments != 0) bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); + ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); + ahc_unlock(ahc, &s); xpt_done(ccb); return; } @@ -1140,7 +1142,9 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, CAM_REQ_TOO_BIG); bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); + ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); + ahc_unlock(ahc, &s); xpt_done(ccb); return; } @@ -1174,8 +1178,8 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); ahc_free_scb(ahc, scb); - xpt_done(ccb); ahc_unlock(ahc, &s); + xpt_done(ccb); return; } @@ -1246,10 +1250,14 @@ ahc_setup_data(struct ahc_softc *ahc, struct cam_sim *sim, if (hscb->cdb_len > sizeof(hscb->cdb32) || (ccb_h->flags & CAM_CDB_PHYS) != 0) { + u_long s; + ahc_set_transaction_status(scb, CAM_REQ_INVALID); - xpt_done(scb->io_ctx); + ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); + ahc_unlock(ahc, &s); + xpt_done((union ccb *)csio); return; } if (hscb->cdb_len > 12) { diff --git a/sys/dev/aic7xxx/aic7xxx_osm.h b/sys/dev/aic7xxx/aic7xxx_osm.h index 4acb128..1a2d65e 100644 --- a/sys/dev/aic7xxx/aic7xxx_osm.h +++ b/sys/dev/aic7xxx/aic7xxx_osm.h @@ -371,6 +371,19 @@ ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, return (0); } +static __inline void +ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb) +{ + /* What do we do to generically handle driver resource shortages??? */ + if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0 + && scb->io_ctx != NULL + && (scb->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { + scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; + ahc->flags &= ~AHC_RESOURCE_SHORTAGE; + } + scb->io_ctx = NULL; +} + /********************************** PCI ***************************************/ #ifdef AHC_SUPPORT_PCI static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci, diff --git a/sys/dev/aic7xxx/aic7xxx_pci.c b/sys/dev/aic7xxx/aic7xxx_pci.c index 00a5213..91c1889 100644 --- a/sys/dev/aic7xxx/aic7xxx_pci.c +++ b/sys/dev/aic7xxx/aic7xxx_pci.c @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/src/aic7xxx/aic7xxx_pci.c#4 $ + * $Id: //depot/src/aic7xxx/aic7xxx_pci.c#5 $ * * $FreeBSD$ */ @@ -662,7 +662,7 @@ ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry) if (error != 0) return (error); dscommand0 = ahc_inb(ahc, DSCOMMAND0); - dscommand0 |= MPARCKEN; + dscommand0 |= MPARCKEN|CACHETHEN; if ((ahc->features & AHC_ULTRA2) != 0) { /* @@ -670,7 +670,6 @@ ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry) * some MBs so don't use it. */ dscommand0 &= ~DPARCKEN; - dscommand0 |= CACHETHEN; } /* @@ -1588,10 +1587,10 @@ ahc_pci_intr(struct ahc_softc *ahc) "or write data phase\n", ahc_name(ahc)); } if (status1 & SSE) { - printf("%s: Signaled System Error Detected\n", ahc_name(ahc)); + printf("%s: Signal System Error Detected\n", ahc_name(ahc)); } if (status1 & RMA) { - printf("%s: Signaled a Master Abort\n", ahc_name(ahc)); + printf("%s: Received a Master Abort\n", ahc_name(ahc)); } if (status1 & RTA) { printf("%s: Received a Target Abort\n", ahc_name(ahc)); |