summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/aic7xxx/aic79xx.c640
-rw-r--r--sys/dev/aic7xxx/aic79xx.h68
-rw-r--r--sys/dev/aic7xxx/aic79xx.reg115
-rw-r--r--sys/dev/aic7xxx/aic79xx.seq127
-rw-r--r--sys/dev/aic7xxx/aic79xx_inline.h12
-rw-r--r--sys/dev/aic7xxx/aic79xx_osm.c9
-rw-r--r--sys/dev/aic7xxx/aic79xx_pci.c25
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);
}
OpenPOWER on IntegriCloud