summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/aic7xxx/aic79xx.c213
-rw-r--r--sys/dev/aic7xxx/aic79xx.h74
-rw-r--r--sys/dev/aic7xxx/aic79xx.reg47
-rw-r--r--sys/dev/aic7xxx/aic79xx.seq333
-rw-r--r--sys/dev/aic7xxx/aic79xx_inline.h62
-rw-r--r--sys/dev/aic7xxx/aic79xx_osm.c4
-rw-r--r--sys/dev/aic7xxx/aic79xx_pci.c49
7 files changed, 499 insertions, 283 deletions
diff --git a/sys/dev/aic7xxx/aic79xx.c b/sys/dev/aic7xxx/aic79xx.c
index f1ef6a8..2e8c000 100644
--- a/sys/dev/aic7xxx/aic79xx.c
+++ b/sys/dev/aic7xxx/aic79xx.c
@@ -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)
{
diff --git a/sys/dev/aic7xxx/aic79xx.h b/sys/dev/aic7xxx/aic79xx.h
index 87452c7..4bbbdbb 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#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);
diff --git a/sys/dev/aic7xxx/aic79xx.reg b/sys/dev/aic7xxx/aic79xx.reg
index fe2d71b..c5e4521 100644
--- a/sys/dev/aic7xxx/aic79xx.reg
+++ b/sys/dev/aic7xxx/aic79xx.reg
@@ -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
diff --git a/sys/dev/aic7xxx/aic79xx.seq b/sys/dev/aic7xxx/aic79xx.seq
index 3a772aa..cbc8101 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#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,118 +1807,134 @@ 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:
+ dec SCB_FIFO_USE_COUNT;
+ test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
mvi DFFSXFRCTL, CLRCHN ret;
END_CRITICAL;
-last_pkt_xfer_done:
+/*
+ * 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_last_seg_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.
+ * Mark transfer as completed.
*/
-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;
+ or SCB_SGPTR, SG_LIST_NULL;
-wait_pkt_end:
+ /*
+ * Wait for the current context to finish to verify that
+ * no overrun condition has occurred.
+ */
+ test SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
call setjmp;
-END_CRITICAL;
-wait_pkt_end_loop:
- test SEQINTSRC, CTXTDONE jnz pkt_end;
+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;
-pkt_end:
-BEGIN_CRITICAL;
+ /* FALLTHROUGH */
+
+pkt_ctxt_done:
check_overrun;
or LONGJMP_ADDR[1], INVALID_ADDR;
- or SCB_SGPTR, SG_LIST_NULL;
- test SCB_CONTROL, STATUS_RCVD jnz last_pkt_complete;
+ /*
+ * 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;
/*
+ * Must wait until CDB xfer is over before issuing the
+ * clear channel.
+ */
+pkt_handle_cdb:
+ 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.
+ * 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_setscb;
- test SG_CACHE_SHADOW, LAST_SEG_DONE jz check_status_overrun;
- test SEQINTSRC, CTXTDONE jz return;
-status_IU_done:
-BEGIN_CRITICAL;
+ 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 last_pkt_complete;
+ jmp pkt_complete_scb_if_fifos_idle;
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.
- */
- test SHCNT[2], 0xFF jnz report_status_overrun;
- test SEQINTSRC, CTXTDONE jz return;
- test SHCNT[2], 0xFF jz status_IU_done;
-report_status_overrun:
+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)
- jmp status_IU_done;
-
-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;
-END_CRITICAL;
-
-/*
- * Must wait until CDB xfer is over before issuing the
- * clear channel.
- */
-pkt_handle_cdb:
- call setjmp_setscb;
- test SG_CACHE_SHADOW, LAST_SEG_DONE jz return;
- or LONGJMP_ADDR[1], INVALID_ADDR;
- mvi DFFSXFRCTL, CLRCHN ret;
+ /* 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:
@@ -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) {
diff --git a/sys/dev/aic7xxx/aic79xx_inline.h b/sys/dev/aic7xxx/aic79xx_inline.h
index 78462ad..94c8f4a 100644
--- a/sys/dev/aic7xxx/aic79xx_inline.h
+++ b/sys/dev/aic7xxx/aic79xx_inline.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_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 & SEQINT) != 0)
+ ahd_handle_seqint(ahd, intstat);
- if ((intstat & SCSIINT) != 0)
- ahd_handle_scsiint(ahd, intstat);
+ if ((intstat & SCSIINT) != 0)
+ ahd_handle_scsiint(ahd, intstat);
+ }
+ return (1);
}
#endif /* _AIC79XX_INLINE_H_ */
diff --git a/sys/dev/aic7xxx/aic79xx_osm.c b/sys/dev/aic7xxx/aic79xx_osm.c
index d6f2a49..c1650b3 100644
--- a/sys/dev/aic7xxx/aic79xx_osm.c
+++ b/sys/dev/aic7xxx/aic79xx_osm.c
@@ -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 */
diff --git a/sys/dev/aic7xxx/aic79xx_pci.c b/sys/dev/aic7xxx/aic79xx_pci.c
index 588681e..ada19db 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#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 @@ fail:
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.
OpenPOWER on IntegriCloud