diff options
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.c | 213 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.h | 74 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.reg | 47 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.seq | 333 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx_inline.h | 62 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx_osm.c | 4 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic79xx_pci.c | 49 |
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. |