1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-18 10:35:55 +00:00

Clean up error recovery code:

aic7xxx.c:
	In target mode, reset the TQINPOS on every restart of the sequencer.
	In the past we did this only during a bus reset, but there are other
	reasons the sequencer might be reset.

	In ahc_clear_critical_section(), disable pausing chip interrupts while
	we step the sequencer out of a critical section.  This avoids the
	possibility of getting a pausing interrupt (unexpected bus free,
	bus reset, etc.) that would prevent the sequencer from stepping.

	Send the correct async notifications in the case of a BDR or bus reset.

	In ahc_loadseq(), correct the calculation of our critical sections.
	In some cases, the sections would be larger than needed.

aic7xxx.h:
	Remove an unused SCB flag.

aic7xxx.seq:
	MK_MESSAGE is cleared by the kernel, there is no need to waste
	a sequencer instruction clearing it.

aic7xxx_freebsd.c:
	Go through the host message loop instead of issuing a single
	byte message directly in the ahc_timeout() case where we
	are currently on the bus to the device.  The effect is the same,
	but this way we get a nice printf saying that an expected BDR
	was delivered instead of an unexpected bus free.

	If we are requeuing an SCB for an error recovery action, be sure
	to set the DISCONNECTED flag in the in-core version of the SCB.
	This ensures that, in the SCB-paging case, the sequencer will
	still recognize the reselection as valid even if the version
	of the SCB with this flag set was never previously paged out
	to system memory.  In the non-paging case, set the MK_MESSAGE
	flag in SCB_CONTROL directly.

aic7xxx_pci.c:
	Enable the Memeory Write and Invalidate bug workaround for
	all aic7880 chips with revs < 1.  This bug is rarely triggered
	in FreeBSD as most transfers end on cache-aligned boundaries,
	but a recheck of my references indicates that these chips
	are affected.
This commit is contained in:
Justin T. Gibbs 2000-10-09 01:46:01 +00:00
parent 03de26e0d0
commit 73f1c25915
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=66845
6 changed files with 117 additions and 68 deletions

View File

