diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-31 13:12:41 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-31 13:12:41 -0800 |
commit | e0ae23550ffd3bfe319fa66879779be1a9a4a155 (patch) | |
tree | c9b7cce2baa430b8625dc01928260c7a6754f29b /drivers/scsi | |
parent | dd1c1853e2742f4938b271dbe0cee735e2ffa3d9 (diff) | |
parent | a5b3c86e4bfb5689d68932105d3fdd1477c9c281 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6
Diffstat (limited to 'drivers/scsi')
36 files changed, 1432 insertions, 1179 deletions
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx index 69ed77fcb71..7955ebe8e1e 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic79xx +++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx @@ -37,13 +37,13 @@ config AIC79XX_CMDS_PER_DEVICE config AIC79XX_RESET_DELAY_MS int "Initial bus reset delay in milli-seconds" depends on SCSI_AIC79XX - default "15000" + default "5000" ---help--- The number of milliseconds to delay after an initial bus reset. The bus settle delay following all error recovery actions is dictated by the SCSI layer and is not affected by this value. - Default: 15000 (15 seconds) + Default: 5000 (5 seconds) config AIC79XX_BUILD_FIRMWARE bool "Build Adapter Firmware with Kernel Build" diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h index 2cfdbef447d..1d11f7e7756 100644 --- a/drivers/scsi/aic7xxx/aic79xx.h +++ b/drivers/scsi/aic7xxx/aic79xx.h @@ -37,7 +37,7 @@ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * - * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#108 $ + * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#109 $ * * $FreeBSD$ */ @@ -222,6 +222,7 @@ typedef enum { typedef enum { AHD_FENONE = 0x00000, AHD_WIDE = 0x00001,/* Wide Channel */ + AHD_AIC79XXB_SLOWCRC = 0x00002,/* SLOWCRC bit should be set */ AHD_MULTI_FUNC = 0x00100,/* Multi-Function/Channel Device */ AHD_TARGETMODE = 0x01000,/* Has tested target mode support */ AHD_MULTIROLE = 0x02000,/* Space for two roles at a time */ diff --git a/drivers/scsi/aic7xxx/aic79xx.reg b/drivers/scsi/aic7xxx/aic79xx.reg index 3a3204703b1..be14e2ecb8f 100644 --- a/drivers/scsi/aic7xxx/aic79xx.reg +++ b/drivers/scsi/aic7xxx/aic79xx.reg @@ -1,7 +1,7 @@ /* * Aic79xx register and scratch ram definitions. * - * Copyright (c) 1994-2001 Justin T. Gibbs. + * Copyright (c) 1994-2001, 2004 Justin T. Gibbs. * Copyright (c) 2000-2002 Adaptec Inc. * All rights reserved. * @@ -39,7 +39,7 @@ * * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#76 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#77 $" /* * This file is processed by the aic7xxx_asm utility for use in assembling @@ -3715,8 +3715,9 @@ scratch_ram { SEQ_FLAGS2 { size 1 - field TARGET_MSG_PENDING 0x02 - field SELECTOUT_QFROZEN 0x04 + field PENDING_MK_MESSAGE 0x01 + field TARGET_MSG_PENDING 0x02 + field SELECTOUT_QFROZEN 0x04 } ALLOCFIFO_SCBPTR { @@ -3777,6 +3778,26 @@ scratch_ram { CMDSIZE_TABLE { size 8 } + /* + * When an SCB with the MK_MESSAGE flag is + * queued to the controller, it cannot enter + * the waiting for selection list until the + * selections for any previously queued + * commands to that target complete. During + * the wait, the MK_MESSAGE SCB is queued + * here. + */ + MK_MESSAGE_SCB { + size 2 + } + /* + * Saved SCSIID of MK_MESSAGE_SCB to avoid + * an extra SCBPTR operation when deciding + * if the MK_MESSAGE_SCB can be run. + */ + MK_MESSAGE_SCSIID { + size 1 + } } /************************* Hardware SCB Definition ****************************/ diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq index bef1f9d369b..58bc17591b5 100644 --- a/drivers/scsi/aic7xxx/aic79xx.seq +++ b/drivers/scsi/aic7xxx/aic79xx.seq @@ -1,7 +1,7 @@ /* * Adaptec U320 device driver firmware for Linux and FreeBSD. * - * Copyright (c) 1994-2001 Justin T. Gibbs. + * Copyright (c) 1994-2001, 2004 Justin T. Gibbs. * Copyright (c) 2000-2002 Adaptec Inc. * All rights reserved. * @@ -40,7 +40,7 @@ * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#119 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#120 $" PATCH_ARG_LIST = "struct ahd_softc *ahd" PREFIX = "ahd_" @@ -110,10 +110,8 @@ check_waiting_list: * one last time. */ test SSTAT0, SELDO jnz select_out; -END_CRITICAL; call start_selection; idle_loop_checkbus: -BEGIN_CRITICAL; test SSTAT0, SELDO jnz select_out; END_CRITICAL; test SSTAT0, SELDI jnz select_in; @@ -294,7 +292,6 @@ fetch_new_scb_inprog: test CCSCBCTL, ARRDONE jz return; fetch_new_scb_done: and CCSCBCTL, ~(CCARREN|CCSCBEN); - bmov REG0, SCBPTR, 2; clr A; add CMDS_PENDING, 1; adc CMDS_PENDING[1], A; @@ -316,43 +313,117 @@ fetch_new_scb_done: clr SCB_FIFO_USE_COUNT; /* Update the next SCB address to download. */ bmov NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4; + /* + * NULL out the SCB links since these fields + * occupy the same location as SCB_NEXT_SCB_BUSADDR. + */ mvi SCB_NEXT[1], SCB_LIST_NULL; mvi SCB_NEXT2[1], SCB_LIST_NULL; /* Increment our position in the QINFIFO. */ mov NONE, SNSCB_QOFF; + /* - * SCBs that want to send messages are always - * queued independently. This ensures that they - * are at the head of the SCB list to select out - * to a target and we will see the MK_MESSAGE flag. + * Save SCBID of this SCB in REG0 since + * SCBPTR will be clobbered during target + * list updates. We also record the SCB's + * flags so that we can refer to them even + * after SCBPTR has been changed. + */ + bmov REG0, SCBPTR, 2; + mov A, SCB_CONTROL; + + /* + * Find the tail SCB of the execution queue + * for this target. */ - test SCB_CONTROL, MK_MESSAGE jnz first_new_target_scb; shr SINDEX, 3, SCB_SCSIID; and SINDEX, ~0x1; mvi SINDEX[1], (WAITING_SCB_TAILS >> 8); bmov DINDEX, SINDEX, 2; bmov SCBPTR, SINDIR, 2; + + /* + * Update the tail to point to the new SCB. + */ bmov DINDIR, REG0, 2; + + /* + * If the queue was empty, queue this SCB as + * the first for this target. + */ cmp SCBPTR[1], SCB_LIST_NULL je first_new_target_scb; + + /* + * SCBs that want to send messages must always be + * at the head of their per-target queue so that + * ATN can be asserted even if the current + * negotiation agreement is packetized. If the + * target queue is empty, the SCB can be queued + * immediately. If the queue is not empty, we must + * wait for it to empty before entering this SCB + * into the waiting for selection queue. Otherwise + * our batching and round-robin selection scheme + * could allow commands to be queued out of order. + * To simplify the implementation, we stop pulling + * new commands from the host until the MK_MESSAGE + * SCB can be queued to the waiting for selection + * list. + */ + test A, MK_MESSAGE jz batch_scb; + + /* + * If the last SCB is also a MK_MESSAGE SCB, then + * order is preserved even if we batch. + */ + test SCB_CONTROL, MK_MESSAGE jz batch_scb; + + /* + * Defer this SCB and stop fetching new SCBs until + * it can be queued. Since the SCB_SCSIID of the + * tail SCB must be the same as that of the newly + * queued SCB, there is no need to restore the SCBID + * here. + */ + or SEQ_FLAGS2, PENDING_MK_MESSAGE; + bmov MK_MESSAGE_SCB, REG0, 2; + mov MK_MESSAGE_SCSIID, SCB_SCSIID ret; + +batch_scb: + /* + * Otherwise just update the previous tail SCB to + * point to the new tail. + */ bmov SCB_NEXT, REG0, 2 ret; + first_new_target_scb: + /* + * Append SCB to the tail of the waiting for + * selection list. + */ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je first_new_scb; bmov SCBPTR, WAITING_TID_TAIL, 2; bmov SCB_NEXT2, REG0, 2; bmov WAITING_TID_TAIL, REG0, 2 ret; first_new_scb: + /* + * Whole list is empty, so the head of + * the list must be initialized too. + */ bmov WAITING_TID_HEAD, REG0, 2; bmov WAITING_TID_TAIL, REG0, 2 ret; END_CRITICAL; scbdma_idle: /* - * Give precedence to downloading new SCBs to execute - * unless select-outs are currently frozen. + * Don't bother downloading new SCBs to execute + * if select-outs are currently frozen or we have + * a MK_MESSAGE SCB waiting to enter the queue. */ - test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz . + 2; + test SEQ_FLAGS2, SELECTOUT_QFROZEN|PENDING_MK_MESSAGE + jnz scbdma_no_new_scbs; BEGIN_CRITICAL; test QOFF_CTLSTA, NEW_SCB_AVAIL jnz fetch_new_scb; +scbdma_no_new_scbs: cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne dma_complete_scb; cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL je return; /* FALLTHROUGH */ @@ -671,27 +742,41 @@ curscb_ww_done: } /* - * Requeue any SCBs not sent, to the tail of the waiting Q. + * The whole list made it. Clear our tail pointer to indicate + * that the per-target selection queue is now empty. */ - cmp SCB_NEXT[1], SCB_LIST_NULL je select_out_list_done; + cmp SCB_NEXT[1], SCB_LIST_NULL je select_out_clear_tail; /* + * Requeue any SCBs not sent, to the tail of the waiting Q. * We know that neither the per-TID list nor the list of - * TIDs is empty. Use this knowledge to our advantage. + * TIDs is empty. Use this knowledge to our advantage and + * queue the remainder to the tail of the global execution + * queue. */ bmov REG0, SCB_NEXT, 2; +select_out_queue_remainder: bmov SCBPTR, WAITING_TID_TAIL, 2; bmov SCB_NEXT2, REG0, 2; bmov WAITING_TID_TAIL, REG0, 2; jmp select_out_inc_tid_q; -select_out_list_done: +select_out_clear_tail: + /* + * Queue any pending MK_MESSAGE SCB for this target now + * that the queue is empty. + */ + test SEQ_FLAGS2, PENDING_MK_MESSAGE jz select_out_no_mk_message_scb; + mov A, MK_MESSAGE_SCSIID; + cmp SCB_SCSIID, A jne select_out_no_mk_message_scb; + and SEQ_FLAGS2, ~PENDING_MK_MESSAGE; + bmov REG0, MK_MESSAGE_SCB, 2; + jmp select_out_queue_remainder; + +select_out_no_mk_message_scb: /* - * The whole list made it. Just clear our TID's tail pointer - * unless we were queued independently due to our need to - * send a message. + * Clear this target's execution tail and increment the queue. */ - test SCB_CONTROL, MK_MESSAGE jnz select_out_inc_tid_q; shr DINDEX, 3, SCB_SCSIID; or DINDEX, 1; /* Want only the second byte */ mvi DINDEX[1], ((WAITING_SCB_TAILS) >> 8); @@ -703,8 +788,8 @@ select_out_inc_tid_q: mvi WAITING_TID_TAIL[1], SCB_LIST_NULL; bmov SCBPTR, CURRSCB, 2; mvi CLRSINT0, CLRSELDO; - test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase; - test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase; + test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_mode_cleared; + test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_mode_cleared; /* * If this is a packetized connection, return to our @@ -2127,6 +2212,18 @@ SET_DST_MODE M_DFF0; mvi DFFSXFRCTL, CLRCHN; unexpected_nonpkt_mode_cleared: mvi CLRSINT2, CLRNONPACKREQ; + if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) { + /* + * Test to ensure that the bus has not + * already gone free prior to clearing + * any stale busfree status. This avoids + * a window whereby a busfree just after + * a selection could be missed. + */ + test SCSISIGI, BSYI jz . + 2; + mvi CLRSINT1,CLRBUSFREE; + or SIMODE1, ENBUSFREE; + } test SCSIPHASE, ~(MSG_IN_PHASE|MSG_OUT_PHASE) jnz illegal_phase; SET_SEQINTCODE(ENTERING_NONPACK) jmp ITloop; diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index db8f5ce99ee..342f77966a5 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -37,7 +37,7 @@ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * - * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#247 $ + * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#250 $ */ #ifdef __linux__ @@ -197,7 +197,8 @@ static int ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, role_t role, uint32_t status, ahd_search_action action, - u_int *list_head, u_int tid); + u_int *list_head, u_int *list_tail, + u_int tid); static void ahd_stitch_tid_list(struct ahd_softc *ahd, u_int tid_prev, u_int tid_cur, u_int tid_next); @@ -1660,7 +1661,8 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat) * so just clear the error. */ ahd_outb(ahd, CLRLQIINT1, CLRLQICRCI_NLQ); - } else if ((status & BUSFREE) != 0) { + } else if ((status & BUSFREE) != 0 + || (lqistat1 & LQOBUSFREE) != 0) { u_int lqostat1; int restart; int clear_fifo; @@ -2025,10 +2027,6 @@ ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime) u_int waiting_t; u_int next; - if ((busfreetime & BUSFREE_LQO) == 0) - printf("%s: Warning, BUSFREE time is 0x%x. " - "Expected BUSFREE_LQO.\n", - ahd_name(ahd), busfreetime); /* * The LQO manager detected an unexpected busfree * either: @@ -2251,8 +2249,14 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) struct ahd_tmode_tstate *tstate; /* - * PPR Rejected. Try non-ppr negotiation - * and retry command. + * PPR Rejected. + * + * If the previous negotiation was packetized, + * this could be because the device has been + * reset without our knowledge. Force our + * current negotiation to async and retry the + * negotiation. Otherwise retry the command + * with non-ppr negotiation. */ #ifdef AHD_DEBUG if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) @@ -2261,11 +2265,34 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) tinfo = ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); - tinfo->curr.transport_version = 2; - tinfo->goal.transport_version = 2; - tinfo->goal.ppr_options = 0; - ahd_qinfifo_requeue_tail(ahd, scb); - printerror = 0; + if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ)!=0) { + ahd_set_width(ahd, &devinfo, + MSG_EXT_WDTR_BUS_8_BIT, + AHD_TRANS_CUR, + /*paused*/TRUE); + ahd_set_syncrate(ahd, &devinfo, + /*period*/0, /*offset*/0, + /*ppr_options*/0, + AHD_TRANS_CUR, + /*paused*/TRUE); + /* + * The expect PPR busfree handler below + * will effect the retry and necessary + * abort. + */ + } else { + tinfo->curr.transport_version = 2; + tinfo->goal.transport_version = 2; + tinfo->goal.ppr_options = 0; + /* + * Remove any SCBs in the waiting for selection + * queue that may also be for this target so + * that command ordering is preserved. + */ + ahd_freeze_devq(ahd, scb); + ahd_qinfifo_requeue_tail(ahd, scb); + printerror = 0; + } } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE) && ppr_busfree == 0) { /* @@ -2280,6 +2307,12 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) MSG_EXT_WDTR_BUS_8_BIT, AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE); + /* + * Remove any SCBs in the waiting for selection + * queue that may also be for this target so that + * command ordering is preserved. + */ + ahd_freeze_devq(ahd, scb); ahd_qinfifo_requeue_tail(ahd, scb); printerror = 0; } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE) @@ -2297,6 +2330,12 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE); + /* + * Remove any SCBs in the waiting for selection + * queue that may also be for this target so that + * command ordering is preserved. + */ + ahd_freeze_devq(ahd, scb); ahd_qinfifo_requeue_tail(ahd, scb); printerror = 0; } else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0 @@ -2369,14 +2408,14 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) */ printf("%s: ", ahd_name(ahd)); } - if (lastphase != P_BUSFREE) - ahd_force_renegotiation(ahd, &devinfo); printf("Unexpected busfree %s, %d SCBs aborted, " "PRGMCNT == 0x%x\n", ahd_lookup_phase_entry(lastphase)->phasemsg, aborted, ahd_inw(ahd, PRGMCNT)); ahd_dump_card_state(ahd); + if (lastphase != P_BUSFREE) + ahd_force_renegotiation(ahd, &devinfo); } /* Always restart the sequencer. */ return (1); @@ -3293,6 +3332,15 @@ ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, con_opts |= WIDEXFER; /* + * Slow down our CRC interval to be + * compatible with packetized U320 devices + * that can't handle a CRC at full speed + */ + if (ahd->features & AHD_AIC79XXB_SLOWCRC) { + con_opts |= ENSLOWCRC; + } + + /* * During packetized transfers, the target will * give us the oportunity to send command packets * without us asserting attention. @@ -3315,7 +3363,6 @@ ahd_update_pending_scbs(struct ahd_softc *ahd) { struct scb *pending_scb; int pending_scb_count; - u_int scb_tag; int paused; u_int saved_scbptr; ahd_mode_state saved_modes; @@ -3333,7 +3380,6 @@ ahd_update_pending_scbs(struct ahd_softc *ahd) pending_scb_count = 0; LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { struct ahd_devinfo devinfo; - struct hardware_scb *pending_hscb; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; @@ -3341,11 +3387,10 @@ ahd_update_pending_scbs(struct ahd_softc *ahd) tinfo = ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); - pending_hscb = pending_scb->hscb; if ((tstate->auto_negotiate & devinfo.target_mask) == 0 && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) { pending_scb->flags &= ~SCB_AUTO_NEGOTIATE; - pending_hscb->control &= ~MK_MESSAGE; + pending_scb->hscb->control &= ~MK_MESSAGE; } ahd_sync_scb(ahd, pending_scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); @@ -3377,18 +3422,15 @@ ahd_update_pending_scbs(struct ahd_softc *ahd) ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO); saved_scbptr = ahd_get_scbptr(ahd); /* Ensure that the hscbs down on the card match the new information */ - for (scb_tag = 0; scb_tag < ahd->scb_data.maxhscbs; scb_tag++) { - struct hardware_scb *pending_hscb; + LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { + u_int scb_tag; u_int control; - pending_scb = ahd_lookup_scb(ahd, scb_tag); - if (pending_scb == NULL) - continue; + scb_tag = SCB_GET_TAG(pending_scb); ahd_set_scbptr(ahd, scb_tag); - pending_hscb = pending_scb->hscb; control = ahd_inb_scbram(ahd, SCB_CONTROL); control &= ~MK_MESSAGE; - control |= pending_hscb->control & MK_MESSAGE; + control |= pending_scb->hscb->control & MK_MESSAGE; ahd_outb(ahd, SCB_CONTROL, control); } ahd_set_scbptr(ahd, saved_scbptr); @@ -6500,13 +6542,14 @@ ahd_chip_init(struct ahd_softc *ahd) | ENLQIOVERI_LQ|ENLQIOVERI_NLQ); ahd_outb(ahd, LQOMODE0, ENLQOATNLQ|ENLQOATNPKT|ENLQOTCRC); /* - * An interrupt from LQOBUSFREE is made redundant by the - * BUSFREE interrupt. We choose to have the sequencer catch - * LQOPHCHGINPKT errors manually for the command phase at the - * start of a packetized selection case. - ahd_outb(ahd, LQOMODE1, ENLQOBUSFREE|ENLQOPHACHGINPKT); + * We choose to have the sequencer catch LQOPHCHGINPKT errors + * manually for the command phase at the start of a packetized + * selection case. ENLQOBUSFREE should be made redundant by + * the BUSFREE interrupt, but it seems that some LQOBUSFREE + * events fail to assert the BUSFREE interrupt so we must + * also enable LQOBUSFREE interrupts. */ - ahd_outb(ahd, LQOMODE1, 0); + ahd_outb(ahd, LQOMODE1, ENLQOBUSFREE); /* * Setup sequencer interrupt handlers. @@ -6617,6 +6660,8 @@ ahd_chip_init(struct ahd_softc *ahd) /* We don't have any waiting selections */ ahd_outw(ahd, WAITING_TID_HEAD, SCB_LIST_NULL); ahd_outw(ahd, WAITING_TID_TAIL, SCB_LIST_NULL); + ahd_outw(ahd, MK_MESSAGE_SCB, SCB_LIST_NULL); + ahd_outw(ahd, MK_MESSAGE_SCSIID, 0xFF); for (i = 0; i < AHD_NUM_TARGETS; i++) ahd_outw(ahd, WAITING_SCB_TAILS + (2 * i), SCB_LIST_NULL); @@ -6704,6 +6749,18 @@ ahd_chip_init(struct ahd_softc *ahd) ahd_loadseq(ahd); ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); + + if (ahd->features & AHD_AIC79XXB_SLOWCRC) { + u_int negodat3 = ahd_inb(ahd, NEGCONOPTS); + + negodat3 |= ENSLOWCRC; + ahd_outb(ahd, NEGCONOPTS, negodat3); + negodat3 = ahd_inb(ahd, NEGCONOPTS); + if (!(negodat3 & ENSLOWCRC)) + printf("aic79xx: failed to set the SLOWCRC bit\n"); + else + printf("aic79xx: SLOWCRC bit set\n"); + } } /* @@ -7260,12 +7317,28 @@ ahd_reset_cmds_pending(struct ahd_softc *ahd) ahd->flags &= ~AHD_UPDATE_PEND_CMDS; } +void +ahd_done_with_status(struct ahd_softc *ahd, struct scb *scb, uint32_t status) +{ + cam_status ostat; + cam_status cstat; + + ostat = ahd_get_transaction_status(scb); + if (ostat == CAM_REQ_INPROG) + ahd_set_transaction_status(scb, status); + cstat = ahd_get_transaction_status(scb); + if (cstat != CAM_REQ_CMP) + ahd_freeze_scb(scb); + ahd_done(ahd, scb); +} + int ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, role_t role, uint32_t status, ahd_search_action action) { struct scb *scb; + struct scb *mk_msg_scb; struct scb *prev_scb; ahd_mode_state saved_modes; u_int qinstart; @@ -7274,6 +7347,7 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, u_int tid_next; u_int tid_prev; u_int scbid; + u_int seq_flags2; u_int savedscbptr; uint32_t busaddr; int found; @@ -7329,23 +7403,10 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, found++; switch (action) { case SEARCH_COMPLETE: - { - cam_status ostat; - cam_status cstat; - - ostat = ahd_get_transaction_status(scb); - if (ostat == CAM_REQ_INPROG) - ahd_set_transaction_status(scb, - status); - cstat = ahd_get_transaction_status(scb); - if (cstat != CAM_REQ_CMP) - ahd_freeze_scb(scb); if ((scb->flags & SCB_ACTIVE) == 0) printf("Inactive SCB in qinfifo\n"); - ahd_done(ahd, scb); - + ahd_done_with_status(ahd, scb, status); /* FALLTHROUGH */ - } case SEARCH_REMOVE: break; case SEARCH_PRINT: @@ -7375,21 +7436,24 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, * looking for matches. */ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); + seq_flags2 = ahd_inb(ahd, SEQ_FLAGS2); + if ((seq_flags2 & PENDING_MK_MESSAGE) != 0) { + scbid = ahd_inw(ahd, MK_MESSAGE_SCB); + mk_msg_scb = ahd_lookup_scb(ahd, scbid); + } else + mk_msg_scb = NULL; savedscbptr = ahd_get_scbptr(ahd); tid_next = ahd_inw(ahd, WAITING_TID_HEAD); tid_prev = SCB_LIST_NULL; targets = 0; for (scbid = tid_next; !SCBID_IS_NULL(scbid); scbid = tid_next) { u_int tid_head; + u_int tid_tail; - /* - * We limit based on the number of SCBs since - * MK_MESSAGE SCBs are not in the per-tid lists. - */ targets++; - if (targets > AHD_SCB_MAX) { + if (targets > AHD_NUM_TARGETS) panic("TID LIST LOOP"); - } + if (scbid >= ahd->scb_data.numscbs) { printf("%s: Waiting TID List inconsistency. " "SCB index == 0x%x, yet numscbs == 0x%x.", @@ -7419,8 +7483,71 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, tid_head = scbid; found += ahd_search_scb_list(ahd, target, channel, lun, tag, role, status, - action, &tid_head, + action, &tid_head, &tid_tail, SCB_GET_TARGET(ahd, scb)); + /* + * Check any MK_MESSAGE SCB that is still waiting to + * enter this target's waiting for selection queue. + */ + if (mk_msg_scb != NULL + && ahd_match_scb(ahd, mk_msg_scb, target, channel, + lun, tag, role)) { + + /* + * We found an scb that needs to be acted on. + */ + found++; + switch (action) { + case SEARCH_COMPLETE: + if ((mk_msg_scb->flags & SCB_ACTIVE) == 0) + printf("Inactive SCB pending MK_MSG\n"); + ahd_done_with_status(ahd, mk_msg_scb, status); + /* FALLTHROUGH */ + case SEARCH_REMOVE: + { + u_int tail_offset; + + printf("Removing MK_MSG scb\n"); + + /* + * Reset our tail to the tail of the + * main per-target list. + */ + tail_offset = WAITING_SCB_TAILS + + (2 * SCB_GET_TARGET(ahd, mk_msg_scb)); + ahd_outw(ahd, tail_offset, tid_tail); + + seq_flags2 &= ~PENDING_MK_MESSAGE; + ahd_outb(ahd, SEQ_FLAGS2, seq_flags2); + ahd_outw(ahd, CMDS_PENDING, + ahd_inw(ahd, CMDS_PENDING)-1); + mk_msg_scb = NULL; + break; + } + case SEARCH_PRINT: + printf(" 0x%x", SCB_GET_TAG(scb)); + /* FALLTHROUGH */ + case SEARCH_COUNT: + break; + } + } + + if (mk_msg_scb != NULL + && SCBID_IS_NULL(tid_head) + && ahd_match_scb(ahd, scb, target, channel, CAM_LUN_WILDCARD, + SCB_LIST_NULL, ROLE_UNKNOWN)) { + + /* + * When removing the last SCB for a target + * queue with a pending MK_MESSAGE scb, we + * must queue the MK_MESSAGE scb. + */ + printf("Queueing mk_msg_scb\n"); + tid_head = ahd_inw(ahd, MK_MESSAGE_SCB); + seq_flags2 &= ~PENDING_MK_MESSAGE; + ahd_outb(ahd, SEQ_FLAGS2, seq_flags2); + mk_msg_scb = NULL; + } if (tid_head != scbid) ahd_stitch_tid_list(ahd, tid_prev, tid_head, tid_next); if (!S |