Correct spelling errors.

Switch to handling bad SCSI status as a sequencer interrupt
instead of having the kernel proccess these failures via
the completion queue.  This is done because:

 o The old scheme required us to pause the sequencer and clear
   critical sections for each SCB.  It seems that these pause
   actions, if coincident with a sequencer FIFO interrupt, would
   result in a FIFO interrupt getting lost or directing to the
   wrong FIFO.  This caused hangs when the driver was stressed
   under high "queue full" loads.
 o The completion code assumed that it was always called with
   the sequencer running.  This may not be the case in timeout
   processing where completions occur manually via
   ahd_pause_and_flushwork().
 o With this scheme, the extra expense of clearing critical
   sections is avoided since the sequencer will only self pause
   once all pending selections have cleared and it is not in
   a critical section.

  aic79xx.c
	Add code to handle the new BAD_SCB_STATUS sequencer
	interrupt code.  This just redirects the SCB through
	the already existing ahd_complete_scb() code path.
	Remove code in ahd_handle_scsi_status() that paused
	the sequencer, made sure that no selections where
	pending, and cleared critical sections.  Bad
	status SCBs are now only processed when all of these
	conditions are true.

  aic79xx.reg:
	Add the BAD_SCB_STATUS sequencer interrupt code.

  aic79xx.seq:
	When completing an SCB upload to the host, if
	we are doing this because the SCB contains non-zero
	SCSI status, defer completing the SCB until there
	are no pending selection events.  When completing
	these SCBs, use the new BAD_SCB_STATUS sequencer
	interrupt.  For all other uploaded SCBs (currently
	only for underruns), the SCB is completed via the
	normal done queue.  Additionally, keep the SCB that
	is currently being uploaded on the COMPLETE_DMA_SCB
	list until the dma is completed, not just until the
	DMA is started.  This ensures that the DMA is restarted
	properly should the host disable the DMA transfer for
	some reason.

	In our RevA workaround for Maxtor drives, guard against
	the host pausing us while trying to pause I/O until the
	first data-valid REQ by clearing the current snapshot
	so that we can tell if the transfer has completed prior
	to us noticing the REQINIT status.

	In cfg4data_intr, shave off an instruction before getting
	the data path running by adding an entrypoint to the
	overrun handler to also increment the FIFO use count.

	In the overrun handler, be sure to clear our LONGJMP
	address in both exit paths.

Perform a few sequencer optimizations.

  aic79xx.c:
	Print the full path from the SCB when a packetized
	status overrun occurs.

	Remove references to LONGJMP_SCB which is being
	removed from firmware usage.

	Print the new SCB_FIFO_USE_COUNT field in the
	per-SCB section of ahd_dump_card_state().  The
	SCB_TAG field is now re-used by the sequencer,
	so it no longer makes sense to reference this
	field in the kernel driver.

  aic79xx.h:
	Re-arrange fields in the hardware SCB from largest
	size type to smallest.  This makes it easier to
	move fields without changing field alignment.

	The hardware scb tag field is now down near the
	"spare" portion of the SCB to facilitate reuse
	by the sequencer.

  aic79xx.reg:
	Remove LONGJMP_ADDR.

	Rearrange SCB fields to match aic79xx.h.
	Add SCB_FIFO_USE_COUNT as the first byte
	of the SCB_TAG field.

  aic79xx.seq:
	Add a per-SCB "Fifos in use count" field and use
	it to determine when it is safe (all data posted)
	to deliver status back to the host.  The old method
	involved polling one or both FIFOs to verify that
	the current task did not have pending data.  This
	makes running down the GSFIFO very cheap, so we
	will empty the GSFIFO in one idle loop pass in
	all cases.

	Use this simplification of the completion process
	to prune down the data FIFO teardown sequencer for
	packetized transfers.  Much more code is now shared
	between the data residual and transfer complete cases.

	Correct some issues in the packetized status handler.
	It used to be possible to CLRCHN our FIFO before status
	had fully transferred to the host.  We also failed to
	handle NONPACKREQ phases that could occur should a CRC
	error occur during transmission of the status data packet.

Correct a few big endian issues:

  aic79xx.c:
  aic79xx_inline.h:
  aic79xx_pci.c:
  aic79xx_osm.c:
	o Always get the SCB's tag via the SCB_GET_TAG acccessor
	o Add missing use of byte swapping macros when touching
	  hscb fields.
	o Don't double swap SEEPROM data when it is printed.
	  Correct a big-endian bug.  We cannot assign a
	o When assigning a 32bit LE variable to a 64bit LE
	  variable, we must be explict about how the words
	  of the 64bit LE variable are initialized.  Cast to
	  (uint32_t*) to do this.

aic79xx.c:
	In ahd_clear_critical_section(), hit CRLSCSIINT
	after restoring the interrupt masks to avoid what
	appears to be a glitch on SCSIINT.  Any real SCSIINT
	status will be persistent and will immidiately
	reset SCSIINT.  This clear should only get rid of
	spurious SCSIINTs.

	This glitch was the cause of the "Unexpected PKT busfree"
	status that occurred under high queue full loads

	Call ahd_fini_scbdata() after shutdown so that
	any ahd_chip_init() routine that might access
	SCB data will not access free'd memory.

	Reset the bus on an IOERR since the chip doesn't
	seem to reset to the new voltage level without
	this.

	Change offset calculation for scatter gather maps
	so that the calculation is correct if an integral
	multiple of sg lists does not fit in the allocation
	size.

	Adjust bus dma tag for data buffers based on 39BIT
	addressing flag in our softc.

	Use the QFREEZE count to simplify ahd_pause_and_flushworkd().
	We can thus rely on the sequencer eventually clearing ENSELO.

	In ahd_abort_scbs(), fix a bug that could potentially
	corrupt sequencer state.  The saved SCB was being
	restored in the SCSI mode instead of the saved mode.
	It turns out that the SCB did not need to be saved at all
	as the scbptr is already restored by all subroutines
	called during this function that modify that register.

aic79xx.c:
aic79xx.h:
aic79xx_pci.c:
	Add support for parsing the seeprom vital product
	data.  The VPD data are currently unused.

aic79xx.h:
aic79xx.seq:
aic79xx_pci.c:
	Add a firmware workaround to make the LED blink
	brighter during packetized operations on the H2A.

aic79xx_inline.h:
	The host does not use timer interrupts, so don't
	gate our decision on whether or not to unpause
	the sequencer on whether or not a timer interrupt
	is pending.
This commit is contained in:
gibbs 2003-05-04 00:20:07 +00:00
parent d6e6bc2d1a
commit a72ce088d2
7 changed files with 511 additions and 295 deletions

View File

