diff options
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.c | 640 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.h | 68 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.reg | 115 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.seq | 127 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx_inline.h | 12 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx_osm.c | 9 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx_pci.c | 25 |
7 files changed, 844 insertions, 152 deletions
diff --git a/sys/dev/aic7xxx/aic79xx.c b/sys/dev/aic7xxx/aic79xx.c index 423c01c..8a90e86 100644 --- a/sys/dev/aic7xxx/aic79xx.c +++ b/sys/dev/aic7xxx/aic79xx.c @@ -2,7 +2,7 @@ * Core routines and tables shareable across OS platforms. * * Copyright (c) 1994-2002 Justin T. Gibbs. - * Copyright (c) 2000-2002 Adaptec Inc. + * Copyright (c) 2000-2003 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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#139 $ + * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#155 $ * * $FreeBSD$ */ @@ -171,8 +171,8 @@ static void ahd_handle_ign_wide_residue(struct ahd_softc *ahd, static void ahd_reinitialize_dataptrs(struct ahd_softc *ahd); static void ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, - cam_status status, char *message, - int verbose_level); + u_int lun, cam_status status, + char *message, int verbose_level); #if AHD_TARGET_MODE static void ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, @@ -211,6 +211,7 @@ static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid, u_int prev, u_int next, u_int tid); static void ahd_reset_current_bus(struct ahd_softc *ahd); static ahd_callback_t ahd_reset_poll; +static ahd_callback_t ahd_stat_timer; #ifdef AHD_DUMP_SEQ static void ahd_dumpseq(struct ahd_softc *ahd); #endif @@ -282,7 +283,6 @@ ahd_set_active_fifo(struct ahd_softc *ahd) AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK); active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO; -/* XXX This is a three possition switch in the B. */ switch (active_fifo) { case 0: case 1: @@ -332,7 +332,6 @@ ahd_restart(struct ahd_softc *ahd) ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN); ahd_outb(ahd, SCBHCNT, 0); ahd_outb(ahd, CCSCBCTL, CCSCBRESET); - ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET); ahd_unpause(ahd); } @@ -357,12 +356,122 @@ ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo) } /************************* Input/Output Queues ********************************/ +/* + * Flush and completed commands that are sitting in the command + * complete queues down on the chip but have yet to be dma'ed back up. + */ +void +ahd_flush_qoutfifo(struct ahd_softc *ahd) +{ + struct scb *scb; + ahd_mode_state saved_modes; + u_int saved_scbptr; + u_int ccscbctl; + u_int scbid; + u_int next_scbid; + + saved_modes = ahd_save_modes(ahd); + ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN); + saved_scbptr = ahd_get_scbptr(ahd); + + /* + * Wait for any inprogress DMA to complete and clear DMA state + * if this if for an SCB in the qinfifo. + */ + while ((ccscbctl = ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN)) != 0) { + + if ((ccscbctl & (CCSCBDIR|CCARREN)) == (CCSCBDIR|CCARREN)) { + if ((ccscbctl & ARRDONE) != 0) + break; + } else if ((ccscbctl & CCSCBDONE) != 0) + break; + ahd_delay(200); + } + if ((ccscbctl & CCSCBDIR) != 0) + ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN)); + + /* + * Complete any SCBs that just finished being + * DMA'ed into the qoutfifo. + */ + ahd_run_qoutfifo(ahd); + + /* + * Manually update/complete any completed SCBs that are waiting to be + * DMA'ed back up to the host. + */ + scbid = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD); + while (!SCBID_IS_NULL(scbid)) { + uint8_t *hscb_ptr; + u_int i; + + ahd_set_scbptr(ahd, scbid); + next_scbid = ahd_inw(ahd, SCB_NEXT_COMPLETE); + scb = ahd_lookup_scb(ahd, scbid); + if (scb == NULL) { + printf("%s: Warning - DMA-up and complete " + "SCB %d invalid\n", ahd_name(ahd), scbid); + continue; + } + hscb_ptr = (uint8_t *)scb->hscb; + for (i = 0; i < sizeof(struct hardware_scb); i++) + *hscb_ptr++ = ahd_inb(ahd, SCB_BASE + i); + + ahd_complete_scb(ahd, scb); + scbid = next_scbid; + } + ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL); + + scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD); + while (!SCBID_IS_NULL(scbid)) { + + ahd_set_scbptr(ahd, scbid); + next_scbid = ahd_inw(ahd, SCB_NEXT_COMPLETE); + scb = ahd_lookup_scb(ahd, scbid); + if (scb == NULL) { + printf("%s: Warning - Complete SCB %d invalid\n", + ahd_name(ahd), scbid); + continue; + } + + ahd_complete_scb(ahd, scb); + scbid = next_scbid; + } + ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL); + ahd_set_scbptr(ahd, saved_scbptr); + + /* + * Flush the good status FIFO for compelted packetized commands. + */ + ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); + while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) { + scbid = (ahd_inb(ahd, GSFIFO+1) << 8) + | ahd_inb(ahd, GSFIFO); + scb = ahd_lookup_scb(ahd, scbid); + if (scb == NULL) { + printf("%s: Warning - GSFIFO SCB %d invalid\n", + ahd_name(ahd), scbid); + continue; + } + ahd_complete_scb(ahd, scb); + } + + /* + * Restore state. + */ + ahd_restore_modes(ahd, saved_modes); + ahd->flags |= AHD_UPDATE_PEND_CMDS; +} + void ahd_run_qoutfifo(struct ahd_softc *ahd) { struct scb *scb; u_int scb_index; + if ((ahd->flags & AHD_RUNNING_QOUTFIFO) != 0) + panic("ahd_run_qoutfifo recursion"); + ahd->flags |= AHD_RUNNING_QOUTFIFO; ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD); while ((ahd->qoutfifo[ahd->qoutfifonext] & QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag) { @@ -382,8 +491,8 @@ ahd_run_qoutfifo(struct ahd_softc *ahd) ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1); if (ahd->qoutfifonext == 0) ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID_LE; - } + ahd->flags &= ~AHD_RUNNING_QOUTFIFO; } /************************* Interrupt Handling *********************************/ @@ -579,8 +688,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat) &tstate); tinfo = &targ_info->curr; ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, - AHD_TRANS_ACTIVE|AHD_TRANS_GOAL, - /*paused*/TRUE); + AHD_TRANS_ACTIVE, /*paused*/TRUE); ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0, /*ppr_options*/0, AHD_TRANS_ACTIVE, /*paused*/TRUE); @@ -895,6 +1003,96 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat) ahd_inb(ahd, SCB_CONTROL) & ~MK_MESSAGE); break; } + case TASKMGMT_FUNC_COMPLETE: + { + u_int scbid; + struct scb *scb; + + scbid = ahd_get_scbptr(ahd); + scb = ahd_lookup_scb(ahd, scbid); + if (scb != NULL) { + u_int lun; + u_int tag; + cam_status error; + + ahd_print_path(ahd, scb); + printf("Task Management Func 0x%x Complete\n", + scb->hscb->task_management); + lun = CAM_LUN_WILDCARD; + tag = SCB_LIST_NULL; + + switch (scb->hscb->task_management) { + case SIU_TASKMGMT_ABORT_TASK: + tag = scb->hscb->tag; + case SIU_TASKMGMT_ABORT_TASK_SET: + case SIU_TASKMGMT_CLEAR_TASK_SET: + lun = scb->hscb->lun; + error = CAM_REQ_ABORTED; + ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), + 'A', lun, tag, ROLE_INITIATOR, + error); + break; + case SIU_TASKMGMT_LUN_RESET: + lun = scb->hscb->lun; + case SIU_TASKMGMT_TARGET_RESET: + { + struct ahd_devinfo devinfo; + + ahd_scb_devinfo(ahd, &devinfo, scb); + error = CAM_BDR_SENT; + ahd_handle_devreset(ahd, &devinfo, lun, + CAM_BDR_SENT, + lun != CAM_LUN_WILDCARD + ? "Lun Reset" + : "Target Reset", + /*verbose_level*/0); + break; + } + default: + panic("Unexpected TaskMgmt Func\n"); + break; + } + } + break; + } + case TASKMGMT_CMD_CMPLT_OKAY: + { + u_int scbid; + struct scb *scb; + + /* + * An ABORT TASK TMF failed to be delivered before + * the targeted command completed normally. + */ + scbid = ahd_get_scbptr(ahd); + scb = ahd_lookup_scb(ahd, scbid); + if (scb != NULL) { + /* + * Remove the second instance of this SCB from + * the QINFIFO if it is still there. + */ + ahd_print_path(ahd, scb); + printf("SCB completes before TMF\n"); + /* + * Handle losing the race. Wait until any + * current selection completes. We will then + * set the TMF back to zero in this SCB so that + * the sequencer doesn't bother to issue another + * sequencer interrupt for its completion. + */ + while ((ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0 + && (ahd_inb(ahd, SSTAT0) & SELDO) == 0 + && (ahd_inb(ahd, SSTAT1) & SELTO) == 0) + ; + ahd_outb(ahd, SCB_TASK_MANAGEMENT, 0); + ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb), + SCB_GET_CHANNEL(ahd, scb), + SCB_GET_LUN(scb), scb->hscb->tag, + ROLE_INITIATOR, /*status*/0, + SEARCH_REMOVE); + } + break; + } case TRACEPOINT0: case TRACEPOINT1: case TRACEPOINT2: @@ -1041,7 +1239,7 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat) } ahd_outb(ahd, CLRINT, CLRSCSIINT); ahd_iocell_first_selection(ahd); - ahd_restart(ahd); + ahd_unpause(ahd); } else if ((status0 & (SELDI|SELDO)) != 0) { ahd_iocell_first_selection(ahd); ahd_unpause(ahd); @@ -1171,6 +1369,7 @@ ahd_handle_transmission_error(struct ahd_softc *ahd) u_int lastphase; u_int perrdiag; u_int cur_col; + int silent; scb = NULL; ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); @@ -1201,18 +1400,38 @@ ahd_handle_transmission_error(struct ahd_softc *ahd) perrdiag = ahd_inb(ahd, PERRDIAG); msg_out = MSG_INITIATOR_DET_ERR; ahd_outb(ahd, CLRSINT1, CLRSCSIPERR); - printf("%s: Transmission error detected\n", ahd_name(ahd)); + + /* + * Try to find the SCB associated with this error. + */ + silent = FALSE; + if (lqistat1 == 0 + || (lqistat1 & LQICRCI_NLQ) != 0) { + if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0) + ahd_set_active_fifo(ahd); + scbid = ahd_get_scbptr(ahd); + scb = ahd_lookup_scb(ahd, scbid); + if (scb != NULL && SCB_IS_SILENT(scb)) + silent = TRUE; + } + cur_col = 0; - ahd_lqistat1_print(lqistat1, &cur_col, 50); - ahd_lastphase_print(lastphase, &cur_col, 50); - ahd_scsisigi_print(curphase, &cur_col, 50); - ahd_perrdiag_print(perrdiag, &cur_col, 50); - printf("\n"); - ahd_dump_card_state(ahd); + if (silent == FALSE) { + printf("%s: Transmission error detected\n", ahd_name(ahd)); + ahd_lqistat1_print(lqistat1, &cur_col, 50); + ahd_lastphase_print(lastphase, &cur_col, 50); + ahd_scsisigi_print(curphase, &cur_col, 50); + ahd_perrdiag_print(perrdiag, &cur_col, 50); + printf("\n"); + ahd_dump_card_state(ahd); + } + if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) { - printf("%s: Gross protocol error during incoming " - "packet. lqistat1 == 0x%x. Resetting bus.\n", - ahd_name(ahd), lqistat1); + if (silent == FALSE) { + printf("%s: Gross protocol error during incoming " + "packet. lqistat1 == 0x%x. Resetting bus.\n", + ahd_name(ahd), lqistat1); + } ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE); return; } else if ((lqistat1 & LQICRCI_LQ) != 0) { @@ -1285,10 +1504,8 @@ ahd_handle_transmission_error(struct ahd_softc *ahd) * listed above for the read streaming with P0 asserted. * Busfree detection is enabled. */ - printf("LQICRC_NLQ\n"); - ahd_set_active_fifo(ahd); - scbid = ahd_get_scbptr(ahd); - scb = ahd_lookup_scb(ahd, scbid); + if (silent == FALSE) + printf("LQICRC_NLQ\n"); if (scb == NULL) { printf("%s: No SCB valid for LQICRC_NLQ. " "Resetting bus\n", ahd_name(ahd)); @@ -1300,12 +1517,11 @@ ahd_handle_transmission_error(struct ahd_softc *ahd) ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE); return; } else if ((perrdiag & (PARITYERR|PREVPHASE)) == PARITYERR) { - scbid = ahd_get_scbptr(ahd); - scb = ahd_lookup_scb(ahd, scbid); if ((curphase & ~P_DATAIN_DT) != 0) { /* Ack the byte. So we can continue. */ - printf("Acking %s to clear perror\n", - ahd_lookup_phase_entry(curphase)->phasemsg); + if (silent == FALSE) + printf("Acking %s to clear perror\n", + ahd_lookup_phase_entry(curphase)->phasemsg); ahd_inb(ahd, SCSIDAT); } @@ -1449,9 +1665,11 @@ ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime) } ahd_set_scbptr(ahd, saved_scbptr); if (scb->crc_retry_count < AHD_MAX_LQ_CRC_ERRORS) { - ahd_print_path(ahd, scb); - printf("Probable outgoing LQ CRC error. " - "Retrying command\n"); + if (SCB_IS_SILENT(scb) == FALSE) { + ahd_print_path(ahd, scb); + printf("Probable outgoing LQ CRC error. " + "Retrying command\n"); + } scb->crc_retry_count++; } else { ahd_set_transaction_status(scb, CAM_UNCOR_PARITY); @@ -1598,8 +1816,8 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) ROLE_INITIATOR)) ahd_set_transaction_status(scb, CAM_REQ_CMP); #endif - ahd_handle_devreset(ahd, &devinfo, CAM_BDR_SENT, - "Bus Device Reset", + ahd_handle_devreset(ahd, &devinfo, CAM_LUN_WILDCARD, + CAM_BDR_SENT, "Bus Device Reset", /*verbose_level*/0); printerror = 0; } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE) @@ -1679,10 +1897,6 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd) ahd_set_transaction_status(scb, CAM_REQUEUE_REQ); ahd_freeze_scb(scb); if ((ahd->msg_flags & MSG_FLAG_IU_REQ_CHANGED) != 0) { - ahd_print_path(ahd, scb); - printf("Now %spacketized.\n", - (scb->flags & SCB_PACKETIZED) == 0 - ? "" : "non-"); ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), SCB_GET_CHANNEL(ahd, scb), SCB_GET_LUN(scb), SCB_LIST_NULL, @@ -2168,8 +2382,10 @@ ahd_devlimited_syncrate(struct ahd_softc *ahd, else transinfo = &tinfo->goal; *ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN); - if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) + if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) { maxsync = MAX(maxsync, AHD_SYNCRATE_ULTRA2); + *ppr_options &= ~MSG_EXT_PPR_DT_REQ; + } if (transinfo->period == 0) { *period = 0; *ppr_options = 0; @@ -2188,16 +2404,6 @@ void ahd_find_syncrate(struct ahd_softc *ahd, u_int *period, u_int *ppr_options, u_int maxsync) { - /* Skip all PACED only entries if IU is not available */ - if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0 - && maxsync < AHD_SYNCRATE_DT) - maxsync = AHD_SYNCRATE_DT; - - /* Skip all DT only entries if DT is not available */ - if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0 - && maxsync < AHD_SYNCRATE_ULTRA2) - maxsync = AHD_SYNCRATE_ULTRA2; - if (*period < maxsync) *period = maxsync; @@ -2217,6 +2423,16 @@ ahd_find_syncrate(struct ahd_softc *ahd, u_int *period, if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0) *ppr_options &= MSG_EXT_PPR_QAS_REQ; + + /* Skip all PACED only entries if IU is not available */ + if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0 + && *period < AHD_SYNCRATE_DT) + *period = AHD_SYNCRATE_DT; + + /* Skip all DT only entries if DT is not available */ + if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0 + && *period < AHD_SYNCRATE_ULTRA2) + *period = AHD_SYNCRATE_ULTRA2; } /* @@ -2299,10 +2515,10 @@ ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, * occurs the need to renegotiate is * recorded persistently. */ + if ((ahd->features & AHD_WIDE) != 0) + tinfo->curr.width = AHD_WIDTH_UNKNOWN; tinfo->curr.period = AHD_PERIOD_UNKNOWN; - tinfo->curr.width = AHD_WIDTH_UNKNOWN; tinfo->curr.offset = AHD_OFFSET_UNKNOWN; - tinfo->curr.ppr_options = AHD_OFFSET_UNKNOWN; } if (tinfo->curr.period != tinfo->goal.period || tinfo->curr.width != tinfo->goal.width @@ -2382,16 +2598,39 @@ ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL); if (bootverbose) { if (offset != 0) { + int options; + printf("%s: target %d synchronous with " - "period = 0x%x, offset = 0x%x%s\n", + "period = 0x%x, offset = 0x%x", ahd_name(ahd), devinfo->target, - period, offset, - (ppr_options & MSG_EXT_PPR_DT_REQ) - ? " (DT)" : ""); + period, offset); + options = 0; + if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) { + printf("(DT"); + options++; + } + if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { + printf("%s", options ? "|IU" : "(IU"); + options++; + } + if ((ppr_options & MSG_EXT_PPR_RTI) != 0) { + printf("%s", options ? "|RTI" : "(RTI"); + options++; + } + if ((ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) { + printf("%s", options ? "|QAS" : "(QAS"); + options++; + } + if (options != 0) + printf(")\n"); + else + printf("\n"); } else { printf("%s: target %d using " - "asynchronous transfers\n", - ahd_name(ahd), devinfo->target); + "asynchronous transfers%s\n", + ahd_name(ahd), devinfo->target, + (ppr_options & MSG_EXT_PPR_QAS_REQ) != 0 + ? "(QAS)" : ""); } } } @@ -2504,9 +2743,9 @@ void ahd_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, ahd_queue_alg alg) { - ahd_platform_set_tags(ahd, devinfo, alg); - ahd_send_async(ahd, devinfo->channel, devinfo->target, - devinfo->lun, AC_TRANSFER_NEG, &alg); + ahd_platform_set_tags(ahd, devinfo, alg); + ahd_send_async(ahd, devinfo->channel, devinfo->target, + devinfo->lun, AC_TRANSFER_NEG, &alg); } static void @@ -2518,11 +2757,13 @@ ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, u_int ppr_opts; u_int con_opts; u_int offset; + u_int saved_negoaddr; uint8_t iocell_opts[sizeof(ahd->iocell_opts)]; saved_modes = ahd_save_modes(ahd); ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); + saved_negoaddr = ahd_inb(ahd, NEGOADDR); ahd_outb(ahd, NEGOADDR, devinfo->target); period = tinfo->period; offset = tinfo->offset; @@ -2597,6 +2838,7 @@ ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) == 0) con_opts |= ENAUTOATNO; ahd_outb(ahd, NEGCONOPTS, con_opts); + ahd_outb(ahd, NEGOADDR, saved_negoaddr); ahd_restore_modes(ahd, saved_modes); } @@ -2892,9 +3134,10 @@ ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, "does not have a waiting message\n"); printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid, devinfo->target_mask); - panic("SCB = %d, SCB Control = %x, MSG_OUT = %x " + panic("SCB = %d, SCB Control = %x:%x, MSG_OUT = %x " "SCB flags = %x", SCB_GET_TAG(scb), scb->hscb->control, - ahd_inb(ahd, MSG_OUT), scb->flags); + ahd_inb(ahd, SCB_CONTROL), ahd_inb(ahd, MSG_OUT), + scb->flags); } /* @@ -3277,8 +3520,15 @@ reswitch: * assert ATN so the target takes us to the * message out phase. */ - if (ahd->msgout_len != 0) + if (ahd->msgout_len != 0) { +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) { + ahd_print_devinfo(ahd, &devinfo); + printf("Asserting ATN for response\n"); + } +#endif ahd_assert_atn(ahd); + } } else ahd->msgin_index++; @@ -3472,8 +3722,10 @@ ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full) } else { /* Single byte message */ if (type == AHDMSG_1B - && ahd->msgout_buf[index] == msgval - && ahd->msgout_index > index) + && ahd->msgout_index > index + && (ahd->msgout_buf[index] == msgval + || ((ahd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0 + && msgval == MSG_IDENTIFYFLAG))) found = TRUE; index++; } @@ -3689,7 +3941,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) AHD_TRANS_ACTIVE, /*paused*/TRUE); if (sending_reply == FALSE && reject == FALSE) { - if (tinfo->goal.period) { + if (tinfo->goal.offset) { ahd->msgout_index = 0; ahd->msgout_len = 0; ahd_build_transfer_msg(ahd, devinfo); @@ -3819,7 +4071,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) } #ifdef AHD_TARGET_MODE case MSG_BUS_DEV_RESET: - ahd_handle_devreset(ahd, devinfo, + ahd_handle_devreset(ahd, devinfo, CAM_LUN_WILDCARD, CAM_BDR_SENT, "Bus Device Reset Received", /*verbose_level*/0); @@ -3975,7 +4227,7 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) * but rejected our response, we already cleared the * sync rate before sending our WDTR. */ - if (tinfo->goal.period) { + if (tinfo->goal.period != tinfo->curr.offset) { /* Start the sync negotiation */ ahd->msgout_index = 0; @@ -4040,6 +4292,20 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ, SEARCH_COMPLETE); + } else if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_IDENTIFYFLAG, TRUE)) { + /* + * Most likely the device believes that we had + * previously negotiated packetized. + */ + ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE + | MSG_FLAG_IU_REQ_CHANGED; + + ahd_force_renegotiation(ahd, devinfo); + ahd->msgout_index = 0; + ahd->msgout_len = 0; + ahd_build_transfer_msg(ahd, devinfo); + ahd->msgout_index = 0; + response = 1; } else { /* * Otherwise, we ignore it. @@ -4304,16 +4570,16 @@ ahd_reinitialize_dataptrs(struct ahd_softc *ahd) */ static void ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, - cam_status status, char *message, int verbose_level) + u_int lun, cam_status status, char *message, + int verbose_level) { #ifdef AHD_TARGET_MODE struct ahd_tmode_tstate* tstate; - u_int lun; #endif int found; found = ahd_abort_scbs(ahd, devinfo->target, devinfo->channel, - CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role, + lun, SCB_LIST_NULL, devinfo->role, status); #ifdef AHD_TARGET_MODE @@ -4323,10 +4589,20 @@ ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, */ tstate = ahd->enabled_targets[devinfo->our_scsiid]; if (tstate != NULL) { - for (lun = 0; lun < AHD_NUM_LUNS; lun++) { + u_int cur_lun; + u_int max_lun; + + if (lun != CAM_LUN_WILDCARD) { + cur_lun = 0; + max_lun = AHD_NUM_LUNS - 1; + } else { + cur_lun = lun; + max_lun = lun; + } + for (cur_lun <= max_lun; cur_lun++) { struct ahd_tmode_lstate* lstate; - lstate = tstate->enabled_luns[lun]; + lstate = tstate->enabled_luns[cur_lun]; if (lstate == NULL) continue; @@ -4346,7 +4622,7 @@ ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, /*ppr_options*/0, AHD_TRANS_CUR, /*paused*/TRUE); ahd_send_async(ahd, devinfo->channel, devinfo->target, - CAM_LUN_WILDCARD, AC_SENT_BDR, NULL); + lun, AC_SENT_BDR, NULL); if (message != NULL && (verbose_level <= bootverbose)) @@ -4478,6 +4754,13 @@ ahd_alloc(void *platform_arg, char *name) ahd->flags = AHD_SPCHK_ENB_A|AHD_RESET_BUS_A|AHD_TERM_ENB_A | AHD_EXTENDED_TRANS_A|AHD_STPWLEVEL_A; ahd_timer_init(&ahd->reset_timer); + ahd_timer_init(&ahd->stat_timer); + ahd->int_coalessing_timer = AHD_INT_COALESSING_TIMER_DEFAULT; + ahd->int_coalessing_maxcmds = AHD_INT_COALESSING_MAXCMDS_DEFAULT; + ahd->int_coalessing_mincmds = AHD_INT_COALESSING_MINCMDS_DEFAULT; + ahd->int_coalessing_threshold = AHD_INT_COALESSING_THRESHOLD_DEFAULT; + ahd->int_coalessing_stop_threshold = + AHD_INT_COALESSING_STOP_THRESHOLD_DEFAULT; if (ahd_platform_alloc(ahd, platform_arg) != 0) { ahd_free(ahd); @@ -4670,6 +4953,12 @@ ahd_shutdown(void *arg) ahd = (struct ahd_softc *)arg; + /* + * Stop periodic timer callbacks. + */ + ahd_timer_stop(&ahd->reset_timer); + ahd_timer_stop(&ahd->stat_timer); + /* This will reset most registers to 0, but not all */ ahd_reset(ahd); } @@ -5672,6 +5961,8 @@ ahd_init(struct ahd_softc *ahd) } init_done: ahd_restart(ahd); + ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US, + ahd_stat_timer, ahd); return (0); } @@ -5694,6 +5985,12 @@ ahd_chip_init(struct ahd_softc *ahd) */ ahd_outb(ahd, SBLKCTL, ahd_inb(ahd, SBLKCTL) & ~(DIAGLEDEN|DIAGLEDON)); + /* + * Return HS_MAILBOX to its default value. + */ + ahd->hs_mailbox = 0; + ahd_outb(ahd, HS_MAILBOX, 0); + /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1. */ ahd_outb(ahd, IOWNID, ahd->our_id); ahd_outb(ahd, TOWNID, ahd->our_id); @@ -5798,9 +6095,10 @@ ahd_chip_init(struct ahd_softc *ahd) ahd_outb(ahd, LQOMODE1, 0); /* - * Setup sequencer interrupt handler. + * Setup sequencer interrupt handlers. */ ahd_outw(ahd, INTVEC1_ADDR, ahd_resolve_seqaddr(ahd, LABEL_seq_isr)); + ahd_outw(ahd, INTVEC2_ADDR, ahd_resolve_seqaddr(ahd, LABEL_timer_isr)); /* * Setup SCB Offset registers. @@ -5982,6 +6280,17 @@ ahd_chip_init(struct ahd_softc *ahd) ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF); ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF); ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF); + + /* + * Default to coalessing disabled. + */ + ahd_outw(ahd, INT_COALESSING_CMDCOUNT, 0); + ahd_outw(ahd, CMDS_PENDING, 0); + ahd_update_coalessing_values(ahd, ahd->int_coalessing_timer, + ahd->int_coalessing_maxcmds, + ahd->int_coalessing_mincmds); + ahd_enable_coalessing(ahd, FALSE); + ahd_loadseq(ahd); ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); } @@ -6128,7 +6437,7 @@ ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc) user_tinfo->offset = MAX_OFFSET; } else { user_tinfo->offset = 0; - user_tinfo->period = AHD_PERIOD_ASYNC; + user_tinfo->period = AHD_ASYNC_XFER_PERIOD; } #ifdef AHD_FORCE_160 if (user_tinfo->period <= AHD_SYNCRATE_160) @@ -6216,6 +6525,36 @@ ahd_intr_enable(struct ahd_softc *ahd, int enable) ahd_outb(ahd, HCNTRL, hcntrl); } +void +ahd_update_coalessing_values(struct ahd_softc *ahd, u_int timer, u_int maxcmds, + u_int mincmds) +{ + if (timer > AHD_TIMER_MAX_US) + timer = AHD_TIMER_MAX_US; + ahd->int_coalessing_timer = timer; + + if (maxcmds > AHD_INT_COALESSING_MAXCMDS_MAX) + maxcmds = AHD_INT_COALESSING_MAXCMDS_MAX; + if (mincmds > AHD_INT_COALESSING_MINCMDS_MAX) + mincmds = AHD_INT_COALESSING_MINCMDS_MAX; + ahd->int_coalessing_maxcmds = maxcmds; + ahd_outw(ahd, INT_COALESSING_TIMER, timer / AHD_TIMER_US_PER_TICK); + ahd_outb(ahd, INT_COALESSING_MAXCMDS, -maxcmds); + ahd_outb(ahd, INT_COALESSING_MINCMDS, -mincmds); +} + +void +ahd_enable_coalessing(struct ahd_softc *ahd, int enable) +{ + + ahd->hs_mailbox &= ~ENINT_COALESS; + if (enable) + ahd->hs_mailbox |= ENINT_COALESS; + ahd_outb(ahd, HS_MAILBOX, ahd->hs_mailbox); + ahd_flush_device_writes(ahd); + ahd_run_qoutfifo(ahd); +} + /* * Ensure that the card is paused in a location * outside of all critical sections and that all @@ -6226,24 +6565,51 @@ ahd_intr_enable(struct ahd_softc *ahd, int enable) void ahd_pause_and_flushwork(struct ahd_softc *ahd) { - int intstat; - int maxloops; + u_int intstat; + u_int maxloops; + int paused; maxloops = 1000; ahd->flags |= AHD_ALL_INTERRUPTS; intstat = 0; + paused = FALSE; do { + struct scb *waiting_scb; + + if (paused) + ahd_unpause(ahd); ahd_intr(ahd); ahd_pause(ahd); + paused = TRUE; ahd_clear_critical_section(ahd); + if ((ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0) + ahd_outb(ahd, SCSISEQ0, + ahd_inb(ahd, SCSISEQ0) & ~ENSELO); + /* + * In the non-packetized case, the sequencer (for Rev A), + * relies on ENSELO remaining set after SELDO. The hardware + * auto-clears ENSELO in the packetized case. + */ + waiting_scb = ahd_lookup_scb(ahd, + ahd_inw(ahd, WAITING_TID_HEAD)); + if (waiting_scb != NULL + && (waiting_scb->flags & SCB_PACKETIZED) == 0 + && (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0) + ahd_outb(ahd, SCSISEQ0, + ahd_inb(ahd, SCSISEQ0) | ENSELO); + if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0) break; - maxloops--; - } while (((intstat = ahd_inb(ahd, INTSTAT)) & INT_PEND) && --maxloops); + } while (--maxloops + && (((intstat = ahd_inb(ahd, INTSTAT)) & INT_PEND) != 0 + || (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)))); if (maxloops == 0) { printf("Infinite interrupt loop, INTSTAT = %x", ahd_inb(ahd, INTSTAT)); } + + ahd_flush_qoutfifo(ahd); + ahd_platform_flushwork(ahd); ahd->flags &= ~AHD_ALL_INTERRUPTS; } @@ -6556,13 +6922,38 @@ ahd_qinfifo_count(struct ahd_softc *ahd) qinpos = ahd_get_snscb_qoff(ahd); wrap_qinpos = AHD_QIN_WRAP(qinpos); wrap_qinfifonext = AHD_QIN_WRAP(ahd->qinfifonext); - if (wrap_qinfifonext > wrap_qinpos) + if (wrap_qinfifonext >= wrap_qinpos) return (wrap_qinfifonext - wrap_qinpos); else return (wrap_qinfifonext + NUM_ELEMENTS(ahd->qinfifo) - wrap_qinpos); } +void +ahd_reset_cmds_pending(struct ahd_softc *ahd) +{ + struct scb *scb; + ahd_mode_state saved_modes; + u_int pending_cmds; + + saved_modes = ahd_save_modes(ahd); + ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN); + + /* + * Don't count any commands as outstanding that the + * sequencer has already marked for completion. + */ + ahd_flush_qoutfifo(ahd); + + pending_cmds = 0; + LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) { + pending_cmds++; + } + ahd_outw(ahd, CMDS_PENDING, pending_cmds - ahd_qinfifo_count(ahd)); + ahd_restore_modes(ahd, saved_modes); + ahd->flags &= ~AHD_UPDATE_PEND_CMDS; +} + int ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, role_t role, uint32_t status, @@ -6734,7 +7125,6 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, printf(")\n"); } ahd_set_scbptr(ahd, savedscbptr); - ahd_restore_modes(ahd, saved_modes); return (found); } @@ -6807,6 +7197,9 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel, if (found > AHD_SCB_MAX) panic("SCB LIST LOOP"); } + if (action == SEARCH_COMPLETE + || action == SEARCH_REMOVE) + ahd_outw(ahd, CMDS_PENDING, ahd_inw(ahd, CMDS_PENDING) - found); return (found); } @@ -6959,6 +7352,12 @@ ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel, } /* + * Don't abort commands that have already completed, + * but haven't quite made it up to the host yet. + */ + ahd_flush_qoutfifo(ahd); + + /* * Go through the pending CCB list and look for * commands for this target that are still active. * These are other tagged commands that were @@ -6985,6 +7384,7 @@ ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel, ahd_set_scbptr(ahd, active_scb); ahd_restore_modes(ahd, saved_modes); ahd_platform_abort_scbs(ahd, target, channel, lun, tag, role, status); + ahd->flags |= AHD_UPDATE_PEND_CMDS; return found; } @@ -7039,12 +7439,6 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset) /* Make sure the sequencer is in a safe location. */ ahd_clear_critical_section(ahd); - /* - * Run our command complete fifos to ensure that we perform - * completion processing on any commands that 'completed' - * before the reset occurred. - */ - ahd_run_qoutfifo(ahd); #if AHD_TARGET_MODE if ((ahd->flags & AHD_TARGETROLE) != 0) { ahd_run_tqinfifo(ahd, /*paused*/TRUE); @@ -7214,6 +7608,50 @@ ahd_reset_poll(void *arg) ahd_list_unlock(&l); } +/**************************** Statistics Processing ***************************/ +static void +ahd_stat_timer(void *arg) +{ + struct ahd_softc *ahd; + u_long l; + u_long s; + int enint_coal; + + ahd_list_lock(&l); + ahd = ahd_find_softc((struct ahd_softc *)arg); + if (ahd == NULL) { + printf("ahd_stat_timer: Instance %p no longer exists\n", arg); + ahd_list_unlock(&l); + return; + } + ahd_lock(ahd, &s); + + enint_coal = ahd->hs_mailbox & ENINT_COALESS; + if (ahd->cmdcmplt_total > ahd->int_coalessing_threshold) + enint_coal |= ENINT_COALESS; + else if (ahd->cmdcmplt_total < ahd->int_coalessing_stop_threshold) + enint_coal &= ~ENINT_COALESS; + + if (enint_coal != (ahd->hs_mailbox & ENINT_COALESS)) { + ahd_enable_coalessing(ahd, enint_coal); +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_INT_COALESSING) != 0) + printf("%s: Interrupt coalessing " + "now %sabled. Cmds %d\n", + ahd_name(ahd), + (enint_coal & ENINT_COALESS) ? "en" : "dis", + ahd->cmdcmplt_total); +#endif + } + + ahd->cmdcmplt_bucket = (ahd->cmdcmplt_bucket+1) & (AHD_STAT_BUCKETS-1); + ahd->cmdcmplt_total -= ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]; + ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket] = 0; + ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US, + ahd_stat_timer, ahd); + ahd_unlock(ahd, &s); + ahd_list_unlock(&l); +} /****************************** Status Processing *****************************/ void @@ -7535,7 +7973,8 @@ ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb) #ifdef AHD_DEBUG if ((ahd_debug & AHD_SHOW_MISC) != 0) { ahd_print_path(ahd, scb); - printf("Handled Residual of %d bytes\n", resid); + printf("Handled %sResidual of %d bytes\n", + (scb->flags & SCB_SENSE) ? "Sense " : "", resid); } #endif } @@ -7806,8 +8245,11 @@ ahd_loadseq(struct ahd_softc *ahd) } ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE); - if (bootverbose) + if (bootverbose) { printf(" %d instructions downloaded\n", downloaded); + printf("%s: Features 0x%x, Bugs 0x%x, Flags 0x%x\n", + ahd_name(ahd), ahd->features, ahd->bugs, ahd->flags); + } } static int @@ -8069,6 +8511,9 @@ ahd_dump_card_state(struct ahd_softc *ahd) * Mode independent registers. */ cur_col = 0; + ahd_hs_mailbox_print(ahd_inb(ahd, LOCAL_HS_MAILBOX), &cur_col, 50); + ahd_intctl_print(ahd_inb(ahd, INTCTL), &cur_col, 50); + ahd_seqintstat_print(ahd_inb(ahd, SEQINTSTAT), &cur_col, 50); ahd_saved_mode_print(ahd_inb(ahd, SAVED_MODE), &cur_col, 50); ahd_dffstat_print(ahd_inb(ahd, DFFSTAT), &cur_col, 50); ahd_scsisigi_print(ahd_inb(ahd, SCSISIGI), &cur_col, 50); @@ -8081,7 +8526,6 @@ ahd_dump_card_state(struct ahd_softc *ahd) ahd_seqintctl_print(ahd_inb(ahd, SEQINTCTL), &cur_col, 50); ahd_seq_flags_print(ahd_inb(ahd, SEQ_FLAGS), &cur_col, 50); ahd_seq_flags2_print(ahd_inb(ahd, SEQ_FLAGS2), &cur_col, 50); - ahd_ccscbctl_print(ahd_inb(ahd, CCSCBCTL), &cur_col, 50); ahd_sstat0_print(ahd_inb(ahd, SSTAT0), &cur_col, 50); ahd_sstat1_print(ahd_inb(ahd, SSTAT1), &cur_col, 50); ahd_sstat2_print(ahd_inb(ahd, SSTAT2), &cur_col, 50); @@ -8095,9 +8539,11 @@ ahd_dump_card_state(struct ahd_softc *ahd) ahd_lqostat1_print(ahd_inb(ahd, LQOSTAT1), &cur_col, 50); ahd_lqostat2_print(ahd_inb(ahd, LQOSTAT2), &cur_col, 50); printf("\n"); - printf("\nSCB Count = %d LASTSCB 0x%x CURRSCB 0x%x NEXTSCB 0x%x\n", - ahd->scb_data.numscbs, ahd_inw(ahd, LASTSCB), - ahd_inw(ahd, CURRSCB), ahd_inw(ahd, NEXTSCB)); + printf("\nSCB Count = %d CMDS_PENDING = %d LASTSCB 0x%x " + "CURRSCB 0x%x NEXTSCB 0x%x\n", + ahd->scb_data.numscbs, ahd_inw(ahd, CMDS_PENDING), + ahd_inw(ahd, LASTSCB), ahd_inw(ahd, CURRSCB), + ahd_inw(ahd, NEXTSCB)); cur_col = 0; /* QINFIFO */ ahd_search_qinfifo(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS, @@ -8115,7 +8561,7 @@ ahd_dump_card_state(struct ahd_softc *ahd) ahd_scb_scsiid_print(ahd_inb(ahd, SCB_SCSIID), &cur_col, 60); ahd_scb_tag_print(ahd_inb(ahd, SCB_TAG), &cur_col, 60); } - printf("\n"); + printf("\nTotal %d\n", i); printf("Kernel Free SCB list: "); i = 0; @@ -8197,7 +8643,7 @@ ahd_dump_card_state(struct ahd_softc *ahd) printf("\n"); cur_col = 0; } - cur_col += printf("SHADDR = 0x%x%x, SHCNT = 0x%x", + cur_col += printf("SHADDR = 0x%x%x, SHCNT = 0x%x ", ahd_inl(ahd, SHADDR+4), ahd_inl(ahd, SHADDR), (ahd_inb(ahd, SHCNT) @@ -8235,6 +8681,10 @@ ahd_dump_card_state(struct ahd_softc *ahd) ahd_inb(ahd, MAXCMDCNT)); ahd_simode0_print(ahd_inb(ahd, SIMODE0), &cur_col, 50); printf("\n"); + ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN); + cur_col = 0; + ahd_ccscbctl_print(ahd_inb(ahd, CCSCBCTL), &cur_col, 50); + printf("\n"); ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode); printf("%s: REG0 == 0x%x, SINDEX = 0x%x, DINDEX = 0x%x\n", ahd_name(ahd), ahd_inw(ahd, REG0), ahd_inw(ahd, SINDEX), diff --git a/sys/dev/aic7xxx/aic79xx.h b/sys/dev/aic7xxx/aic79xx.h index 2f9a47e..64a97dc 100644 --- a/sys/dev/aic7xxx/aic79xx.h +++ b/sys/dev/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#73 $ + * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#78 $ * * $FreeBSD$ */ @@ -97,6 +97,14 @@ struct scb_platform_data; SCB_GET_TARGET(ahd, scb) #define SCB_GET_TARGET_MASK(ahd, scb) \ (0x01 << (SCB_GET_TARGET_OFFSET(ahd, scb))) +#ifdef AHD_DEBUG +#define SCB_IS_SILENT(scb) \ + ((ahd_debug & AHD_SHOW_MASKED_ERRORS) == 0 \ + && (((scb)->flags & SCB_SILENT) != 0)) +#else +#define SCB_IS_SILENT(scb) \ + (((scb)->flags & SCB_SILENT) != 0) +#endif /* * TCLs have the following format: TTTTLLLLLLLL */ @@ -348,7 +356,9 @@ typedef enum { AHD_CURRENT_SENSING = 0x40000, AHD_SCB_CONFIG_USED = 0x80000,/* No SEEPROM but SCB had info. */ AHD_HP_BOARD = 0x100000, - AHD_RESET_POLL_ACTIVE = 0x200000 + AHD_RESET_POLL_ACTIVE = 0x200000, + AHD_UPDATE_PEND_CMDS = 0x400000, + AHD_RUNNING_QOUTFIFO = 0x800000 } ahd_flag; /************************* Hardware SCB Definition ***************************/ @@ -560,7 +570,13 @@ typedef enum { SCB_EXPECT_PPR_BUSFREE = 0x01000, SCB_PKT_SENSE = 0x02000, SCB_CMDPHASE_ABORT = 0x04000, - SCB_ON_COL_LIST = 0x08000 + SCB_ON_COL_LIST = 0x08000, + SCB_SILENT = 0x10000 /* + * Be quiet about transmission type + * errors. They are expected and we + * don't want to upset the user. This + * flag is typically used during DV. + */ } scb_flag; struct scb { @@ -707,7 +723,6 @@ struct ahd_tmode_lstate; #define AHD_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */ #define AHD_TRANS_GOAL 0x04 /* Modify negotiation goal */ #define AHD_TRANS_USER 0x08 /* Modify user negotiation settings */ -#define AHD_PERIOD_ASYNC 0xFF #define AHD_PERIOD_10MHz 0x19 #define AHD_WIDTH_UNKNOWN 0xFF @@ -757,7 +772,6 @@ struct ahd_tmode_tstate { /* * Points of interest along the negotiated transfer scale. */ -#define AHD_SYNCRATE_MAX 0x8 #define AHD_SYNCRATE_160 0x8 #define AHD_SYNCRATE_PACED 0x8 #define AHD_SYNCRATE_DT 0x9 @@ -768,6 +782,7 @@ struct ahd_tmode_tstate { #define AHD_SYNCRATE_SYNC 0x32 #define AHD_SYNCRATE_MIN 0x60 #define AHD_SYNCRATE_ASYNC 0xFF +#define AHD_SYNCRATE_MAX AHD_SYNCRATE_160 /* Safe and valid period for async negotiations. */ #define AHD_ASYNC_XFER_PERIOD 0x44 @@ -1050,6 +1065,16 @@ struct ahd_softc { * Timer handles for timer driven callbacks. */ ahd_timer_t reset_timer; + ahd_timer_t stat_timer; + + /* + * Statistics. + */ +#define AHD_STAT_UPDATE_US 250000 /* 250ms */ +#define AHD_STAT_BUCKETS 4 + u_int cmdcmplt_bucket; + uint32_t cmdcmplt_counts[AHD_STAT_BUCKETS]; + uint32_t cmdcmplt_total; /* * Card characteristics @@ -1094,6 +1119,12 @@ struct ahd_softc { uint8_t tqinfifonext; /* + * Cached verson of the hs_mailbox so we can avoid + * pausing the sequencer during mailbox updates. + */ + uint8_t hs_mailbox; + + /* * Incoming and outgoing message handling. */ uint8_t send_msg_perror; @@ -1141,6 +1172,22 @@ struct ahd_softc { /* Selection Timer settings */ int seltime; + /* + * Interrupt coalessing settings. + */ +#define AHD_INT_COALESSING_TIMER_DEFAULT 250 /*us*/ +#define AHD_INT_COALESSING_MAXCMDS_DEFAULT 10 +#define AHD_INT_COALESSING_MAXCMDS_MAX 127 +#define AHD_INT_COALESSING_MINCMDS_DEFAULT 5 +#define AHD_INT_COALESSING_MINCMDS_MAX 127 +#define AHD_INT_COALESSING_THRESHOLD_DEFAULT 2000 +#define AHD_INT_COALESSING_STOP_THRESHOLD_DEFAULT 1000 + u_int int_coalessing_timer; + u_int int_coalessing_maxcmds; + u_int int_coalessing_mincmds; + u_int int_coalessing_threshold; + u_int int_coalessing_stop_threshold; + uint16_t user_discenable;/* Disconnection allowed */ uint16_t user_tagenable;/* Tagged Queuing allowed */ }; @@ -1227,6 +1274,7 @@ extern const int ahd_num_aic7770_devs; /*************************** Function Declarations ****************************/ /******************************************************************************/ +void ahd_reset_cmds_pending(struct ahd_softc *ahd); u_int ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl); void ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int busyid); @@ -1260,6 +1308,12 @@ int ahd_default_config(struct ahd_softc *ahd); int ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc); void ahd_intr_enable(struct ahd_softc *ahd, int enable); +void ahd_update_coalessing_values(struct ahd_softc *ahd, + u_int timer, + u_int maxcmds, + u_int mincmds); +void ahd_enable_coalessing(struct ahd_softc *ahd, + int enable); void ahd_pause_and_flushwork(struct ahd_softc *ahd); int ahd_suspend(struct ahd_softc *ahd); int ahd_resume(struct ahd_softc *ahd); @@ -1282,6 +1336,7 @@ int ahd_wait_flexport(struct ahd_softc *ahd); /*************************** Interrupt Services *******************************/ void ahd_pci_intr(struct ahd_softc *ahd); void ahd_clear_intstat(struct ahd_softc *ahd); +void ahd_flush_qoutfifo(struct ahd_softc *ahd); void ahd_run_qoutfifo(struct ahd_softc *ahd); #ifdef AHD_TARGET_MODE void ahd_run_tqinfifo(struct ahd_softc *ahd, int paused); @@ -1405,7 +1460,8 @@ extern uint32_t ahd_debug; #define AHD_SHOW_QUEUE 0x02000 #define AHD_SHOW_TQIN 0x04000 #define AHD_SHOW_SG 0x08000 -#define AHD_DEBUG_SEQUENCER 0x10000 +#define AHD_SHOW_INT_COALESSING 0x10000 +#define AHD_DEBUG_SEQUENCER 0x20000 #endif void ahd_print_scb(struct scb *scb); void ahd_print_devinfo(struct ahd_softc *ahd, diff --git a/sys/dev/aic7xxx/aic79xx.reg b/sys/dev/aic7xxx/aic79xx.reg index 9949d3b..0138ca2 100644 --- a/sys/dev/aic7xxx/aic79xx.reg +++ b/sys/dev/aic7xxx/aic79xx.reg @@ -39,7 +39,7 @@ * * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#56 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#60 $" /* * This file is processed by the aic7xxx_asm utility for use in assembling @@ -173,6 +173,23 @@ register SEQINTCODE { STATUS_OVERRUN, CFG4OVERRUN, ENTERING_NONPACK, + TASKMGMT_FUNC_COMPLETE, /* + * Task management function + * request completed with + * an expected busfree. + */ + TASKMGMT_CMD_CMPLT_OKAY, /* + * A command with a non-zero + * task management function + * has completed via the normal + * command completion method + * for commands with a zero + * task management function. + * This happens when an attempt + * to abort a command loses + * the race for the command to + * complete normally. + */ TRACEPOINT0, TRACEPOINT1, TRACEPOINT2, @@ -265,16 +282,17 @@ register HESCB_QOFF { * Host Mailbox */ register HS_MAILBOX { - address 0x0B + address 0x00B access_mode RW mask HOST_TQINPOS 0x80 /* Boundary at either 0 or 128 */ + mask ENINT_COALESS 0x40 /* Perform interrupt coalessing */ } /* * Sequencer Interupt Status */ register SEQINTSTAT { - address 0x0C + address 0x00C access_mode RO field SEQ_SWTMRTO 0x10 field SEQ_SEQINT 0x08 @@ -287,7 +305,7 @@ register SEQINTSTAT { * Clear SEQ Interrupt */ register CLRSEQINTSTAT { - address 0x0C0 + address 0x00C access_mode WO field CLRSEQ_SWTMRTO 0x10 field CLRSEQ_SEQINT 0x08 @@ -300,7 +318,7 @@ register CLRSEQINTSTAT { * Software Timer */ register SWTIMER { - address 0x0E0 + address 0x00E access_mode RW size 2 } @@ -3106,21 +3124,6 @@ register RCVRBIASCALC { } /* - * Data FIFO Debug Control - */ -register DFDBCTL { - address 0x0C8 - access_mode RW - modes M_DFF0, M_DFF1 - field DFF_CIO_WR_RDY 0x20 - field DFF_CIO_RD_RDY 0x10 - field DFF_DIR_ERR 0x08 - field DFF_RAMBIST_FAIL 0x04 - field DFF_RAMBIST_DONE 0x02 - field DFF_RAMBIST_EN 0x01 -} - -/* * Data FIFO Backup Read Pointer * Contains the data FIFO address to be restored if the last * data accessed from the data FIFO was not transferred successfully. @@ -3142,6 +3145,21 @@ register SKEWCALC { } /* + * Data FIFO Debug Control + */ +register DFDBCTL { + address 0x0CB + access_mode RW + modes M_DFF0, M_DFF1 + field DFF_CIO_WR_RDY 0x20 + field DFF_CIO_RD_RDY 0x10 + field DFF_DIR_ERR 0x08 + field DFF_RAMBIST_FAIL 0x04 + field DFF_RAMBIST_DONE 0x02 + field DFF_RAMBIST_EN 0x01 +} + +/* * Data FIFO Space Count * Number of FIFO locations that are free. */ @@ -3226,7 +3244,8 @@ register SEQINTCTL { field INT1_CONTEXT 0x20 field SCS_SEQ_INT1M1 0x10 field SCS_SEQ_INT1M0 0x08 - field INTMASK 0x06 + field INTMASK2 0x04 + field INTMASK1 0x02 field IRET 0x01 } @@ -3687,6 +3706,53 @@ scratch_ram { } /* + * The maximum amount of time to wait, when interrupt coalessing + * is enabled, before issueing a CMDCMPLT interrupt for a completed + * command. + */ + INT_COALESSING_TIMER { + size 2 + } + + /* + * The maximum number of commands to coaless into a single interrupt. + * Actually the 2's complement of that value to simplify sequencer + * code. + */ + INT_COALESSING_MAXCMDS { + size 1 + } + + /* + * The minimum number of commands still outstanding required + * to continue coalessing (2's compliment of value). + */ + INT_COALESSING_MINCMDS { + size 1 + } + + /* + * Number of commands "in-flight". + */ + CMDS_PENDING { + size 2 + } + + /* + * The count of commands that have been coalessed. + */ + INT_COALESSING_CMDCOUNT { + size 1 + } + + /* + * Since the HS_MAIBOX is self clearing, copy its contents to + * this position in scratch ram every time it changes. + */ + LOCAL_HS_MAILBOX { + size 1 + } + /* * Target-mode CDB type to CDB length table used * in non-packetized operation. */ @@ -3861,6 +3927,13 @@ const SCB_TRANSFER_SIZE_1BYTE_LUN 48 const PKT_OVERRUN_BUFSIZE 512 /* + * Timer parameters. + */ +const AHD_TIMER_US_PER_TICK 25 +const AHD_TIMER_MAX_TICKS 0xFFFF +const AHD_TIMER_MAX_US (AHD_TIMER_MAX_TICKS * AHD_TIMER_US_PER_TICK) + +/* * Downloaded (kernel inserted) constants */ const SG_PREFETCH_CNT download diff --git a/sys/dev/aic7xxx/aic79xx.seq b/sys/dev/aic7xxx/aic79xx.seq index 804ab77..31cc29d 100644 --- a/sys/dev/aic7xxx/aic79xx.seq +++ b/sys/dev/aic7xxx/aic79xx.seq @@ -40,7 +40,7 @@ * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#73 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#78 $" PATCH_ARG_LIST = "struct ahd_softc *ahd" PREFIX = "ahd_" @@ -107,6 +107,16 @@ idle_loop_gsfifo_in_scsi_mode: good_status_IU_done: bmov SCBPTR, GSFIFO, 2; clr SCB_SCSI_STATUS; + /* + * If a command completed before an attempted task management + * function completed, notify the host after disabling any + * pending select-outs. + */ + test SCB_TASK_MANAGEMENT, 0xFF jz gsfifo_complete_normally; + test SSTAT0, SELDO|SELINGO jnz . + 2; + and SCSISEQ0, ~ENSELO; + SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY) +gsfifo_complete_normally: or SCB_CONTROL, STATUS_RCVD; /* @@ -164,6 +174,10 @@ idle_loop_next_fifo: idle_loop_cchan: SET_MODE(M_CCHAN, M_CCHAN) + test QOFF_CTLSTA, HS_MAILBOX_ACT jz hs_mailbox_empty; + mov LOCAL_HS_MAILBOX, HS_MAILBOX; + or QOFF_CTLSTA, HS_MAILBOX_ACT; +hs_mailbox_empty: BEGIN_CRITICAL; test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle; test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog; @@ -181,19 +195,65 @@ scbdma_tohost_done: and CCSCBCTL, ~(CCARREN|CCSCBEN) ret; fill_qoutfifo_dmadone: and CCSCBCTL, ~(CCARREN|CCSCBEN); - mvi INTSTAT, CMDCMPLT; + call qoutfifo_updated; mvi COMPLETE_SCB_DMAINPROG_HEAD[1], SCB_LIST_NULL; bmov QOUTFIFO_NEXT_ADDR, SCBHADDR, 4; test QOFF_CTLSTA, SDSCB_ROLLOVR jz return; bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4; xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret; +qoutfifo_updated: + /* + * If there are more commands waiting to be dma'ed + * to the host, always coaless. Otherwise honor the + * host's wishes. + */ + cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne coaless_by_count; + cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL jne coaless_by_count; + test LOCAL_HS_MAILBOX, ENINT_COALESS jz issue_cmdcmplt; + + /* + * If we have relatively few commands outstanding, don't + * bother waiting for another command to complete. + */ + test CMDS_PENDING[1], 0xFF jnz coaless_by_count; + /* Add -1 so that jnc means <= not just < */ + add A, -1, INT_COALESSING_MINCMDS; + add NONE, A, CMDS_PENDING; + jnc issue_cmdcmplt; + + /* + * If coalessing, only coaless up to the limit + * provided by the host driver. + */ +coaless_by_count: + mov A, INT_COALESSING_MAXCMDS; + add NONE, A, INT_COALESSING_CMDCOUNT; + jc issue_cmdcmplt; + /* + * If the timer is not currently active, + * fire it up. + */ + test INTCTL, SWTMINTMASK jz return; + bmov SWTIMER, INT_COALESSING_TIMER, 2; + mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO; + or INTCTL, SWTMINTEN|SWTIMER_START; + and INTCTL, ~SWTMINTMASK ret; + +issue_cmdcmplt: + mvi INTSTAT, CMDCMPLT; + clr INT_COALESSING_CMDCOUNT; + or INTCTL, SWTMINTMASK ret; + BEGIN_CRITICAL; 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; /* Update the next SCB address to download. */ bmov NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4; mvi SCB_NEXT[1], SCB_LIST_NULL; @@ -229,7 +289,6 @@ scbdma_idle: /* * Give precedence to downloading new SCBs to execute * unless select-outs are currently frozen. - * XXX Use a timer to prevent completion starvation. */ test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz . + 2; BEGIN_CRITICAL; @@ -251,6 +310,9 @@ fill_qoutfifo_loop: mov CCSCBRAM, SCBPTR; or CCSCBRAM, A, SCBPTR[1]; mov NONE, SDSCB_QOFF; + inc INT_COALESSING_CMDCOUNT; + add CMDS_PENDING, -1; + adc CMDS_PENDING[1], -1; cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done; cmp CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done; test QOFF_CTLSTA, SDSCB_ROLLOVR jnz fill_qoutfifo_done; @@ -419,7 +481,7 @@ select_in: /* * This exposes a window whereby a * busfree just after a selection will - * be missed, but there is not other safe + * be missed, but there is no other safe * way to enable busfree detection if * the busfreerev function is broken. */ @@ -545,7 +607,6 @@ select_out_inc_tid_q: cmp WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2; mvi WAITING_TID_TAIL[1], SCB_LIST_NULL; bmov SCBPTR, CURRSCB, 2; -END_CRITICAL; mvi CLRSINT0, CLRSELDO; test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase; test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase; @@ -554,17 +615,23 @@ END_CRITICAL; * If this is a packetized connection, return to our * idle_loop and let our interrupt handler deal with * any connection setup/teardown issues. The only - * exception is the case of MK_MESSAGE SCBs. In the - * A, the LQO manager transitions to LQOSTOP0 even if - * we have selected out with ATN asserted and the target - * REQs in a non-packet phase. + * exceptions are the case of MK_MESSAGE and task management + * SCBs. */ if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) { + /* + * In the A, the LQO manager transitions to LQOSTOP0 even if + * we have selected out with ATN asserted and the target + * REQs in a non-packet phase. + */ test SCB_CONTROL, MK_MESSAGE jz select_out_no_message; test SCSISIGO, ATNO jnz select_out_non_packetized; select_out_no_message: } - test LQOSTAT2, LQOSTOP0 jnz idle_loop; + test LQOSTAT2, LQOSTOP0 jz select_out_non_packetized; + test SCB_TASK_MANAGEMENT, 0xFF jz idle_loop; + SET_SEQINTCODE(TASKMGMT_FUNC_COMPLETE) + jmp idle_loop; select_out_non_packetized: /* Non packetized request. */ @@ -573,7 +640,7 @@ select_out_non_packetized: /* * This exposes a window whereby a * busfree just after a selection will - * be missed, but there is not other safe + * be missed, but there is no other safe * way to enable busfree detection if * the busfreerev function is broken. */ @@ -582,6 +649,8 @@ select_out_non_packetized: } mov SAVED_SCSIID, SCB_SCSIID; mov SAVED_LUN, SCB_LUN; + mvi SEQ_FLAGS, NO_CDB_SENT; +END_CRITICAL; or SXFRCTL0, SPIOEN; /* @@ -590,7 +659,6 @@ select_out_non_packetized: * asserted. */ mvi MSG_OUT, MSG_IDENTIFYFLAG; - mvi SEQ_FLAGS, NO_CDB_SENT; /* * Main loop for information transfer phases. Wait for the @@ -781,14 +849,17 @@ host_message_loop: jmp host_message_loop; mesgin_ign_wide_residue: + mov SAVED_MODE, MODE_PTR; + SET_MODE(M_SCSI, M_SCSI) shr NEGOADDR, 4, SAVED_SCSIID; - test NEGCONOPTS, WIDEXFER jz mesgin_reject; + mov A, NEGCONOPTS; + RESTORE_MODE(SAVED_MODE) + test A, WIDEXFER jz mesgin_reject; /* Pull the residue byte */ mvi REG0 call inb_next; cmp REG0, 0x01 jne mesgin_reject; test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2; test DATA_COUNT_ODD, 0x1 jz mesgin_done; - SET_SEQINTCODE(IGN_WIDE_RES) jmp mesgin_done; mesgin_proto_violation: @@ -946,7 +1017,7 @@ complete_nomsg: freeze_queue: /* Cancel any pending select-out. */ - test SSTAT0, SELDO jnz . + 2; + test SSTAT0, SELDO|SELINGO jnz . + 2; and SCSISEQ0, ~ENSELO; mov ACCUM_SAVE, A; clr A; @@ -1481,11 +1552,28 @@ sgptr_fixup_done: clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */ bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret; +export timer_isr: + call issue_cmdcmplt; + mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO; + if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { + /* + * In H2A4, the mode pointer is not saved + * for intvec2, but is restored on iret. + * This can lead to the restoration of a + * bogus mode ptr. Manually clear the + * intmask bits and do a normal return + * to compensate. + */ + and SEQINTCTL, ~(INTMASK2|INTMASK1) ret; + } else { + or SEQINTCTL, IRET ret; + } + export seq_isr: nop; /* Jumps in the first ISR instruction fail on Rev A. */ - test SEQINTSRC, SAVEPTRS jnz saveptr_intr; test SEQINTSRC, CFG4DATA jnz cfg4data_intr; test SEQINTSRC, CFG4ISTAT jnz cfg4istat_intr; + test SEQINTSRC, SAVEPTRS jnz saveptr_intr; test SEQINTSRC, CFG4ICMD jnz cfg4icmd_intr; SET_SEQINTCODE(INVALID_SEQINT) @@ -1536,7 +1624,12 @@ cfg4istat_setup_handler: /* * Status pkt is transferring to host. * Wait in idle loop for transfer to complete. + * If a command completed before an attempted + * task management function completed, notify the host. */ + test SCB_TASK_MANAGEMENT, 0xFF jz cfg4istat_no_taskmgmt_func; + SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY) +cfg4istat_no_taskmgmt_func: call pkt_handle_status; or SEQINTCTL, IRET ret; @@ -1790,7 +1883,7 @@ load_overrun_buf: /* PKT_OVERRUN_BUFSIZE is a multiple of 256 */ clr HCNT[0]; mvi HCNT[1], ((PKT_OVERRUN_BUFSIZE >> 8) & 0xFF); - clr HCNT[2]; + clr HCNT[2] ret; } cfg4icmd_intr: diff --git a/sys/dev/aic7xxx/aic79xx_inline.h b/sys/dev/aic7xxx/aic79xx_inline.h index 4281145..e417014 100644 --- a/sys/dev/aic7xxx/aic79xx_inline.h +++ b/sys/dev/aic7xxx/aic79xx_inline.h @@ -2,7 +2,7 @@ * Inline routines shareable across OS platforms. * * Copyright (c) 1994-2001 Justin T. Gibbs. - * Copyright (c) 2000-2002 Adaptec Inc. + * Copyright (c) 2000-2003 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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_inline.h#39 $ + * $Id: //depot/aic7xxx/aic7xxx/aic79xx_inline.h#41 $ * * $FreeBSD$ */ @@ -217,8 +217,11 @@ ahd_unpause(struct ahd_softc *ahd) * prior to the first change of the mode. */ if (ahd->saved_src_mode != AHD_MODE_UNKNOWN - && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) + && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) { + if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0) + ahd_reset_cmds_pending(ahd); ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode); + } if ((ahd_inb(ahd, INTSTAT) & ~(SWTMINT | CMDCMPLT)) == 0) ahd_outb(ahd, HCNTRL, ahd->unpause); @@ -269,7 +272,6 @@ ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb) if ((scb->flags & SCB_PACKETIZED) != 0) { /* XXX what about ACA?? It is type 4, but TAG_TYPE == 0x3. */ scb->hscb->task_attribute= scb->hscb->control & SCB_TAG_TYPE; - scb->hscb->task_management = 0; /* * For Rev A short lun workaround. */ @@ -913,6 +915,8 @@ ahd_intr(struct ahd_softc *ahd) ahd_flush_device_writes(ahd); } ahd_run_qoutfifo(ahd); + ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]++; + ahd->cmdcmplt_total++; #ifdef AHD_TARGET_MODE if ((ahd->flags & AHD_TARGETROLE) != 0) ahd_run_tqinfifo(ahd, /*paused*/FALSE); diff --git a/sys/dev/aic7xxx/aic79xx_osm.c b/sys/dev/aic7xxx/aic79xx_osm.c index bfb4027..7e4a8d9 100644 --- a/sys/dev/aic7xxx/aic79xx_osm.c +++ b/sys/dev/aic7xxx/aic79xx_osm.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/aic7xxx/freebsd/dev/aic7xxx/aic79xx_osm.c#24 $ + * $Id: //depot/aic7xxx/freebsd/dev/aic7xxx/aic79xx_osm.c#25 $ * * $FreeBSD$ */ @@ -465,6 +465,7 @@ ahd_action(struct cam_sim *sim, union ccb *ccb) hscb->cdb_len = 0; scb->flags |= SCB_DEVICE_RESET; hscb->control |= MK_MESSAGE; + hscb->task_management = SIU_TASKMGMT_LUN_RESET; ahd_execute_scb(scb, NULL, 0, 0); } else { #ifdef AHD_TARGET_MODE @@ -488,6 +489,7 @@ ahd_action(struct cam_sim *sim, union ccb *ccb) ahd_htole16(ccb->csio.tag_id); } #endif + hscb->task_management = 0; if (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) hscb->control |= ccb->csio.tag_action; @@ -1112,8 +1114,11 @@ ahd_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) scb->hscb->control |= DISCENB; - if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) + if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { scb->flags |= SCB_PACKETIZED; + if (scb->hscb->task_management != 0) + scb->hscb->control &= ~MK_MESSAGE; + } if ((ccb->ccb_h.flags & CAM_NEGOTIATE) != 0 && (tinfo->goal.width != 0 diff --git a/sys/dev/aic7xxx/aic79xx_pci.c b/sys/dev/aic7xxx/aic79xx_pci.c index 7dd68a3..79b1a17 100644 --- a/sys/dev/aic7xxx/aic79xx_pci.c +++ b/sys/dev/aic7xxx/aic79xx_pci.c @@ -38,7 +38,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_pci.c#60 $ + * $Id: //depot/aic7xxx/aic7xxx/aic79xx_pci.c#61 $ * * $FreeBSD$ */ @@ -379,15 +379,20 @@ int ahd_pci_test_register_access(struct ahd_softc *ahd) { ahd_mode_state saved_modes; + uint32_t cmd; int error; - uint8_t seqctl; + uint8_t hcntrl; saved_modes = ahd_save_modes(ahd); error = EIO; - /* Enable PCI error interrupt status */ - seqctl = ahd_inb(ahd, SEQCTL0); - ahd_outb(ahd, SEQCTL0, seqctl & ~FAILDIS); + /* + * Enable PCI error interrupt status, but suppress NMIs + * generated by SERR raised due to target aborts. + */ + cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2); + ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, + cmd & ~PCIM_CMD_SERRESPEN, /*bytes*/2); /* * First a simple test to see if any @@ -397,7 +402,8 @@ ahd_pci_test_register_access(struct ahd_softc *ahd) * be zero so it is a good register to * use for this test. */ - if (ahd_inb(ahd, HCNTRL) == 0xFF) + hcntrl = ahd_inb(ahd, HCNTRL); + if (hcntrl == 0xFF) goto fail; /* @@ -407,6 +413,10 @@ ahd_pci_test_register_access(struct ahd_softc *ahd) * either, so look for data corruption and/or flaged * PCI errors. */ + ahd_outb(ahd, HCNTRL, hcntrl|PAUSE); + while (ahd_is_paused(ahd) == 0) + ; + ahd_outb(ahd, SEQCTL0, PERRORDIS); ahd_outl(ahd, SRAM_BASE, 0x5aa555aa); if (ahd_inl(ahd, SRAM_BASE) != 0x5aa555aa) goto fail; @@ -440,7 +450,8 @@ fail: } ahd_restore_modes(ahd, saved_modes); - ahd_outb(ahd, SEQCTL0, seqctl); + ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS); + ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, cmd, /*bytes*/2); return (error); } |