summaryrefslogtreecommitdiffstats
path: root/sys/dev/aic7xxx
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>2003-01-20 20:17:35 +0000
committergibbs <gibbs@FreeBSD.org>2003-01-20 20:17:35 +0000
commita516e1442e01e83c84553dc725794e64b6274c17 (patch)
treec9cf60d32292c09a58bb1cbcec361535141d8108 /sys/dev/aic7xxx
parent091797e7e50e59c126583724131f99db3b7b6815 (diff)
downloadFreeBSD-src-a516e1442e01e83c84553dc725794e64b6274c17.zip
FreeBSD-src-a516e1442e01e83c84553dc725794e64b6274c17.tar.gz
aic79xx.c:
Implement the SCB_SILENT flag. This is useful for hushing up the driver during DV or other operations that we expect to cause transmission errors. The messages will still print if the SHOW_MASKED_ERRORS debug option is enabled. Save and restore the NEGOADDR address when setting new transfer settings. The sequencer performs lookups in the negotiation table too and it expects NEGOADDR to remain consistent across pause/unpause sessions. Consistently use "offset" instead of "period" to determine if we are running sync or not. Add a SHOW_MESSAGES diagnostic for when we assert ATN during message processing. Print out IU, QAS, and RTI features when showing transfer options. Limit the syncrate after all option conformance changes have taken place in ahd_devlimited_syncrate. Changes in options may change the final syncrate we accept. Keep a copy of the hs_mailbox in our softc so that we can perform read/modify/write operations on the hs_mailbox without having to pause the sequencer to read the last written value. Use the ENINT_COALESS flag in the hs_mailbox to toggle interrupt coalessing. Add entrypoints for enabling interrupt coalessing and setting both a timeout (how long to wait for commands to be coalessed) and a maximum commands to coaless value. Add a statistics timer that decides when to enable or disable interrupt coalessing based on load. Add a routine, ahd_reset_cmds_pending() which is used to update the CMDS_PENDING sequencer variable whenever error recovery compeltes SCBs without notifying the sequencer. Since ahd_reset_cmds_pending is called during ahd_unpause() only if we've aborted SCBs, its call to ahd_flush_qoutfifo should not cause recursion through ahd_run_qoutfifo(). A panic has been added to ensure that this recursion does not occur. In ahd_search_qinfifo, update the CMDS_PENDING sequencer variable directly. ahd_search_qinififo can be called in situations where using ahd_reset_cmds_pending() might cause recursion. Since we can safely determine the exact number to reduce CMDS_PENDING by in this scenario without running the qoutfifo, the manual update is sufficient. Clean up diagnostics. Add ahd_flush_qoutfifo() which will run the qoutfifo as well as complete any commands sitting on the sequencer's COMPLETE_SCB lists or the good status FIFO. Use this routine in several places that did similar things in an add-hoc, but incomplete, fashion. A call to this routine was also added to ahd_abort_scbs() to close a race. In ahd_pause_and_flushwork() only return once selections are safely disabled. Flush all completed commands via ahd_flush_qoutfifo(). Remove "Now packetized" diagnostic now that this information is incorperated into the actual negotiation messages that are displayed. When forcing renegotiation, don't clober the current ppr_options. Much of the driver uses this information to determine if we are currently packetized or not. Remove some stray spaces at column 1 in ahd_set_tags. When complaining about getting a host message loop request with no pending messages, print out the SCB_CONTROL register down on the card. Modify the ahd_sent_msg() routine to handle a search for an outgoing identify message. Use this to detect a msg reject on an identify message which typically indicates that the target thought we were packetized. Force a renegotiation in this case. In ahd_search_qinfifo(), wait more effectively for SCB DMA activities to cease. We also disable SCB fetch operations since we are about to change the qinfifo and any fetch in progress will likely be invalidated. In ahd_qinfifo_count(), fix the qinfifo empty case. In ahd_dump_card_state(), print out CCSCBCTL in the correct mode. If we are a narrow controller, don't set the current width to unknown when forcing a future negotiation. This just confuses the code into attempting a wide negotiation on a narrow bus. Add support for task management function completions. Modify ahd_handle_devreset so that it can handle lun resets in addition to target resets. Use ahd_handle_devreset for lun and target reset task management functions. Handle the abort task TMF race case better. We now wait until any current selections are over and then set the TMF back to zero. This should cause the sequencer to ignore the abort TMF completion should it occur. Correct a bug in the illegal phase handler that caused us to drop down to narrow when handling the unexpected command phase case after 3rd party reset of a packetized device. Indicate the features, bugs, and flags set in the softc that are used to control firmware patch download when booting verbose. aic79xx.h: Add coalessing and HS_MAILBOX fields. Add per-softc variables for the stats "daemon". Add a debug option for interrupt coalessing activities. Add two new softc flags: o AHD_UPDATE_PEND_CMDS Run ahd_reset_cmds_pending() on the next unpause. o AHD_RUNNING_QOUTFIFO Used to catch recursion through ahd_run_qoutfifo(). aic79xx.reg: Correct register addresses related to the software timer and the DFDBCTL register. Add constants paramaterizing the software timer. Add scratch ram locations for storing interrupt coalessing tunables. Break INTMASK in SEQITNCTL out into INTMASK1 and INTMASK2. In at least the REV A, these are writable bits. We make use of that for a swtimer workaround in the sequencer. Since HS_MAILBOX autoclears, provide a sequencer variable to store its contents. Add SEQINT codes for handling task management completions. aic79xx.seq: Correct ignore wide residue processing check for a wide negotiation being in effect. We must be in the SCSI register window in order to access the negotiation table. Use the software timer and a commands completed count to implement interrupt coalessing. The command complete is deferred until either the maximum command threshold or a the expiration of a command deferral timer. If we have more SCBs to complete to the host (sitting in COMPLETE_SCB lists), always try to coaless them up to our coalessing limit. If coalessing is enabled, but we have fewer commands oustanting than the host's min coalessing limit, complete the command immediately. Add code to track the number of commands outstanding. Commands are outstanding from the time they are placed into the execution queue until the DMA to post completion is setup. Add a workaround for intvec_2 interrupts on the H2A4. 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. We use intvec_2 to track interrupt coalessing timeouts. Since we cannot disable the swtimer's countdown, simply mask its interrupt once we no longer care about it firing. In idle_loop_cchan, update LOCAL_HS_MAILBOX everytime we are notified of an HS_MAILBOX update via the HS_MAILBOX_ACT bit in QOFF_CTLSTA. We have to use a local copy of persistant portions of the HS_MAILBOX as the mailbox auto-clears on any read. Move the test for the cfg4istat interrupt up an instruction to hopefully close a race between the next outgoing selection and our disabling of selections. Add a missing ret to the last instruction in load_overrun_buf. Add notifications to the host of task management completions as well as the completions for commands that completed successfully before their corresponding TMF could be sent. Hold a critical section during select-out processing until we have a fully identified connection. This removes a race condition with the legacy abort handler. Correct a few spelling errors in some comments. aic79xx_inline.h: Call ahd_reset_cmds_pending() in ahd_unpause if required. Update cmdcmplt interrupt statistics in our interrupt handler. Allow callers to ahd_send_scb() to set the task management function. aic79xx_pci.c: Disable SERR and pause the controller prior to performing our mmapped I/O test. The U320 controllers do not support "auto-access-pause". aic79xx_osm.c: Set the task management function now that ahd_send_scb() doesn't do it for us. We also perform a lun reset in response to BDR requests to packetized devices.
Diffstat (limited to 'sys/dev/aic7xxx')
-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