@ -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#170 $
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#190 $
*
* $FreeBSD$
*/
@ -516,7 +516,7 @@ ahd_handle_hwerrint(struct ahd_softc *ahd)
ahd_dump_card_state(ahd);
panic("BRKADRINT");
/* Tell everyone that this HBA is no longer availible */
/* Tell everyone that this HBA is no longer available */
ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
CAM_NO_HBA);
@ -556,6 +556,26 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
ahd_name(ahd), seqintcode);
#endif
switch (seqintcode) {
case BAD_SCB_STATUS:
{
struct scb *scb;
u_int scbid;
int cmds_pending;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL) {
ahd_complete_scb(ahd, scb);
} else {
printf("%s: WARNING no command for scb %d "
"(bad status)\n", ahd_name(ahd), scbid);
ahd_dump_card_state(ahd);
}
cmds_pending = ahd_inw(ahd, CMDS_PENDING);
if (cmds_pending > 0)
ahd_outw(ahd, CMDS_PENDING, cmds_pending - 1);
break;
}
case ENTERING_NONPACK:
{
struct scb *scb;
@ -604,7 +624,16 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
break;
case STATUS_OVERRUN:
{
printf("%s: Status Overrun", ahd_name(ahd));
struct scb *scb;
u_int scbid;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL)
ahd_print_path(ahd, scb);
else
printf("%s: ", ahd_name(ahd));
printf("SCB %d Packetized Status Overrun", scbid);
ahd_dump_card_state(ahd);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
break;
@ -1023,7 +1052,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
switch (scb->hscb->task_management) {
case SIU_TASKMGMT_ABORT_TASK:
tag = scb->hscb->tag;
tag = SCB_GET_TAG(scb);
case SIU_TASKMGMT_ABORT_TASK_SET:
case SIU_TASKMGMT_CLEAR_TASK_SET:
lun = scb->hscb->lun;
@ -1087,7 +1116,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
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,
SCB_GET_LUN(scb), SCB_GET_TAG(scb),
ROLE_INITIATOR, /*status*/0,
SEARCH_REMOVE);
}
@ -1166,7 +1195,7 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
/*
* A change in I/O mode is equivalent to a bus reset.
*/
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
ahd_pause(ahd);
ahd_setup_iocell_workaround(ahd);
ahd_unpause(ahd);
@ -1292,9 +1321,9 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL) {
printf("%s: Invalid SCB in DFF%d "
printf("%s: Invalid SCB %d in DFF%d "
"during unexpected busfree\n",
ahd_name(ahd), mode);
ahd_name(ahd), scbid, mode);
packetized = 0;
} else
packetized = (scb->flags & SCB_PACKETIZED) != 0;
@ -2183,6 +2212,13 @@ ahd_clear_critical_section(struct ahd_softc *ahd)
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
ahd_outb(ahd, SIMODE1, simode1);
/*
* SCSIINT seems to glitch occassionally when
* the interrupt masks are restored. Clear SCSIINT
* one more time so that only persistent errors
* are seen as a real interrupt.
*/
ahd_outb(ahd, CLRINT, CLRSCSIINT);
}
ahd_restore_modes(ahd, saved_modes);
}
@ -3770,9 +3806,9 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
devinfo->target, &tstate);
/*
* Parse as much of the message as is availible,
* Parse as much of the message as is available,
* rejecting it if we don't support it. When
* the entire message is availible and has been
* the entire message is available and has been
* handled, return MSGLOOP_MSGCOMPLETE, indicating
* that we have parsed an entire message.
*
@ -4832,9 +4868,6 @@ ahd_softc_insert(struct ahd_softc *ahd)
slave->flags &= ~AHD_BIOS_ENABLED;
slave->flags |=
master->flags & AHD_BIOS_ENABLED;
slave->flags &= ~AHD_PRIMARY_CHANNEL;
slave->flags |=
master->flags & AHD_PRIMARY_CHANNEL;
break;
}
}
@ -4846,7 +4879,7 @@ ahd_softc_insert(struct ahd_softc *ahd)
*/
list_ahd = TAILQ_FIRST(&ahd_tailq);
while (list_ahd != NULL
&& ahd_softc_comp(list_ahd, ahd) <= 0)
&& ahd_softc_comp(ahd, list_ahd) <= 0)
list_ahd = TAILQ_NEXT(list_ahd, links);
if (list_ahd != NULL)
TAILQ_INSERT_BEFORE(list_ahd, ahd, links);
@ -4890,7 +4923,6 @@ ahd_free(struct ahd_softc *ahd)
{
int i;
ahd_fini_scbdata(ahd);
switch (ahd->init_level) {
default:
case 5:
@ -4922,6 +4954,7 @@ ahd_free(struct ahd_softc *ahd)
ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
#endif
ahd_platform_free(ahd);
ahd_fini_scbdata(ahd);
for (i = 0; i < AHD_NUM_TARGETS; i++) {
struct ahd_tmode_tstate *tstate;
@ -5484,7 +5517,7 @@ ahd_free_scb(struct ahd_softc *ahd, struct scb *scb)
/* Clean up for the next user */
scb->flags = SCB_FLAG_NONE;
scb->hscb->control = 0;
ahd->scb_data.scbindex[scb->hscb->tag] = NULL;
ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = NULL;
if (scb->col_scb == NULL) {
@ -5587,8 +5620,8 @@ ahd_alloc_scbs(struct ahd_softc *ahd)
if (scb_data->sgs_left != 0) {
int offset;
offset = ahd_sglist_allocsize(ahd)
- (scb_data->sgs_left * ahd_sglist_size(ahd));
offset = ((ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd))
- scb_data->sgs_left) * ahd_sglist_size(ahd);
sg_map = SLIST_FIRST(&scb_data->sg_maps);
segs = sg_map->vaddr + offset;
sg_busaddr = sg_map->physaddr + offset;
@ -5818,7 +5851,9 @@ ahd_init(struct ahd_softc *ahd)
/* DMA tag for mapping buffers into device visible space. */
if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
/*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
/*lowaddr*/BUS_SPACE_MAXADDR,
/*lowaddr*/ahd->flags & AHD_39BIT_ADDRESSING
? (bus_addr_t)0x7FFFFFFFFFULL
: BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/(AHD_NSEG - 1) * PAGE_SIZE,
@ -5895,7 +5930,7 @@ ahd_init(struct ahd_softc *ahd)
* specially from the DMA safe memory chunk used for the QOUTFIFO.
*/
ahd->next_queued_hscb = (struct hardware_scb *)next_vaddr;
ahd->next_queued_hscb->hscb_busaddr = next_baddr;
ahd->next_queued_hscb->hscb_busaddr = ahd_htole32(next_baddr);
ahd->init_level++;
@ -6041,7 +6076,7 @@ ahd_chip_init(struct ahd_softc *ahd)
* Now that termination is set, wait for up
* to 500ms for our transceivers to settle. If
* the adapter does not have a cable attached,
* the tranceivers may never settle, so don't
* the transceivers may never settle, so don't
* complain if we fail here.
*/
for (wait = 10000;
@ -6057,7 +6092,6 @@ ahd_chip_init(struct ahd_softc *ahd)
for (i = 0; i < 2; i++) {
ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
ahd_outw(ahd, LONGJMP_SCB, SCB_LIST_NULL);
ahd_outb(ahd, SG_STATE, 0);
ahd_outb(ahd, CLRSEQINTSRC, 0xFF);
ahd_outb(ahd, SEQIMODE,
@ -6533,6 +6567,22 @@ ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc)
return (0);
}
/*
* Parse device configuration information.
*/
int
ahd_parse_vpddata(struct ahd_softc *ahd, struct vpd_config *vpd)
{
int error;
error = ahd_verify_vpd_cksum(vpd);
if (error == 0)
return (EINVAL);
if ((vpd->bios_flags & VPDBOOTHOST) != 0)
ahd->flags |= AHD_BOOT_CHANNEL;
return (0);
}
void
ahd_intr_enable(struct ahd_softc *ahd, int enable)
{
@ -6590,24 +6640,29 @@ ahd_enable_coalessing(struct ahd_softc *ahd, int enable)
void
ahd_pause_and_flushwork(struct ahd_softc *ahd)
{
ahd_mode_state saved_modes;
u_int intstat;
u_int maxloops;
int paused;
u_int intstat;
u_int maxloops;
u_int qfreeze_cnt;
maxloops = 1000;
ahd->flags |= AHD_ALL_INTERRUPTS;
paused = FALSE;
ahd_pause(ahd);
/*
* Increment the QFreeze Count so that the sequencer
* will not start new selections. We do this only
* until we are safely paused without further selections
* pending.
*/
ahd_outw(ahd, QFREEZE_COUNT, ahd_inw(ahd, QFREEZE_COUNT) + 1);
ahd_outb(ahd, SEQ_FLAGS2, ahd_inb(ahd, SEQ_FLAGS2) | SELECTOUT_QFROZEN);
do {
struct scb *waiting_scb;
if (paused)
ahd_unpause(ahd);
ahd_unpause(ahd);
ahd_intr(ahd);
ahd_pause(ahd);
paused = TRUE;
ahd_clear_critical_section(ahd);
saved_modes = ahd_save_modes(ahd);
intstat = ahd_inb(ahd, INTSTAT);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
if ((ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0)
ahd_outb(ahd, SCSISEQ0,
@ -6624,22 +6679,32 @@ ahd_pause_and_flushwork(struct ahd_softc *ahd)
&& (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0)
ahd_outb(ahd, SCSISEQ0,
ahd_inb(ahd, SCSISEQ0) | ENSELO);
intstat = ahd_inb(ahd, INTSTAT);
} while (--maxloops
&& (intstat != 0xFF || (ahd->features & AHD_REMOVABLE) == 0)
&& ((intstat & INT_PEND) != 0
|| (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO))));
|| (ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
|| (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0));
if (maxloops == 0) {
printf("Infinite interrupt loop, INTSTAT = %x",
ahd_inb(ahd, INTSTAT));
}
qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
if (qfreeze_cnt == 0) {
printf("%s: ahd_pause_and_flushwork with 0 qfreeze count!\n",
ahd_name(ahd));
} else {
qfreeze_cnt--;
}
ahd_outw(ahd, QFREEZE_COUNT, qfreeze_cnt);
if (qfreeze_cnt == 0)
ahd_outb(ahd, SEQ_FLAGS2,
ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
ahd_flush_qoutfifo(ahd);
ahd_platform_flushwork(ahd);
ahd->flags &= ~AHD_ALL_INTERRUPTS;
ahd_restore_modes(ahd, saved_modes);
}
int
@ -6813,7 +6878,6 @@ ahd_index_busy_tcl(struct ahd_softc *ahd, u_int *saved_scbid, u_int tcl)
/*
* Return the untagged transaction id for a given target/channel lun.
* Optionally, clear the entry.
*/
u_int
ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl)
@ -7323,7 +7387,6 @@ ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
{
struct scb *scbp;
struct scb *scbp_next;
u_int active_scb;
u_int i, j;
u_int maxtarget;
u_int minlun;
@ -7331,11 +7394,10 @@ ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
int found;
ahd_mode_state saved_modes;
/* restore these when we're done */
active_scb = ahd_get_scbptr(ahd);
/* restore this when we're done */
saved_modes = ahd_save_modes(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
found = ahd_search_qinfifo(ahd, target, channel, lun, SCB_LIST_NULL,
role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
@ -7409,7 +7471,6 @@ ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
found++;
}
}
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;
@ -7503,14 +7564,17 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_outb(ahd, DFFSTAT, next_fifo);
} while (next_fifo != fifo);
/*
* Reset the bus if we are initiating this reset
*/
ahd_clear_msg_state(ahd);
ahd_outb(ahd, SIMODE1,
ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST|ENBUSFREE));
if (initiate_reset)
ahd_reset_current_bus(ahd);
ahd_clear_intstat(ahd);
/*
@ -7708,9 +7772,6 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
hscb = scb->hscb;
/* Freeze the queue until the client sees the error. */
ahd_pause(ahd);
ahd_clear_critical_section(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_freeze_devq(ahd, scb);
ahd_freeze_scb(scb);
qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
@ -7723,7 +7784,7 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
if (qfreeze_cnt == 0)
ahd_outb(ahd, SEQ_FLAGS2,
ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
ahd_unpause(ahd);
/* Don't want to clobber the original sense code */
if ((scb->flags & SCB_SENSE) != 0) {
/*
@ -8581,11 +8642,11 @@ ahd_dump_card_state(struct ahd_softc *ahd)
LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
if (i++ > AHD_SCB_MAX)
break;
cur_col = printf("\n%3d ", SCB_GET_TAG(scb));
cur_col = printf("\n%3d FIFO_USE[0x%x] ", SCB_GET_TAG(scb),
ahd_inb(ahd, SCB_FIFO_USE_COUNT));
ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
ahd_scb_control_print(ahd_inb(ahd, SCB_CONTROL), &cur_col, 60);
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("\nTotal %d\n", i);
@ -8648,12 +8709,10 @@ ahd_dump_card_state(struct ahd_softc *ahd)
ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
fifo_scbptr = ahd_get_scbptr(ahd);
printf("\n%s: FIFO%d %s, LONGJMP == 0x%x, "
"SCB 0x%x, LJSCB 0x%x\n",
printf("\n%s: FIFO%d %s, LONGJMP == 0x%x, SCB 0x%x\n",
ahd_name(ahd), i,
(dffstat & (FIFO0FREE << i)) ? "Free" : "Active",
ahd_inw(ahd, LONGJMP_ADDR), fifo_scbptr,
ahd_inw(ahd, LONGJMP_SCB));
ahd_inw(ahd, LONGJMP_ADDR), fifo_scbptr);
cur_col = 0;
ahd_seqimode_print(ahd_inb(ahd, SEQIMODE), &cur_col, 50);
ahd_seqintsrc_print(ahd_inb(ahd, SEQINTSRC), &cur_col, 50);
@ -8770,11 +8829,12 @@ ahd_dump_scbs(struct ahd_softc *ahd)
/*
* Read count 16bit words from 16bit word address start_addr from the
* SEEPROM attached to the controller, into buf, using the controller's
* SEEPROM reading state machine.
* SEEPROM reading state machine. Optionally treat the data as a byte
* stream in terms of byte order.
*/
int
ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
u_int start_addr, u_int count)
u_int start_addr, u_int count, int bytestream)
{
u_int cur_addr;
u_int end_addr;
@ -8788,13 +8848,26 @@ ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
end_addr = start_addr + count;
for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
ahd_outb(ahd, SEEADR, cur_addr);
ahd_outb(ahd, SEECTL, SEEOP_READ | SEESTART);
error = ahd_wait_seeprom(ahd);
if (error)
break;
*buf++ = ahd_inw(ahd, SEEDAT);
if (bytestream != 0) {
uint8_t *bytestream_ptr;
bytestream_ptr = (uint8_t *)buf;
*bytestream_ptr++ = ahd_inb(ahd, SEEDAT);
*bytestream_ptr = ahd_inb(ahd, SEEDAT+1);
} else {
/*
* ahd_inw() already handles machine byte order.
*/
*buf = ahd_inw(ahd, SEEDAT);
}
buf++;
}
return (error);
}
@ -8867,6 +8940,38 @@ ahd_wait_seeprom(struct ahd_softc *ahd)
return (0);
}
/*
* Validate the two checksums in the per_channel
* vital product data struct.
*/
int
ahd_verify_vpd_cksum(struct vpd_config *vpd)
{
int i;
int maxaddr;
uint32_t checksum;
uint8_t *vpdarray;
vpdarray = (uint8_t *)vpd;
maxaddr = offsetof(struct vpd_config, vpd_checksum);
checksum = 0;
for (i = offsetof(struct vpd_config, resource_type); i < maxaddr; i++)
checksum = checksum + vpdarray[i];
if (checksum == 0
|| (-checksum & 0xFF) != vpd->vpd_checksum)
return (0);
checksum = 0;
maxaddr = offsetof(struct vpd_config, checksum);
for (i = offsetof(struct vpd_config, default_target_flags);
i < maxaddr; i++)
checksum = checksum + vpdarray[i];
if (checksum == 0
|| (-checksum & 0xFF) != vpd->checksum)
return (0);
return (1);
}
int
ahd_verify_cksum(struct seeprom_config *sc)
{

View File

@ -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#85 $
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#89 $
*
* $FreeBSD$
*/
@ -180,7 +180,7 @@ do { \
/*
* Define the size of our QIN and QOUT FIFOs. They must be a power of 2
* in size and accomodate as many transactions as can be queued concurrently.
* in size and accommodate as many transactions as can be queued concurrently.
*/
#define AHD_QIN_SIZE AHD_MAX_QUEUE
#define AHD_QOUT_SIZE AHD_MAX_QUEUE
@ -322,7 +322,11 @@ typedef enum {
* glitches. This flag tells the firmware to tolerate
* early REQ assertions.
*/
AHD_EARLY_REQ_BUG = 0x400000
AHD_EARLY_REQ_BUG = 0x400000,
/*
* The LED does not stay on long enough in packetized modes.
*/
AHD_FAINT_LED_BUG = 0x800000
} ahd_bug;
/*
@ -332,10 +336,7 @@ typedef enum {
*/
typedef enum {
AHD_FNONE = 0x00000,
AHD_PRIMARY_CHANNEL = 0x00003,/*
* The channel that should
* be probed first.
*/
AHD_BOOT_CHANNEL = 0x00001,/* We were set as the boot channel. */
AHD_USEDEFAULTS = 0x00004,/*
* For cards without an seeprom
* or a BIOS to initialize the chip's
@ -378,7 +379,7 @@ typedef enum {
/*
* The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
* consists of a "hardware SCB" mirroring the fields availible on the card
* consists of a "hardware SCB" mirroring the fields available on the card
* and additional information the kernel stores for each transaction.
*
* To minimize space utilization, a portion of the hardware scb stores
@ -493,13 +494,11 @@ struct hardware_scb {
* transfer.
*/
#define SG_PTR_MASK 0xFFFFFFF8
/*16*/ uint16_t tag;
/*18*/ uint8_t cdb_len;
/*19*/ uint8_t task_management;
/*20*/ uint32_t next_hscb_busaddr;
/*24*/ uint64_t dataptr;
/*32*/ uint32_t datacnt; /* Byte 3 is spare. */
/*36*/ uint32_t sgptr;
/*16*/ uint64_t dataptr;
/*24*/ uint32_t datacnt; /* Byte 3 is spare. */
/*28*/ uint32_t sgptr;
/*32*/ uint32_t hscb_busaddr;
/*36*/ uint32_t next_hscb_busaddr;
/*40*/ uint8_t control; /* See SCB_CONTROL in aic79xx.reg for details */
/*41*/ uint8_t scsiid; /*
* Selection out Id
@ -507,8 +506,10 @@ struct hardware_scb {
*/
/*42*/ uint8_t lun;
/*43*/ uint8_t task_attribute;
/*44*/ uint32_t hscb_busaddr;
/******* Long lun field only downloaded for full 8 byte lun support *******/
/*44*/ uint8_t cdb_len;
/*45*/ uint8_t task_management;
/*46*/ uint16_t tag; /* Reused by Sequencer. */
/********** Long lun field only downloaded for full 8 byte lun support ********/
/*48*/ uint8_t pkt_long_lun[8];
/******* Fields below are not Downloaded (Sequencer may use for scratch) ******/
/*56*/ uint8_t spare[8];
@ -900,6 +901,40 @@ struct seeprom_config {
uint16_t checksum; /* word 31 */
};
/*
* Vital Product Data used during POST and by the BIOS.
*/
struct vpd_config {
uint8_t bios_flags;
#define VPDMASTERBIOS 0x0001
#define VPDBOOTHOST 0x0002
uint8_t reserved_1[21];
uint8_t resource_type;
uint8_t resource_len[2];
uint8_t resource_data[8];
uint8_t vpd_tag;
uint16_t vpd_len;
uint8_t vpd_keyword[2];
uint8_t length;
uint8_t revision;
uint8_t device_flags;
uint8_t termnation_menus[2];
uint8_t fifo_threshold;
uint8_t end_tag;
uint8_t vpd_checksum;
uint16_t default_target_flags;
uint16_t default_bios_flags;
uint16_t default_ctrl_flags;
uint8_t default_irq;
uint8_t pci_lattime;
uint8_t max_target;
uint8_t boot_lun;
uint16_t signature;
uint8_t reserved_2;
uint8_t checksum;
uint8_t reserved_3[4];
};
/****************************** Flexport Logic ********************************/
#define FLXADDR_TERMCTL 0x0
#define FLX_TERMCTL_ENSECHIGH 0x8
@ -932,11 +967,12 @@ struct seeprom_config {
#define FLX_CSTAT_INVALID 0x3
int ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
u_int start_addr, u_int count);
u_int start_addr, u_int count, int bstream);
int ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
u_int start_addr, u_int count);
int ahd_wait_seeprom(struct ahd_softc *ahd);
int ahd_verify_vpd_cksum(struct vpd_config *vpd);
int ahd_verify_cksum(struct seeprom_config *sc);
int ahd_acquire_seeprom(struct ahd_softc *ahd);
void ahd_release_seeprom(struct ahd_softc *ahd);
@ -1321,6 +1357,8 @@ int ahd_softc_init(struct ahd_softc *);
void ahd_controller_info(struct ahd_softc *ahd, char *buf);
int ahd_init(struct ahd_softc *ahd);
int ahd_default_config(struct ahd_softc *ahd);
int ahd_parse_vpddata(struct ahd_softc *ahd,
struct vpd_config *vpd);
int ahd_parse_cfgdata(struct ahd_softc *ahd,
struct seeprom_config *sc);
void ahd_intr_enable(struct ahd_softc *ahd, int enable);

View File

@ -39,7 +39,7 @@
*
* $FreeBSD$
*/
VERSION = "$Id: aic79xx.reg,v 1.9 2003/02/27 23:23:16 gibbs Exp $"
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#67 $"
/*
* This file is processed by the aic7xxx_asm utility for use in assembling
@ -194,7 +194,8 @@ register SEQINTCODE {
TRACEPOINT1,
TRACEPOINT2,
TRACEPOINT3,
SAW_HWERR
SAW_HWERR,
BAD_SCB_STATUS
}
}
@ -3484,9 +3485,6 @@ scratch_ram {
LONGJMP_ADDR {
size 2
}
LONGJMP_SCB {
size 2
}
ACCUM_SAVE {
size 1
}
@ -3799,23 +3797,6 @@ scb {
size 4
alias SCB_NEXT_COMPLETE
}
SCB_TAG {
size 2
}
SCB_CDB_LEN {
size 1
field SCB_CDB_LEN_PTR 0x80 /* CDB in host memory */
}
SCB_TASK_MANAGEMENT {
size 1
}
SCB_NEXT {
alias SCB_NEXT_SCB_BUSADDR
size 2
}
SCB_NEXT2 {
size 2
}
SCB_DATAPTR {
size 8
}
@ -3834,6 +3815,16 @@ scb {
field SG_FULL_RESID 0x02 /* In the first byte */
field SG_LIST_NULL 0x01 /* In the first byte */
}
SCB_BUSADDR {
size 4
}
SCB_NEXT {
alias SCB_NEXT_SCB_BUSADDR
size 2
}
SCB_NEXT2 {
size 2
}
SCB_CONTROL {
size 1
field TARGET_SCB 0x80
@ -3856,8 +3847,16 @@ scb {
SCB_TASK_ATTRIBUTE {
size 1
}
SCB_BUSADDR {
size 4
SCB_CDB_LEN {
size 1
field SCB_CDB_LEN_PTR 0x80 /* CDB in host memory */
}
SCB_TASK_MANAGEMENT {
size 1
}
SCB_TAG {
alias SCB_FIFO_USE_COUNT
size 2
}
SCB_SPARE {
size 8

View File

@ -40,7 +40,7 @@
* $FreeBSD$
*/
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#88 $"
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#91 $"
PATCH_ARG_LIST = "struct ahd_softc *ahd"
PREFIX = "ahd_"
@ -89,6 +89,13 @@ END_CRITICAL;
idle_loop_check_nonpackreq:
test SSTAT2, NONPACKREQ jz . + 2;
call unexpected_nonpkt_phase_find_ctxt;
if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
cmp A, FIFO0FREE|FIFO1FREE jne . + 3;
and SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
jmp . + 2;
or SBLKCTL, DIAGLEDEN|DIAGLEDON;
}
call idle_loop_gsfifo_in_scsi_mode;
call idle_loop_service_fifos;
call idle_loop_cchan;
@ -101,8 +108,8 @@ idle_loop_gsfifo_in_scsi_mode:
test LQISTAT2, LQIGSAVAIL jz return;
/*
* We have received good status for this transaction. There may
* still be data in our FIFOs draining to the host. Setup
* monitoring of the draining process or complete the SCB.
* still be data in our FIFOs draining to the host. Complete
* the SCB only if all data has transferred to the host.
*/
good_status_IU_done:
bmov SCBPTR, GSFIFO, 2;
@ -128,42 +135,20 @@ gsfifo_complete_normally:
* 1) Configured and draining to the host, with a FIFO handler.
* 2) Pending cfg4data, fifo not empty.
*
* Case 1 can be detected by noticing that a longjmp is active for
* the FIFO and LONGJMP_SCB matches our SCB. In this case, we allow
* the routine servicing the FIFO to complete the SCB.
* Case 1 can be detected by noticing a non-zero FIFO active
* count in the SCB. In this case, we allow the routine servicing
* the FIFO to complete the SCB.
*
* Case 2 implies either a pending or yet to occur save data
* pointers for this same context in the other FIFO. So, if
* we detect case 1, we will properly defer the post of the SCB
* and achieve the desired result. The pending cfg4data will
* notice that status has been received and complete the SCB.
*
* If the data-transfer has been completed, or no data transfer
* was needed for this SCB, it is safe to complete the command.
*/
test SCB_SGPTR, SG_LIST_NULL jz good_status_check_fifos;
/*
* All segments have been loaded (or no data transfer), so
* it is safe to complete the command. Since this was a
* cheap command to check for completion, loop to see if
* more entries can be removed from the GSFIFO.
*/
test SCB_FIFO_USE_COUNT, 0xFF jnz idle_loop_gsfifo_in_scsi_mode;
call complete;
END_CRITICAL;
jmp idle_loop_gsfifo_in_scsi_mode;
BEGIN_CRITICAL;
good_status_check_fifos:
clc;
bmov ARG_1, SCBPTR, 2;
SET_MODE(M_DFF0, M_DFF0)
call check_fifo;
jc return;
SET_MODE(M_DFF1, M_DFF1)
call check_fifo;
jc return;
SET_MODE(M_SCSI, M_SCSI)
jmp queue_scb_completion;
END_CRITICAL;
idle_loop_service_fifos:
SET_MODE(M_DFF0, M_DFF0)
@ -172,6 +157,7 @@ idle_loop_service_fifos:
idle_loop_next_fifo:
SET_MODE(M_DFF1, M_DFF1)
test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
return:
ret;
idle_loop_cchan:
@ -189,12 +175,31 @@ END_CRITICAL;
scbdma_tohost_done:
test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
/*
* A complete SCB upload requires no intervention.
* The SCB is already on the COMPLETE_SCB list
* and its completion notification will now be
* handled just like any other SCB.
* An SCB has been succesfully uploaded to the host.
* If the SCB was uploaded for some reason other than
* bad SCSI status (currently only for underruns), we
* queue the SCB for normal completion. Otherwise, we
* wait until any select-out activity has halted, and
* then notify the host so that the transaction can be
* dealt with.
*/
and CCSCBCTL, ~(CCARREN|CCSCBEN) ret;
test SCB_SCSI_STATUS, 0xff jnz scbdma_notify_host;
and CCSCBCTL, ~(CCARREN|CCSCBEN);
bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
scbdma_notify_host:
SET_MODE(M_SCSI, M_SCSI)
test SCSISEQ0, ENSELO jnz return;
test SSTAT0, (SELDO|SELINGO) jnz return;
SET_MODE(M_CCHAN, M_CCHAN)
/*
* Remove SCB and notify host.
*/
and CCSCBCTL, ~(CCARREN|CCSCBEN);
bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
SET_SEQINTCODE(BAD_SCB_STATUS)
ret;
fill_qoutfifo_dmadone:
and CCSCBCTL, ~(CCARREN|CCSCBEN);
call qoutfifo_updated;
@ -256,6 +261,13 @@ fetch_new_scb_done:
clr A;
add CMDS_PENDING, 1;
adc CMDS_PENDING[1], A;
/*
* The FIFO use count field is shared with the
* tag set by the host so that our SCB dma engine
* knows the correct location to store the SCB.
* Set it to zero before processing the SCB.
*/
mov SCB_FIFO_USE_COUNT, ALLZEROS;
/* Update the next SCB address to download. */
bmov NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4;
mvi SCB_NEXT[1], SCB_LIST_NULL;
@ -332,15 +344,7 @@ fetch_new_scb:
dma_complete_scb:
bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
bmov SCBHADDR, SCB_BUSADDR, 4;
mvi CCARREN|CCSCBEN|CCSCBRESET call dma_scb;
/*
* Now that we've started the DMA, push us onto
* the normal completion queue to have our SCBID
* posted to the kernel.
*/
bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
mvi CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb;
END_CRITICAL;
/*
@ -359,8 +363,6 @@ dma_scb:
mov CCSCBCTL, SINDEX ret;
BEGIN_CRITICAL;
setjmp_setscb:
bmov LONGJMP_SCB, SCBPTR, 2;
setjmp:
bmov LONGJMP_ADDR, STACK, 2 ret;
setjmp_inline:
@ -1028,9 +1030,18 @@ freeze_queue:
or SEQ_FLAGS2, SELECTOUT_QFROZEN;
mov A, ACCUM_SAVE ret;
queue_arg1_scb_completion:
/*
* Complete the current FIFO's SCB if data for this same
* SCB is not transferring in the other FIFO.
*/
SET_SRC_MODE M_DFF1;
SET_DST_MODE M_DFF1;
pkt_complete_scb_if_fifos_idle:
bmov ARG_1, SCBPTR, 2;
mvi DFFSXFRCTL, CLRCHN;
SET_MODE(M_SCSI, M_SCSI)
bmov SCBPTR, ARG_1, 2;
test SCB_FIFO_USE_COUNT, 0xFF jnz return;
queue_scb_completion:
test SCB_SCSI_STATUS,0xff jnz bad_status;
/*
@ -1046,6 +1057,12 @@ bad_status:
cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
call freeze_queue;
upload_scb:
/*
* Restore SCB TAG since we reuse this field
* in the sequencer. We don't want to corrupt
* it on the host.
*/
bmov SCB_TAG, SCBPTR, 2;
bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2;
bmov COMPLETE_DMA_SCB_HEAD, SCBPTR, 2;
or SCB_SGPTR, SG_STATUS_VALID ret;
@ -1356,7 +1373,7 @@ load_first_seg:
clr SG_STATE ret;
p_data_handle_xfer:
call setjmp_setscb;
call setjmp;
test SG_STATE, LOADING_NEEDED jnz service_fifo;
p_data_clear_handler:
or LONGJMP_ADDR[1], INVALID_ADDR ret;
@ -1609,25 +1626,32 @@ export seq_isr:
* and deffer the test by one instruction.
*/
mov REG_ISR, LQISTAT2;
test REG_ISR, LQIWORKONLQ jz data_valid;
test SEQINTSRC, SAVEPTRS jz data_valid;
test REG_ISR, LQIWORKONLQ jz main_isr;
test SEQINTSRC, SAVEPTRS jz main_isr;
test LONGJMP_ADDR[1], INVALID_ADDR jz saveptr_active_fifo;
/*
* Switch to the active FIFO.
* Switch to the active FIFO after clearing the snapshot
* savepointer in the current FIFO. We do this so that
* a pending CTXTDONE or SAVEPTR is visible in the active
* FIFO. This status is the only way we can detect if we
* have lost the race (e.g. host paused us) and our attepts
* to disable the channel occurred after all REQs were
* already seen and acked (REQINIT never comes true).
*/
mvi DFFSXFRCTL, CLRCHN;
xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
test DFCNTRL, DIRECTION jz snapshot_other_fifo;
test DFCNTRL, DIRECTION jz interrupt_return;
and DFCNTRL, ~SCSIEN;
test SSTAT1, REQINIT jz .;
snapshot_wait_data_valid:
test SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz snapshot_data_valid;
test SSTAT1, REQINIT jz snapshot_wait_data_valid;
snapshot_data_valid:
or DFCNTRL, SCSIEN;
/* FALLTHROUGH */
snapshot_other_fifo:
xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
/* FALLTHROUGH */
or SEQINTCTL, IRET ret;
snapshot_saveptr:
mvi DFFSXFRCTL, CLRCHN;
or SEQINTCTL, IRET ret;
data_valid:
main_isr:
}
test SEQINTSRC, CFG4DATA jnz cfg4data_intr;
test SEQINTSRC, CFG4ISTAT jnz cfg4istat_intr;
@ -1660,9 +1684,11 @@ saveptr_active_fifo:
or SEQINTCTL, IRET ret;
cfg4data_intr:
test SCB_SGPTR[0], SG_LIST_NULL jnz pkt_handle_overrun;
test SCB_SGPTR[0], SG_LIST_NULL jnz pkt_handle_overrun_inc_use_count;
call load_first_seg;
call pkt_handle_xfer;
inc SCB_FIFO_USE_COUNT;
interrupt_return:
or SEQINTCTL, IRET ret;
cfg4istat_intr:
@ -1722,7 +1748,6 @@ cfg4icmd_intr:
test DFSTATUS, FIFOEMP jz pkt_handle_overrun
pkt_handle_xfer:
bmov LONGJMP_SCB, SCBPTR, 2;
test SG_STATE, LOADING_NEEDED jz pkt_last_seg;
call setjmp;
test SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
@ -1744,7 +1769,7 @@ pkt_service_fifo:
pkt_last_seg:
call setjmp;
test SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
test SG_CACHE_SHADOW, LAST_SEG_DONE jnz last_pkt_xfer_done;
test SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_last_seg_done;
test SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
test SCSISIGO, ATNO jnz . + 2;
test SSTAT2, NONPACKREQ jz return;
@ -1753,7 +1778,7 @@ pkt_last_seg:
/*
* Either a SAVEPTRS interrupt condition is pending for this FIFO
* or we have a pending nonpackreq for this FIFO. We differentiate
* or we have a pending NONPACKREQ for this FIFO. We differentiate
* between the two by capturing the state of the SAVEPTRS interrupt
* prior to clearing this status and executing the common code for
* these two cases.
@ -1782,107 +1807,56 @@ pkt_saveptrs_wait_fifoemp:
pkt_saveptrs_check_status:
or LONGJMP_ADDR[1], INVALID_ADDR;
test REG0, SAVEPTRS jz unexpected_nonpkt_phase;
test SCB_CONTROL, STATUS_RCVD jz pkt_saveptrs_clrchn;
jmp last_pkt_complete;
pkt_saveptrs_clrchn:
mvi DFFSXFRCTL, CLRCHN ret;
END_CRITICAL;
last_pkt_xfer_done:
BEGIN_CRITICAL;
if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
or DFCNTRL, FIFOFLUSH;
}
test SCB_CONTROL, STATUS_RCVD jz wait_pkt_end;
check_overrun;
or SCB_SGPTR, SG_LIST_NULL;
/*
* It is safe to skip the other FIFO check since
* we defer CLRCHN on SAVEPTRS until all data in
* the FIFO are seen by the host and a CFG4DATA
* in this FIFO for the same context is held off
* by hardware.
*/
last_pkt_queue_scb:
or LONGJMP_ADDR[1], INVALID_ADDR;
bmov ARG_1, SCBPTR, 2;
mvi DFFSXFRCTL, CLRCHN;
jmp queue_arg1_scb_completion;
last_pkt_complete:
bmov ARG_1, SCBPTR, 2;
mvi DFFSXFRCTL, CLRCHN;
check_other_fifo:
clc;
TOGGLE_DFF_MODE
call check_fifo;
jnc queue_arg1_scb_completion;
return:
ret;
wait_pkt_end:
call setjmp;
END_CRITICAL;
wait_pkt_end_loop:
test SEQINTSRC, CTXTDONE jnz pkt_end;
check_overrun;
test SSTAT2, NONPACKREQ jz return;
test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
pkt_end:
BEGIN_CRITICAL;
check_overrun;
or LONGJMP_ADDR[1], INVALID_ADDR;
or SCB_SGPTR, SG_LIST_NULL;
test SCB_CONTROL, STATUS_RCVD jnz last_pkt_complete;
dec SCB_FIFO_USE_COUNT;
test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
mvi DFFSXFRCTL, CLRCHN ret;
END_CRITICAL;
/*
* Watch over the status transfer. Our host sense buffer is
* large enough to take the maximum allowed status packet.
* None-the-less, we must still catch and report overruns to
* the host.
* LAST_SEG_DONE status has been seen in the current FIFO.
* This indicates that all of the allowed data for this
* command has transferred across the SCSI and host buses.
* Check for overrun and see if we can complete this command.
*/
pkt_handle_status:
call setjmp_setscb;
test SG_CACHE_SHADOW, LAST_SEG_DONE jz check_status_overrun;
test SEQINTSRC, CTXTDONE jz return;
status_IU_done:
pkt_last_seg_done:
BEGIN_CRITICAL;
if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
or DFCNTRL, FIFOFLUSH;
}
or LONGJMP_ADDR[1], INVALID_ADDR;
mvi SCB_SCSI_STATUS, STATUS_PKT_SENSE;
or SCB_CONTROL, STATUS_RCVD;
jmp last_pkt_complete;
END_CRITICAL;
check_status_overrun:
/*
* We've filled the entire sense buffer.
* Wait for either context done or a negative
* shaddow count. If the context completes without
* causing the shaddow count to go negative, then
* this was a successful transfer up to the status
* limit. Otherwise we report the error.
* Mark transfer as completed.
*/
test SHCNT[2], 0xFF jnz report_status_overrun;
test SEQINTSRC, CTXTDONE jz return;
test SHCNT[2], 0xFF jz status_IU_done;
report_status_overrun:
SET_SEQINTCODE(STATUS_OVERRUN)
jmp status_IU_done;
or SCB_SGPTR, SG_LIST_NULL;
SET_SRC_MODE M_DFF0;
SET_DST_MODE M_DFF0;
BEGIN_CRITICAL;
check_fifo:
test LONGJMP_ADDR[1], INVALID_ADDR jnz return;
mov A, ARG_2;
cmp LONGJMP_SCB[1], A jne return;
mov A, ARG_1;
cmp LONGJMP_SCB[0], A jne return;
stc ret;
/*
* Wait for the current context to finish to verify that
* no overrun condition has occurred.
*/
test SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
call setjmp;
pkt_wait_ctxt_done_loop:
test SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
/*
* A sufficiently large overrun or a NONPACKREQ may
* prevent CTXTDONE from ever asserting, so we must
* poll for these statuses too.
*/
check_overrun;
test SSTAT2, NONPACKREQ jz return;
test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
/* FALLTHROUGH */
pkt_ctxt_done:
check_overrun;
or LONGJMP_ADDR[1], INVALID_ADDR;
/*
* If status has been received, it is safe to skip
* the check to see if another FIFO is active because
* LAST_SEG_DONE has been observed. However, we check
* the FIFO anyway since it costs us only one extra
* instruction to leverage common code to perform the
* SCB completion.
*/
dec SCB_FIFO_USE_COUNT;
test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
mvi DFFSXFRCTL, CLRCHN ret;
END_CRITICAL;
/*
@ -1890,11 +1864,78 @@ END_CRITICAL;
* clear channel.
*/
pkt_handle_cdb:
call setjmp_setscb;
call setjmp;
test SG_CACHE_SHADOW, LAST_SEG_DONE jz return;
or LONGJMP_ADDR[1], INVALID_ADDR;
mvi DFFSXFRCTL, CLRCHN ret;
/*
* Watch over the status transfer. Our host sense buffer is
* large enough to take the maximum allowed status packet.
* None-the-less, we must still catch and report overruns to
* the host. Additionally, properly catch unexpected non-packet
* phases that are typically caused by CRC errors in status packet
* transmission.
*/
pkt_handle_status:
call setjmp;
test SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_status_check_overrun;
test SEQINTSRC, CTXTDONE jz pkt_status_check_nonpackreq;
test SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_status_check_overrun;
pkt_status_IU_done:
if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
or DFCNTRL, FIFOFLUSH;
}
test DFSTATUS, FIFOEMP jz return;
BEGIN_CRITICAL;
or LONGJMP_ADDR[1], INVALID_ADDR;
mvi SCB_SCSI_STATUS, STATUS_PKT_SENSE;
or SCB_CONTROL, STATUS_RCVD;
jmp pkt_complete_scb_if_fifos_idle;
END_CRITICAL;
pkt_status_check_overrun:
/*
* Status PKT overruns are uncerimoniously recovered with a
* bus reset. If we've overrun, let the host know so that
* recovery can be performed.
*
* LAST_SEG_DONE has been observed. If either CTXTDONE or
* a NONPACKREQ phase change have occurred and the FIFO is
* empty, there is no overrun.
*/
test DFSTATUS, FIFOEMP jz pkt_status_report_overrun;
test SEQINTSRC, CTXTDONE jz . + 2;
test DFSTATUS, FIFOEMP jnz pkt_status_IU_done;
test SCSIPHASE, ~DATA_PHASE_MASK jz return;
test DFSTATUS, FIFOEMP jnz pkt_status_check_nonpackreq;
pkt_status_report_overrun:
SET_SEQINTCODE(STATUS_OVERRUN)
/* SEQUENCER RESTARTED */
pkt_status_check_nonpackreq:
/*
* CTXTDONE may be held off if a NONPACKREQ is associated with
* the current context. If a NONPACKREQ is observed, decide
* if it is for the current context. If it is for the current
* context, we must defer NONPACKREQ processing until all data
* has transferred to the host.
*/
test SCSIPHASE, ~DATA_PHASE_MASK jz return;
test SCSISIGO, ATNO jnz . + 2;
test SSTAT2, NONPACKREQ jz return;
test SEQINTSRC, CTXTDONE jnz pkt_status_IU_done;
test DFSTATUS, FIFOEMP jz return;
/*
* The unexpected nonpkt phase handler assumes that any
* data channel use will have a FIFO reference count. It
* turns out that the status handler doesn't need a refernce
* count since the status received flag, and thus completion
* processing, cannot be set until the handler is finished.
* We increment the count here to make the nonpkt handler
* happy.
*/
inc SCB_FIFO_USE_COUNT;
/* FALLTHROUGH */
/*
* Nonpackreq is a polled status. It can come true in three situations:
* we have received an L_Q, we have sent one or more L_Qs, or there is no
@ -1922,6 +1963,7 @@ unexpected_nonpkt_phase:
SET_SRC_MODE M_DFF0;
SET_DST_MODE M_DFF0;
or LONGJMP_ADDR[1], INVALID_ADDR;
dec SCB_FIFO_USE_COUNT;
mvi DFFSXFRCTL, CLRCHN;
mvi CLRSINT2, CLRNONPACKREQ;
test SCSIPHASE, ~(MSG_IN_PHASE|MSG_OUT_PHASE) jnz illegal_phase;
@ -1938,6 +1980,8 @@ illegal_phase:
* data. Otherwise use an overrun buffer in the host to simulate
* BITBUCKET.
*/
pkt_handle_overrun_inc_use_count:
inc SCB_FIFO_USE_COUNT;
pkt_handle_overrun:
SET_SEQINTCODE(CFG4OVERRUN)
call freeze_queue;
@ -1963,8 +2007,9 @@ overrun_load_done:
pkt_overrun_end:
or SCB_RESIDUAL_SGPTR, SG_OVERRUN_RESID;
test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
test SCB_CONTROL, STATUS_RCVD jnz last_pkt_queue_scb;
dec SCB_FIFO_USE_COUNT;
or LONGJMP_ADDR[1], INVALID_ADDR;
test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
mvi DFFSXFRCTL, CLRCHN ret;
if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {

View File

@ -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#44 $
* $Id: //depot/aic7xxx/aic7xxx/aic79xx_inline.h#48 $
*
* $FreeBSD$
*/
@ -223,7 +223,7 @@ ahd_unpause(struct ahd_softc *ahd)
ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
}
if ((ahd_inb(ahd, INTSTAT) & ~(SWTMINT | CMDCMPLT)) == 0)
if ((ahd_inb(ahd, INTSTAT) & ~CMDCMPLT) == 0)
ahd_outb(ahd, HCNTRL, ahd->unpause);
ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN);
@ -298,9 +298,12 @@ ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb)
scb->hscb->datacnt = sg->len;
} else {
struct ahd_dma_seg *sg;
uint32_t *dataptr_words;
sg = (struct ahd_dma_seg *)scb->sg_list;
scb->hscb->dataptr = sg->addr;
dataptr_words = (uint32_t*)&scb->hscb->dataptr;
dataptr_words[0] = sg->addr;
dataptr_words[1] = 0;
if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
uint64_t high_addr;
@ -777,12 +780,15 @@ ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb)
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_QUEUE) != 0) {
uint64_t host_dataptr;
host_dataptr = ahd_le64toh(scb->hscb->dataptr);
printf("%s: Queueing SCB 0x%x bus addr 0x%x - 0x%x%x/0x%x\n",
ahd_name(ahd),
SCB_GET_TAG(scb), scb->hscb->hscb_busaddr,
(u_int)((scb->hscb->dataptr >> 32) & 0xFFFFFFFF),
(u_int)(scb->hscb->dataptr & 0xFFFFFFFF),
scb->hscb->datacnt);
SCB_GET_TAG(scb), ahd_le32toh(scb->hscb->hscb_busaddr),
(u_int)((host_dataptr >> 32) & 0xFFFFFFFF),
(u_int)(host_dataptr & 0xFFFFFFFF),
ahd_le32toh(scb->hscb->datacnt));
}
#endif
/* Tell the adapter about the newly queued SCB */
@ -805,7 +811,7 @@ ahd_get_sense_bufaddr(struct ahd_softc *ahd, struct scb *scb)
static __inline void ahd_sync_qoutfifo(struct ahd_softc *ahd, int op);
static __inline void ahd_sync_tqinfifo(struct ahd_softc *ahd, int op);
static __inline u_int ahd_check_cmdcmpltqueues(struct ahd_softc *ahd);
static __inline void ahd_intr(struct ahd_softc *ahd);
static __inline int ahd_intr(struct ahd_softc *ahd);
static __inline void
ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
@ -864,7 +870,7 @@ ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
/*
* Catch an interrupt from the adapter
*/
static __inline void
static __inline int
ahd_intr(struct ahd_softc *ahd)
{
u_int intstat;
@ -876,7 +882,7 @@ ahd_intr(struct ahd_softc *ahd)
* so just return. This is likely just a shared
* interrupt.
*/
return;
return (0);
}
/*
@ -891,6 +897,9 @@ ahd_intr(struct ahd_softc *ahd)
else
intstat = ahd_inb(ahd, INTSTAT);
if ((intstat & INT_PEND) == 0)
return (0);
if (intstat & CMDCMPLT) {
ahd_outb(ahd, CLRINT, CLRCMDINT);
@ -924,28 +933,25 @@ ahd_intr(struct ahd_softc *ahd)
#endif
}
if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0)
/* Hot eject */
return;
if ((intstat & INT_PEND) == 0)
return;
if (intstat & HWERRINT) {
/*
* Handle statuses that may invalidate our cached
* copy of INTSTAT separately.
*/
if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0) {
/* Hot eject. Do nothing */
} else if (intstat & HWERRINT) {
ahd_handle_hwerrint(ahd);
return;
}
if ((intstat & (PCIINT|SPLTINT)) != 0) {
} else if ((intstat & (PCIINT|SPLTINT)) != 0) {
ahd->bus_intr(ahd);
return;
} else {
if ((intstat & SEQINT) != 0)
ahd_handle_seqint(ahd, intstat);
if ((intstat & SCSIINT) != 0)
ahd_handle_scsiint(ahd, intstat);
}
if ((intstat & SEQINT) != 0)
ahd_handle_seqint(ahd, intstat);
if ((intstat & SCSIINT) != 0)
ahd_handle_scsiint(ahd, intstat);
return (1);
}
#endif /* _AIC79XX_INLINE_H_ */

