diff options
| -rw-r--r-- | sys/i386/scsi/aic7xxx.c | 127 |
1 files changed, 77 insertions, 50 deletions
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c index 3b2cf43097c3..0f974523bbae 100644 --- a/sys/i386/scsi/aic7xxx.c +++ b/sys/i386/scsi/aic7xxx.c @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.c,v 1.102 1997/02/22 09:38:40 peter Exp $ + * $Id: aic7xxx.c,v 1.103 1997/02/25 03:05:31 gibbs Exp $ */ /* * TODO: @@ -289,7 +289,9 @@ static int ahc_reset_device __P((struct ahc_softc *ahc, int target, char channel, int lun, u_int8_t tag, u_int32_t xs_error)); static u_int8_t ahc_rem_scb_from_disc_list __P((struct ahc_softc *ahc, - u_int8_t scbptr)); + u_int8_t scbptr)); +static void ahc_add_curscb_to_free_list __P((struct ahc_softc *ahc)); +static void ahc_clear_intstat __P((struct ahc_softc *ahc)); static void ahc_reset_current_bus __P((struct ahc_softc *ahc)); static void ahc_run_done_queue __P((struct ahc_softc *ahc)); static void ahc_scsirate __P((struct ahc_softc* ahc, u_int8_t *scsirate, @@ -472,8 +474,8 @@ ahc_construct(ahc, bc, ioh, maddr, type, flags) ahc->busreset_args.ahc = ahc; ahc->busreset_args.bus = 'A'; - ahc->busreset_args.ahc = ahc; - ahc->busreset_args.bus = 'B'; + ahc->busreset_args_b.ahc = ahc; + ahc->busreset_args_b.bus = 'B'; #if defined(__FreeBSD__) return (ahc); @@ -940,18 +942,25 @@ ahc_handle_seqint(ahc, intstat) break; } case NO_IDENT: + { /* - * XXX - We probably need to do something very carefully to - * make sure we don't lock up the bus or transfer data to a - * bogus area of memory. Perhaps a bus reset is the best - * alternative??? + * The reconnecting target either did not send an identify + * message, or did, but we didn't find and SCB to match and + * before it could respond to our ATN/abort, it hit a dataphase. + * The only safe thing to do is to blow it away with a bus + * reset. */ - panic("%s:%c:%d: Target did not send an IDENTIFY message. " - "LASTPHASE = 0x%x, SAVED_TCL == 0x%x\n", - ahc_name(ahc), channel, target, - ahc_inb(ahc, LASTPHASE), - ahc_inb(ahc, SAVED_TCL)); - break; + int found; + + printf("%s:%c:%d: Target did not send an IDENTIFY message. " + "LASTPHASE = 0x%x, SAVED_TCL == 0x%x\n", + ahc_name(ahc), channel, target, ahc_inb(ahc, LASTPHASE), + ahc_inb(ahc, SAVED_TCL)); + found = ahc_reset_channel(ahc, channel, XS_TIMEOUT, + /*initiate reset*/TRUE); + printf("%s: Issued Channel %c Bus Reset. " + "%d SCBs aborted\n", ahc_name(ahc), channel, found); + } case BAD_PHASE: printf("%s:%c:%d: unknown scsi bus phase. Attempting to " "continue\n", ahc_name(ahc), channel, target); @@ -1340,6 +1349,15 @@ ahc_handle_seqint(ahc, intstat) ahc_outb(ahc, MSG_LEN, 1); sc_print_addr(scb->xs->sc_link); printf("Bus Device Reset Message Sent\n"); + } else if (scb->flags & SCB_ABORT) { + if ((scb->hscb->control & TAG_ENB) != 0) + ahc_outb(ahc, MSG0 + message_offset, + MSG_ABORT_TAG); + else + ahc_outb(ahc, MSG0 + message_offset, MSG_ABORT); + ahc_outb(ahc, MSG_LEN, message_offset + 1); + sc_print_addr(scb->xs->sc_link); + printf("Abort Message Sent\n"); } else if (scb->flags & SCB_MSGOUT_WDTR) { ahc_construct_wdtr(ahc, message_offset, BUS_16_BIT); } else if (scb->flags & SCB_MSGOUT_SDTR) { @@ -1368,15 +1386,6 @@ ahc_handle_seqint(ahc, intstat) ahc_syncrates[i].period, (target_scratch & WIDEXFER) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT); - } else if (scb->flags & SCB_ABORT) { - if ((scb->hscb->control & TAG_ENB) != 0) - ahc_outb(ahc, MSG0 + message_offset, - MSG_ABORT_TAG); - else - ahc_outb(ahc, MSG0 + message_offset, MSG_ABORT); - ahc_outb(ahc, MSG_LEN, message_offset + 1); - sc_print_addr(scb->xs->sc_link); - printf("Abort Message Sent\n"); } else panic("ahc_intr: AWAITING_MSG for an SCB that " "does not have a waiting message"); @@ -1554,11 +1563,7 @@ ahc_handle_scsiint(ahc, intstat) if ((scb_control & DISCONNECTED) != 0) ahc_rem_scb_from_disc_list(ahc, scbptr); else { - /* Put us on the free list */ - ahc_outb(ahc, SCB_NEXT, - ahc_inb(ahc, FREE_SCBH)); - ahc_outb(ahc, FREE_SCBH, - ahc_inb(ahc, SCBPTR)); + ahc_add_curscb_to_free_list(ahc); } /* Did we ask for this?? */ @@ -1734,10 +1739,8 @@ ahc_handle_scsiint(ahc, intstat) 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); + ahc_add_curscb_to_free_list(ahc); + restart_sequencer(ahc); } else { sc_print_addr(scb->xs->sc_link); @@ -1974,6 +1977,9 @@ ahc_init(ahc) /* Set the next pointer */ ahc_outb(ahc, SCB_NEXT, i+1); + /* Make the tag number invalid */ + ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL); + /* No Busy non-tagged targets yet */ ahc_outb(ahc, SCB_ACTIVE0, SCB_LIST_NULL); ahc_outb(ahc, SCB_ACTIVE1, SCB_LIST_NULL); @@ -2975,9 +2981,14 @@ bus_reset: ahc_outb(ahc, SCBPTR, saved_scbptr); /* ahc_run_waiting_queue will unpause us */ ahc_run_waiting_queue(ahc); - } else + } else { /* Go "immediatly" to the bus reset */ + sc_print_addr(scb->xs->sc_link); + printf("SCB %d: Immediate reset. " + "Flags = 0x%x\n", scb->hscb->tag, + scb->flags); goto bus_reset; + } } } splx(s); @@ -3257,9 +3268,7 @@ ahc_rem_scb_from_disc_list(ahc, scbptr) ahc_outb(ahc, SCB_CONTROL, 0); - /* Add this SCB to the free list */ - ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); - ahc_outb(ahc, FREE_SCBH, scbptr); + ahc_add_curscb_to_free_list(ahc); if (prev != SCB_LIST_NULL) { ahc_outb(ahc, SCBPTR, prev); @@ -3274,6 +3283,17 @@ ahc_rem_scb_from_disc_list(ahc, scbptr) return next; } +static void +ahc_add_curscb_to_free_list(ahc) + struct ahc_softc *ahc; +{ + /* Invalidate the tag so that ahc_find_scb doesn't think it's active */ + ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL); + + ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); + ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR)); +} + /* * Manipulate the waiting for selection list and return the * scb that follows the one that we remove. @@ -3301,9 +3321,7 @@ ahc_abort_wscb (ahc, scbp, scbpos, prev, xs_error) ahc_outb(ahc, SCB_CONTROL, 0); ahc_index_busy_target(ahc, target, channel, /*unbusy*/TRUE); - /* Add this SCB to the free list */ - ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); - ahc_outb(ahc, FREE_SCBH, scbpos); + ahc_add_curscb_to_free_list(ahc); /* update the waiting list */ if (prev == SCB_LIST_NULL) @@ -3356,6 +3374,18 @@ ahc_index_busy_target(ahc, target, channel, unbusy) } static void +ahc_clear_intstat(ahc) + struct ahc_softc *ahc; +{ + /* Clear any interrupt conditions this may have caused */ + ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO); + ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI + |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG| + CLRREQINIT); + ahc_outb(ahc, CLRINT, CLRSCSIINT); +} + +static void ahc_reset_current_bus(ahc) struct ahc_softc *ahc; { @@ -3380,9 +3410,7 @@ ahc_reset_current_bus(ahc) /* Turn off the bus reset */ ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO); - /* Clear any interrupt conditions this may have caused */ - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); /* Re-enable reset interrupts */ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST); @@ -3422,9 +3450,7 @@ ahc_busreset_complete(arg) scsiseq = ahc_inb(ahc, SCSISEQ); ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO); - /* Clear any interrupt conditions this may have caused */ - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); /* Re-enable reset interrupts */ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST); @@ -3484,11 +3510,13 @@ ahc_reset_channel(ahc, channel, xs_error, initiate_reset) if (channel == 'B') { ahc->needsdtr |= (ahc->needsdtr_orig & 0xff00); ahc->sdtrpending &= 0x00ff; + ahc->orderedtag &= 0x00ff; offset = TARG_SCRATCH + 8; offset_max = TARG_SCRATCH + 16; } else if (ahc->type & AHC_WIDE){ ahc->needsdtr = ahc->needsdtr_orig; ahc->needwdtr = ahc->needwdtr_orig; + ahc->orderedtag = 0; ahc->sdtrpending = 0; ahc->wdtrpending = 0; offset = TARG_SCRATCH; @@ -3496,6 +3524,7 @@ ahc_reset_channel(ahc, channel, xs_error, initiate_reset) } else { ahc->needsdtr |= (ahc->needsdtr_orig & 0x00ff); ahc->sdtrpending &= 0xff00; + ahc->orderedtag &= 0xff00; offset = TARG_SCRATCH; offset_max = TARG_SCRATCH + 8; } @@ -3527,17 +3556,15 @@ ahc_reset_channel(ahc, channel, xs_error, initiate_reset) ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); if (initiate_reset) ahc_reset_current_bus(ahc); - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); ahc_outb(ahc, SBLKCTL, sblkctl); - unpause_sequencer(ahc, /*unpause_always*/TRUE); + unpause_sequencer(ahc, /*unpause_always*/FALSE); } else { /* Case 2: A command from this bus is active or we're idle */ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); if (initiate_reset) ahc_reset_current_bus(ahc); - ahc_outb(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO|CLRBUSFREE); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc_clear_intstat(ahc); restart_sequencer(ahc); } ahc_run_done_queue(ahc); |