@ -229,6 +229,15 @@ restart_sequencer(struct ahc_softc *ahc)
ahc_outb(ahc, SCSISIGO, 0); /* De-assert BSY */
ahc_outb(ahc, MSG_OUT, MSG_NOOP); /* No message to send */
ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
/*
* Ensure that the sequencer's idea of TQINPOS
* matches our own. The sequencer increments TQINPOS
* only after it sees a DMA complete and a reset could
* occur before the increment leaving the kernel to believe
* the command arrived but the sequencer to not.
*/
ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
/* Always allow reselection */
ahc_outb(ahc, SCSISEQ,
ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
@ -1045,11 +1054,15 @@ void
ahc_clear_critical_section(struct ahc_softc *ahc)
{
int stepping;
u_int simode0;
u_int simode1;
if (ahc->num_critical_sections == 0)
return;
stepping = FALSE;
simode0 = 0;
simode1 = 0;
for (;;) {
struct cs *cs;
u_int seqaddr;
@ -1068,7 +1081,19 @@ ahc_clear_critical_section(struct ahc_softc *ahc)
if (i == ahc->num_critical_sections)
break;
if (!stepping) {
if (stepping == FALSE) {
/*
* Disable all interrupt sources so that the
* sequencer will not be stuck by a pausing
* interrupt condition while we attempt to
* leave a critical section.
*/
simode0 = ahc_inb(ahc, SIMODE0);
ahc_outb(ahc, SIMODE0, 0);
simode1 = ahc_inb(ahc, SIMODE1);
ahc_outb(ahc, SIMODE1, 0);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
stepping = TRUE;
}
@ -1077,8 +1102,11 @@ ahc_clear_critical_section(struct ahc_softc *ahc)
ahc_delay(200);
} while (!sequencer_paused(ahc));
}
if (stepping)
if (stepping) {
ahc_outb(ahc, SIMODE0, simode0);
ahc_outb(ahc, SIMODE1, simode1);
ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
}
}
/*
@ -2938,7 +2966,7 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
AHC_TRANS_CUR, /*paused*/TRUE);
ahc_send_async(ahc, devinfo->channel, devinfo->target,
CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
CAM_LUN_WILDCARD, AC_SENT_BDR);
if (message != NULL
&& (verbose_level <= bootverbose))
@ -4818,31 +4846,6 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
if (initiate_reset)
ahc_reset_current_bus(ahc);
ahc_clear_intstat(ahc);
/*
* Since we are going to restart the sequencer, avoid
* a race in the sequencer that could cause corruption
* of our Q pointers by starting over from index 1.
*/
*((uint32_t *)(&ahc->qoutfifo[ahc->qoutfifonext & ~0x3]))
= 0xFFFFFFFF;
ahc->qoutfifonext = 0;
if ((ahc->features & AHC_QUEUE_REGS) != 0)
ahc_outb(ahc, SDSCB_QOFF, 0);
else
ahc_outb(ahc, QOUTPOS, 0);
if ((ahc->flags & AHC_TARGETMODE) != 0) {
ahc->tqinfifonext = 1;
ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
if ((ahc->features & AHC_HS_MAILBOX) != 0) {
u_int hs_mailbox;
hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
hs_mailbox &= ~HOST_TQINPOS;
ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
}
}
restart_needed = TRUE;
}
@ -4883,7 +4886,7 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
#endif
/* Notify the XPT that a bus reset occurred */
ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
CAM_LUN_WILDCARD, AC_BUS_RESET);
/*
* Revert to async/narrow transfers until we renegotiate.
@ -5122,8 +5125,10 @@ static void
ahc_loadseq(struct ahc_softc *ahc)
{
struct cs cs_table[num_critical_sections];
u_int begin_set[num_critical_sections];
u_int end_set[num_critical_sections];
struct patch *cur_patch;
u_int cs_table_size;
u_int cs_count;
u_int cur_cs;
u_int i;
int downloaded;
@ -5135,8 +5140,10 @@ ahc_loadseq(struct ahc_softc *ahc)
* Start out with 0 critical sections
* that apply to this firmware load.
*/
cs_table_size = 0;
cs_count = 0;
cur_cs = 0;
memset(begin_set, 0, sizeof(begin_set));
memset(end_set, 0, sizeof(end_set));
/* Setup downloadable constant table */
download_consts[QOUTFIFO_OFFSET] = 0;
@ -5172,32 +5179,34 @@ ahc_loadseq(struct ahc_softc *ahc)
* that might apply to this instruction.
*/
for (; cur_cs < num_critical_sections; cur_cs++) {
if (critical_sections[cur_cs].end >= i) {
if (critical_sections[cur_cs].begin == i) {
cs_table[cs_table_size].begin =
downloaded;
if (critical_sections[cur_cs].end <= i) {
if (begin_set[cs_count] == TRUE
&& end_set[cs_count] == FALSE) {
cs_table[cs_count].end = downloaded;
end_set[cs_count] = TRUE;
cs_count++;
}
if (critical_sections[cur_cs].end == i) {
cs_table[cs_table_size].end =
downloaded;
cs_table_size++;
}
break;
continue;
}
if (critical_sections[cur_cs].begin <= i
&& begin_set[cs_count] == FALSE) {
cs_table[cs_count].begin = downloaded;
begin_set[cs_count] = TRUE;
}
break;
}
ahc_download_instr(ahc, i, download_consts);
downloaded++;
}
ahc->num_critical_sections = cs_table_size;
if (cs_table_size != 0) {
ahc->num_critical_sections = cs_count;
if (cs_count != 0) {
cs_table_size *= sizeof(struct cs);
ahc->critical_sections =
malloc(cs_table_size, M_DEVBUF, M_NOWAIT);
cs_count *= sizeof(struct cs);
ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
if (ahc->critical_sections == NULL)
panic("ahc_loadseq: Could not malloc");
memcpy(ahc->critical_sections, cs_table, cs_table_size);
memcpy(ahc->critical_sections, cs_table, cs_count);
}
ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
restart_sequencer(ahc);

View File

@ -475,7 +475,6 @@ typedef enum {
SCB_RECOVERY_SCB = 0x0040,
SCB_NEGOTIATE = 0x0080,
SCB_ABORT = 0x1000,
SCB_QUEUED_MSG = 0x2000,
SCB_ACTIVE = 0x4000,
SCB_TARGET_IMMEDIATE = 0x8000
} scb_flag;

View File

@ -1624,7 +1624,6 @@ setup_SCB_tagged:
call set_transfer_settings;
/* See if the host wants to send a message upon reconnection */
test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
and SCB_CONTROL, ~MK_MESSAGE;
mvi HOST_MSG call mk_mesg;
jmp mesgin_done;

View File

@ -1542,7 +1542,7 @@ ahc_timeout(void *arg)
}
ahc_set_recoveryscb(ahc, active_scb);
ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET);
ahc_outb(ahc, MSG_OUT, HOST_MSG);
ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
ahc_print_path(ahc, active_scb);
printf("BDR message in message buffer\n");
@ -1580,13 +1580,6 @@ ahc_timeout(void *arg)
struct scb *prev_scb;
ahc_set_recoveryscb(ahc, scb);
/*
* Simply set the MK_MESSAGE control bit.
*/
scb->hscb->control |= MK_MESSAGE;
scb->flags |= SCB_QUEUED_MSG
| SCB_DEVICE_RESET;
/*
* Actually re-queue this SCB in an attempt
* to select the device before it reconnects.
@ -1594,6 +1587,19 @@ ahc_timeout(void *arg)
* we will now issue a target reset to the
* timed-out device.
*
* Set the MK_MESSAGE control bit indicating
* that we desire to send a message. We
* also set the disconnected flag since
* in the paging case there is no guarantee
* that our SCB control byte matches the
* version on the card. We don't want the
* sequencer to abort the command thinking
* an unsolicited reselection occurred.
*/
scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
scb->flags |= SCB_DEVICE_RESET;
/*
* Remove any cached copy of this SCB in the
* disconnected list in preparation for the
* queuing of our abort SCB. We use the
@ -1604,7 +1610,21 @@ ahc_timeout(void *arg)
lun, scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/TRUE,
/*save_state*/TRUE);
/*save_state*/FALSE);
/*
* In the non-paging case, the sequencer will
* never re-reference the in-core SCB.
* To make sure we are notified during
* reslection, set the MK_MESSAGE flag in
* the card's copy of the SCB.
*/
if ((ahc->flags & AHC_PAGESCBS) != 0) {
ahc_outb(ahc, SCBPTR, scb->hscb->tag);
ahc_outb(ahc, SCB_CONTROL,
ahc_inb(ahc, SCB_CONTROL)
| MK_MESSAGE);
}
/*
* Clear out any entries in the QINFIFO first
@ -1630,6 +1650,7 @@ ahc_timeout(void *arg)
prev_tag);
}
ahc_qinfifo_requeue(ahc, prev_scb, scb);
ahc_outb(ahc, SCBPTR, active_scb_index);
scb->io_ctx->ccb_h.timeout_ch =
timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
unpause_sequencer(ahc);

View File

@ -1542,7 +1542,7 @@ ahc_timeout(void *arg)
}
ahc_set_recoveryscb(ahc, active_scb);
ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET);
ahc_outb(ahc, MSG_OUT, HOST_MSG);
ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
ahc_print_path(ahc, active_scb);
printf("BDR message in message buffer\n");
@ -1580,13 +1580,6 @@ ahc_timeout(void *arg)
struct scb *prev_scb;
ahc_set_recoveryscb(ahc, scb);
/*
* Simply set the MK_MESSAGE control bit.
*/
scb->hscb->control |= MK_MESSAGE;
scb->flags |= SCB_QUEUED_MSG
| SCB_DEVICE_RESET;
/*
* Actually re-queue this SCB in an attempt
* to select the device before it reconnects.
@ -1594,6 +1587,19 @@ ahc_timeout(void *arg)
* we will now issue a target reset to the
* timed-out device.
*
* Set the MK_MESSAGE control bit indicating
* that we desire to send a message. We
* also set the disconnected flag since
* in the paging case there is no guarantee
* that our SCB control byte matches the
* version on the card. We don't want the
* sequencer to abort the command thinking
* an unsolicited reselection occurred.
*/
scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
scb->flags |= SCB_DEVICE_RESET;
/*
* Remove any cached copy of this SCB in the
* disconnected list in preparation for the
* queuing of our abort SCB. We use the
@ -1604,7 +1610,21 @@ ahc_timeout(void *arg)
lun, scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/TRUE,
/*save_state*/TRUE);
/*save_state*/FALSE);
/*
* In the non-paging case, the sequencer will
* never re-reference the in-core SCB.
* To make sure we are notified during
* reslection, set the MK_MESSAGE flag in
* the card's copy of the SCB.
*/
if ((ahc->flags & AHC_PAGESCBS) != 0) {
ahc_outb(ahc, SCBPTR, scb->hscb->tag);
ahc_outb(ahc, SCB_CONTROL,
ahc_inb(ahc, SCB_CONTROL)
| MK_MESSAGE);
}
/*
* Clear out any entries in the QINFIFO first
@ -1630,6 +1650,7 @@ ahc_timeout(void *arg)
prev_tag);
}
ahc_qinfifo_requeue(ahc, prev_scb, scb);
ahc_outb(ahc, SCBPTR, active_scb_index);
scb->io_ctx->ccb_h.timeout_ch =
timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
unpause_sequencer(ahc);

View File

@ -1711,7 +1711,7 @@ ahc_aic7880_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
if (rev >= 1) {
probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG;
} else {
probe_config->bugs |= AHC_CACHETHEN_BUG;
probe_config->bugs |= AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
}
return (0);
}