View File

@ -215,7 +215,7 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb)
untimeout(ahd_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch);
if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
int op;
/*XXX bus_dmasync_op_t*/int op;
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
op = BUS_DMASYNC_POSTREAD;
@ -1056,7 +1056,7 @@ ahd_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments,
scb->sg_count = 0;
if (nsegments != 0) {
void *sg;
int op;
/*bus_dmasync_op_t*/int op;
u_int i;
/* Copy the segments into our SG list */

View File

@ -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#67 $
* $Id$
*
* $FreeBSD$
*/
@ -332,9 +332,9 @@ ahd_pci_config(struct ahd_softc *ahd, struct ahd_pci_identity *entry)
}
/* Ensure busmastering is enabled */
command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1);
command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
command |= PCIM_CMD_BUSMASTEREN;
ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, /*bytes*/2);
error = ahd_softc_init(ahd);
if (error != 0)
@ -466,6 +466,7 @@ ahd_pci_test_register_access(struct ahd_softc *ahd)
static int
ahd_check_extport(struct ahd_softc *ahd)
{
struct vpd_config vpd;
struct seeprom_config *sc;
u_int adapter_control;
int have_seeprom;
@ -476,6 +477,27 @@ ahd_check_extport(struct ahd_softc *ahd)
if (have_seeprom) {
u_int start_addr;
/*
* Fetch VPD for this function and parse it.
*/
if (bootverbose)
printf("%s: Reading VPD from SEEPROM...",
ahd_name(ahd));
/* Address is always in units of 16bit words */
start_addr = ((2 * sizeof(*sc))
+ (sizeof(vpd) * (ahd->channel - 'A'))) / 2;
error = ahd_read_seeprom(ahd, (uint16_t *)&vpd,
start_addr, sizeof(vpd)/2,
/*bytestream*/TRUE);
if (error == 0)
error = ahd_parse_vpddata(ahd, &vpd);
if (bootverbose)
printf("%s: VPD parsing %s\n",
ahd_name(ahd),
error == 0 ? "successful" : "failed");
if (bootverbose)
printf("%s: Reading SEEPROM...", ahd_name(ahd));
@ -483,7 +505,8 @@ ahd_check_extport(struct ahd_softc *ahd)
start_addr = (sizeof(*sc) / 2) * (ahd->channel - 'A');
error = ahd_read_seeprom(ahd, (uint16_t *)sc,
start_addr, sizeof(*sc)/2);
start_addr, sizeof(*sc)/2,
/*bytestream*/FALSE);
if (error != 0) {
printf("Unable to read SEEPROM\n");
@ -542,14 +565,13 @@ ahd_check_extport(struct ahd_softc *ahd)
#if AHD_DEBUG
if (have_seeprom != 0
&& (ahd_debug & AHD_DUMP_SEEPROM) != 0) {
uint8_t *sc_data;
int i;
uint16_t *sc_data;
int i;
printf("%s: Seeprom Contents:", ahd_name(ahd));
sc_data = (uint8_t *)sc;
sc_data = (uint16_t *)sc;
for (i = 0; i < (sizeof(*sc)); i += 2)
printf("\n\t0x%.4x",
sc_data[i] | (sc_data[i+1] << 8));
printf("\n\t0x%.4x", sc_data[i]);
printf("\n");
}
#endif
@ -803,7 +825,7 @@ ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
/* Clear latched errors. So our interrupt deasserts. */
ahd_outb(ahd, DCHSPLTSTAT0, split_status[i]);
ahd_outb(ahd, DCHSPLTSTAT1, split_status1[i]);
if (i != 0)
if (i > 1)
continue;
sg_split_status[i] = ahd_inb(ahd, SGSPLTSTAT0);
sg_split_status1[i] = ahd_inb(ahd, SGSPLTSTAT1);
@ -825,7 +847,7 @@ ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
split_status_source[i]);
}
if (i != 0)
if (i > 1)
continue;
if ((sg_split_status[i] & (0x1 << bit)) != 0) {
@ -868,7 +890,7 @@ ahd_aic7902_setup(struct ahd_softc *ahd)
if (rev < ID_AIC7902_PCI_REV_A4) {
printf("%s: Unable to attach to unsupported chip revision %d\n",
ahd_name(ahd), rev);
ahd_pci_write_config(pci, PCIR_COMMAND, 0, /*bytes*/1);
ahd_pci_write_config(pci, PCIR_COMMAND, 0, /*bytes*/2);
return (ENXIO);
}
ahd->channel = ahd_get_pci_function(pci) + 'A';
@ -887,7 +909,8 @@ ahd_aic7902_setup(struct ahd_softc *ahd)
| AHD_PKTIZED_STATUS_BUG|AHD_PKT_LUN_BUG
| AHD_MDFF_WSCBPTR_BUG|AHD_REG_SLOW_SETTLE_BUG
| AHD_SET_MODE_BUG|AHD_BUSFREEREV_BUG
| AHD_NONPACKFIFO_BUG|AHD_PACED_NEGTABLE_BUG;
| AHD_NONPACKFIFO_BUG|AHD_PACED_NEGTABLE_BUG
| AHD_FAINT_LED_BUG;
/*
* IO Cell paramter setup.