summaryrefslogtreecommitdiffstats
path: root/sys/dev/aic
diff options
context:
space:
mode:
authorluoqi <luoqi@FreeBSD.org>1999-10-21 08:56:53 +0000
committerluoqi <luoqi@FreeBSD.org>1999-10-21 08:56:53 +0000
commit5dab878b88e91782e452b9ce86c4fde1506ae95e (patch)
tree9ffd6f41f5fa9cac5a2e4b5072cc82c92688700a /sys/dev/aic
parent37b81ac9d3bca142c50c370b5f86752e070dc23f (diff)
downloadFreeBSD-src-5dab878b88e91782e452b9ce86c4fde1506ae95e.zip
FreeBSD-src-5dab878b88e91782e452b9ce86c4fde1506ae95e.tar.gz
Adaptec 6260/6360 CAM driver.
Diffstat (limited to 'sys/dev/aic')
-rw-r--r--sys/dev/aic/aic.c1373
-rw-r--r--sys/dev/aic/aic6360reg.h331
-rw-r--r--sys/dev/aic/aic_isa.c227
-rw-r--r--sys/dev/aic/aicvar.h151
4 files changed, 2082 insertions, 0 deletions
diff --git a/sys/dev/aic/aic.c b/sys/dev/aic/aic.c
new file mode 100644
index 0000000..f99e216
--- /dev/null
+++ b/sys/dev/aic/aic.c
@@ -0,0 +1,1373 @@
+/*-
+ * Copyright (c) 1999 Luoqi Chen.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/clock.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+
+#include <cam/scsi/scsi_message.h>
+
+#include <dev/aic/aic6360reg.h>
+#include <dev/aic/aicvar.h>
+
+static void aic_action __P((struct cam_sim *sim, union ccb *ccb));
+static void aic_execute_scb __P((void *arg, bus_dma_segment_t *dm_segs,
+ int nseg, int error));
+static void aic_start __P((struct aic_softc *aic));
+static void aic_select __P((struct aic_softc *aic));
+static void aic_selected __P((struct aic_softc *aic));
+static void aic_reselected __P((struct aic_softc *aic));
+static void aic_cmd __P((struct aic_softc *aic));
+static void aic_msgin __P((struct aic_softc *aic));
+static void aic_handle_msgin __P((struct aic_softc *aic));
+static void aic_sched_msgout __P((struct aic_softc *aic, u_int8_t msg));
+static void aic_msgout __P((struct aic_softc *aic));
+static void aic_datain __P((struct aic_softc *aic));
+static void aic_dataout __P((struct aic_softc *aic));
+static void aic_done __P((struct aic_softc *aic, struct aic_scb *scb));
+static void aic_poll __P((struct cam_sim *sim));
+static void aic_timeout __P((void *arg));
+static void aic_scsi_reset __P((struct aic_softc *aic));
+static void aic_chip_reset __P((struct aic_softc *aic));
+static void aic_reset __P((struct aic_softc *aic, int initiate_reset));
+
+static struct aic_scb *free_scbs;
+
+static struct aic_scb *
+aic_get_scb(struct aic_softc *aic)
+{
+ struct aic_scb *scb;
+ int s = splcam();
+ if ((scb = free_scbs) != NULL)
+ free_scbs = (struct aic_scb *)free_scbs->ccb;
+ splx(s);
+ return (scb);
+}
+
+static void
+aic_free_scb(struct aic_softc *aic, struct aic_scb *scb)
+{
+ int s = splcam();
+ if ((aic->flags & AIC_RESOURCE_SHORTAGE) != 0 &&
+ (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
+ scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ aic->flags &= ~AIC_RESOURCE_SHORTAGE;
+ }
+ scb->flags = 0;
+ scb->ccb = (union ccb *)free_scbs;
+ free_scbs = scb;
+ splx(s);
+}
+
+static void
+aic_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct aic_softc *aic;
+ int s;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_action\n"));
+
+ aic = (struct aic_softc *)cam_sim_softc(sim);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO: /* Execute the requested I/O operation */
+ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */
+ {
+ struct aic_scb *scb;
+
+ if ((scb = aic_get_scb(aic)) == NULL) {
+ s = splcam();
+ aic->flags |= AIC_RESOURCE_SHORTAGE;
+ splx(s);
+ xpt_freeze_simq(aic->sim, /*count*/1);
+ ccb->ccb_h.status = CAM_REQUEUE_REQ;
+ xpt_done(ccb);
+ return;
+ }
+
+ scb->ccb = ccb;
+ ccb->ccb_h.ccb_scb_ptr = scb;
+ ccb->ccb_h.ccb_aic_ptr = aic;
+
+ scb->target = ccb->ccb_h.target_id;
+ scb->lun = ccb->ccb_h.target_lun;
+
+ if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
+ scb->cmd_len = ccb->csio.cdb_len;
+ if (ccb->ccb_h.flags & CAM_CDB_POINTER) {
+ if (ccb->ccb_h.flags & CAM_CDB_PHYS) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ aic_free_scb(aic, scb);
+ xpt_done(ccb);
+ return;
+ }
+ scb->cmd_ptr = ccb->csio.cdb_io.cdb_ptr;
+ } else {
+ scb->cmd_ptr = ccb->csio.cdb_io.cdb_bytes;
+ }
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+ if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) ||
+ (ccb->ccb_h.flags & CAM_DATA_PHYS)) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ aic_free_scb(aic, scb);
+ xpt_done(ccb);
+ return;
+ }
+ scb->data_ptr = ccb->csio.data_ptr;
+ scb->data_len = ccb->csio.dxfer_len;
+ } else {
+ scb->data_ptr = NULL;
+ scb->data_len = 0;
+ }
+ aic_execute_scb(scb, NULL, 0, 0);
+ } else {
+ scb->flags |= SCB_DEVICE_RESET;
+ aic_execute_scb(scb, NULL, 0, 0);
+ }
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts;
+ struct aic_tinfo *ti;
+
+ cts = &ccb->cts;
+ ti = &aic->tinfo[ccb->ccb_h.target_id];
+
+ s = splcam();
+
+ if ((cts->valid & CCB_TRANS_DISC_VALID) != 0 &&
+ (aic->flags & AIC_DISC_ENABLE) != 0) {
+ if ((cts->flags & CCB_TRANS_DISC_ENB) != 0)
+ ti->flags |= TINFO_DISC_ENB;
+ else
+ ti->flags &= ~TINFO_DISC_ENB;
+ }
+
+ if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) {
+ if ((cts->flags & CCB_TRANS_TAG_ENB) != 0)
+ ti->flags |= TINFO_TAG_ENB;
+ else
+ ti->flags &= ~TINFO_TAG_ENB;
+ }
+
+ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) {
+ ti->goal.period = cts->sync_period;
+ if (ti->goal.period != ti->current.period)
+ ti->flags |= TINFO_SDTR_NEGO;
+ }
+
+ if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) {
+ ti->goal.offset = cts->sync_offset;
+ if (ti->goal.offset != ti->current.offset)
+ ti->flags |= TINFO_SDTR_NEGO;
+ }
+
+ splx(s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts;
+ struct aic_tinfo *ti;
+
+ cts = &ccb->cts;
+ ti = &aic->tinfo[ccb->ccb_h.target_id];
+
+ s = splcam();
+
+ cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
+ if ((ti->flags & TINFO_DISC_ENB) != 0)
+ cts->flags |= CCB_TRANS_DISC_ENB;
+ if ((ti->flags & TINFO_TAG_ENB) != 0)
+ cts->flags |= CCB_TRANS_TAG_ENB;
+
+ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) {
+ cts->sync_period = ti->current.period;
+ cts->sync_offset = ti->current.offset;
+ } else {
+ cts->sync_period = ti->user.period;
+ cts->sync_offset = ti->user.offset;
+ }
+ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+
+ splx(s);
+
+ cts->valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID
+ | CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_DISC_VALID
+ | CCB_TRANS_TQ_VALID;
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ struct ccb_calc_geometry *ccg;
+ u_int32_t size_mb;
+ u_int32_t secs_per_cylinder;
+ int extended = 0;
+
+ ccg = &ccb->ccg;
+ size_mb = ccg->volume_size
+ / ((1024L * 1024L) / ccg->block_size);
+
+ if (size_mb >= 1024 && extended) {
+ ccg->heads = 255;
+ ccg->secs_per_track = 63;
+ } else {
+ ccg->heads = 64;
+ ccg->secs_per_track = 32;
+ }
+ secs_per_cylinder = ccg->heads * ccg->secs_per_track;
+ ccg->cylinders = ccg->volume_size / secs_per_cylinder;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_RESET_BUS: /* Reset the specified SCSI bus */
+ aic_reset(aic, /*initiate_reset*/TRUE);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ case XPT_PATH_INQ: /* Path routing inquiry */
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ cpi->version_num = 1; /* XXX??? */
+ cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = 0;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 7;
+ cpi->max_lun = 7;
+ cpi->initiator_id = aic->initiator;
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 3300;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+}
+
+static void
+aic_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
+{
+ struct aic_scb *scb = (struct aic_scb *)arg;
+ union ccb *ccb = scb->ccb;
+ struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr;
+ int s;
+
+ s = splcam();
+
+ if (ccb->ccb_h.status != CAM_REQ_INPROG) {
+ splx(s);
+ aic_free_scb(aic, scb);
+ xpt_done(ccb);
+ return;
+ }
+
+ scb->flags |= SCB_ACTIVE;
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+ TAILQ_INSERT_TAIL(&aic->pending_ccbs, &ccb->ccb_h, sim_links.tqe);
+
+ ccb->ccb_h.timeout_ch = timeout(aic_timeout, (caddr_t)scb,
+ (ccb->ccb_h.timeout * hz) / 1000);
+
+ aic_start(aic);
+ splx(s);
+}
+
+static void
+aic_start(struct aic_softc *aic)
+{
+ struct ccb_hdr *ccb_h;
+ struct aic_tinfo *ti;
+
+ if (aic->state != AIC_IDLE)
+ return;
+
+ TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) {
+ ti = &aic->tinfo[ccb_h->target_id];
+ if ((ti->lubusy & (1 << ccb_h->target_lun)) == 0) {
+ TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
+ aic->nexus = (struct aic_scb *)ccb_h->ccb_scb_ptr;
+ aic_select(aic);
+ return;
+ }
+ }
+
+ aic_outb(aic, SIMODE0, ENSELDI);
+ aic_outb(aic, SIMODE1, ENSCSIRST);
+ aic_outb(aic, SCSISEQ, ENRESELI);
+}
+
+static void
+aic_select(struct aic_softc *aic)
+{
+ struct aic_scb *scb = aic->nexus;
+
+ CAM_DEBUG(scb->ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("aic_select - ccb %p\n", scb->ccb));
+
+ aic->state = AIC_SELECTING;
+
+ aic_outb(aic, DMACNTRL1, 0);
+ aic_outb(aic, SCSIID, aic->initiator << OID_S | scb->target);
+ aic_outb(aic, SXFRCTL1, STIMO_256ms | ENSTIMER |
+ (aic->flags & AIC_PARITY_ENABLE ? ENSPCHK : 0));
+
+ aic_outb(aic, SIMODE0, ENSELDI|ENSELDO);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENSELTIMO);
+ aic_outb(aic, SCSISEQ, ENRESELI|ENSELO|ENAUTOATNO);
+}
+
+static void
+aic_selected(struct aic_softc *aic)
+{
+ struct aic_scb *scb = aic->nexus;
+ union ccb *ccb = scb->ccb;
+ struct aic_tinfo *ti = &aic->tinfo[scb->target];
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("aic_selected - ccb %p\n", ccb));
+
+ aic->state = AIC_HASNEXUS;
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ aic->msg_buf[0] = MSG_BUS_DEV_RESET;
+ aic->msg_len = 1;
+ aic->msg_outq = AIC_MSG_MSGBUF;
+ } else {
+ aic->msg_outq = AIC_MSG_IDENTIFY;
+ if ((ti->flags & TINFO_TAG_ENB) != 0 &&
+ (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0)
+ aic->msg_outq |= AIC_MSG_TAG_Q;
+ if ((ti->flags & TINFO_SDTR_NEGO) != 0)
+ aic->msg_outq |= AIC_MSG_SDTR;
+ }
+ if ((aic->msg_outq & AIC_MSG_TAG_Q) == 0)
+ ti->lubusy |= 1 << scb->lun;
+
+ aic_outb(aic, CLRSINT0, CLRSELDO);
+ aic_outb(aic, CLRSINT1, CLRBUSFREE);
+ aic_outb(aic, SCSISEQ, ENAUTOATNP);
+ aic_outb(aic, SIMODE0, 0);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
+ aic_outb(aic, SCSIRATE, ti->scsirate);
+}
+
+static void
+aic_reselected(struct aic_softc *aic)
+{
+ u_int8_t selid;
+ struct aic_tinfo *ti;
+
+ CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reselected\n"));
+
+ if (aic->nexus) {
+ TAILQ_INSERT_HEAD(&aic->pending_ccbs,
+ &aic->nexus->ccb->ccb_h, sim_links.tqe);
+ aic->nexus = NULL;
+ }
+
+ selid = aic_inb(aic, SELID) & SCSI_ID_MASK;
+ if (selid & (selid - 1)) {
+ ;
+ }
+
+ aic->state = AIC_RESELECTED;
+ aic->target = ffs(selid) - 1;
+ aic->lun = -1;
+ ti = &aic->tinfo[aic->target];
+
+ aic_outb(aic, CLRSINT0, CLRSELDI);
+ aic_outb(aic, CLRSINT1, CLRBUSFREE);
+ aic_outb(aic, SIMODE0, 0);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
+ aic_outb(aic, SCSISEQ, ENAUTOATNP);
+ aic_outb(aic, SCSIRATE, ti->scsirate);
+}
+
+static __inline int
+aic_spiordy(struct aic_softc *aic)
+{
+ int spincount = 100;
+ u_int8_t spiordy = 0;
+
+ while (spincount-- && !(aic_inb(aic, DMASTAT) & INTSTAT) &&
+ !(spiordy = aic_inb(aic, SSTAT0) & SPIORDY))
+ ;
+ return (spiordy);
+}
+
+static void
+aic_msgin(struct aic_softc *aic)
+{
+ int msglen;
+
+ CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgin\n"));
+
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
+ aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);
+
+ aic->flags &= ~AIC_DROP_MSGIN;
+ aic->msg_len = 0;
+ do {
+ if (aic_inb(aic, SSTAT1) & SCSIPERR) {
+ aic_outb(aic, CLRSINT1, CLRSCSIPERR);
+ aic->flags |= AIC_DROP_MSGIN;
+ aic_sched_msgout(aic, MSG_PARITY_ERROR);
+ }
+ if ((aic->flags & AIC_DROP_MSGIN)) {
+ aic_inb(aic, SCSIDAT);
+ continue;
+ }
+ aic->msg_buf[aic->msg_len++] = aic_inb(aic, SCSIBUS);
+ if (aic->msg_buf[0] == MSG_EXTENDED) {
+ if (aic->msg_len < 2) {
+ (void) aic_inb(aic, SCSIDAT);
+ continue;
+ }
+ switch (aic->msg_buf[2]) {
+ case MSG_EXT_SDTR:
+ msglen = MSG_EXT_SDTR_LEN;
+ break;
+ case MSG_EXT_WDTR:
+ msglen = MSG_EXT_WDTR_LEN;
+ break;
+ default:
+ msglen = 0;
+ break;
+ }
+ if (aic->msg_buf[1] != msglen) {
+ aic->flags |= AIC_DROP_MSGIN;
+ aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
+ }
+ msglen += 2;
+ } else if (aic->msg_buf[0] & 0x20)
+ msglen = 2;
+ else
+ msglen = 1;
+ if (aic->msg_len == msglen) {
+ aic_handle_msgin(aic);
+ aic->msg_len = 0;
+ }
+ (void) aic_inb(aic, SCSIDAT);
+ } while (aic_spiordy(aic));
+
+ aic_outb(aic, SXFRCTL0, CHEN);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
+}
+
+static void
+aic_handle_msgin(struct aic_softc *aic)
+{
+ struct aic_scb *scb;
+ struct ccb_hdr *ccb_h;
+ struct aic_tinfo *ti;
+ struct ccb_trans_settings neg;
+
+ if (aic->state == AIC_RESELECTED) {
+ int tag;
+
+ ti = &aic->tinfo[aic->target];
+
+ if (MSG_ISIDENTIFY(aic->msg_buf[0])) {
+ aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK;
+ if (ti->flags & TINFO_TAG_ENB)
+ return;
+ else
+ tag = -1;
+ } else if (aic->msg_buf[0] >= MSG_SIMPLE_Q_TAG &&
+ aic->msg_buf[0] <= MSG_ORDERED_Q_TAG) {
+ tag = aic->msg_buf[1];
+ } else {
+ aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
+ return;
+ }
+
+ TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
+ scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
+ if (ccb_h->target_id == aic->target &&
+ ccb_h->target_lun == aic->lun &&
+ (tag == -1 || scb->tag == tag))
+ break;
+ }
+
+ if (!ccb_h) {
+ aic_sched_msgout(aic, MSG_ABORT); /* MSG_ABORT_TAG?*/
+ return;
+ }
+
+ TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
+ aic->nexus = scb;
+ scb->flags &= ~SCB_DISCONNECTED;
+ aic->state = AIC_HASNEXUS;
+ return;
+ }
+
+ switch (aic->msg_buf[0]) {
+ case MSG_CMDCOMPLETE: {
+ struct ccb_scsiio *csio;
+ scb = aic->nexus;
+ ccb_h = &scb->ccb->ccb_h;
+ csio = &scb->ccb->csio;
+ ccb_h->status &= ~CAM_STATUS_MASK;
+ if ((scb->flags & SCB_SENSE) != 0) {
+ scb->flags &= ~SCB_SENSE;
+ csio->sense_resid = scb->data_len;
+ if (scb->status == SCSI_STATUS_OK) {
+ ccb_h->status |=
+ CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID;
+ scsi_sense_print(csio);
+ } else {
+ ccb_h->status |= CAM_AUTOSENSE_FAIL;
+ printf("ccb %p sense failed %x\n",
+ ccb_h, scb->status);
+ }
+ } else {
+ csio->scsi_status = scb->status;
+ csio->resid = scb->data_len;
+ if (scb->status == SCSI_STATUS_OK) {
+ ccb_h->status |= CAM_REQ_CMP;
+ } else if ((ccb_h->flags & CAM_DIS_AUTOSENSE) == 0 &&
+ (csio->scsi_status == SCSI_STATUS_CHECK_COND ||
+ csio->scsi_status == SCSI_STATUS_CMD_TERMINATED)) {
+ scb->flags |= SCB_SENSE;
+ aic->flags |= AIC_BUSFREE_OK;
+ return;
+ } else
+ ccb_h->status |= CAM_SCSI_STATUS_ERROR;
+ }
+ aic_done(aic, scb);
+ aic->flags |= AIC_BUSFREE_OK;
+ break;
+ }
+ case MSG_EXTENDED:
+ switch (aic->msg_buf[2]) {
+ case MSG_EXT_SDTR:
+ scb = aic->nexus;
+ ti = &aic->tinfo[scb->target];
+ if (ti->flags & TINFO_SDTR_SENT) {
+ ti->current.period = aic->msg_buf[3];
+ ti->current.offset = aic->msg_buf[4];
+ } else {
+ ti->current.period = aic->msg_buf[3] =
+ max(ti->goal.period, aic->msg_buf[3]);
+ ti->current.offset = aic->msg_buf[4] =
+ min(ti->goal.offset, aic->msg_buf[4]);
+ aic_sched_msgout(aic, 0);
+ }
+ ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
+ ti->scsirate = ti->current.offset ? ti->current.offset |
+ ((ti->current.period * 4 + 49) / 50 - 2) << 4 : 0;
+ aic_outb(aic, SCSIRATE, ti->scsirate);
+ neg.sync_period = ti->goal.period = ti->current.period;
+ neg.sync_offset = ti->goal.offset = ti->current.offset;
+ neg.valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID;
+ ccb_h = &scb->ccb->ccb_h;
+ xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1);
+ xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg);
+ break;
+ case MSG_EXT_WDTR:
+ default:
+ aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
+ break;
+ }
+ break;
+ case MSG_DISCONNECT:
+ scb = aic->nexus;
+ ccb_h = &scb->ccb->ccb_h;
+ TAILQ_INSERT_HEAD(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
+ scb->flags |= SCB_DISCONNECTED;
+ ti = &aic->tinfo[scb->target];
+ aic->flags |= AIC_BUSFREE_OK;
+ break;
+ case MSG_MESSAGE_REJECT:
+ switch (aic->msg_outq & -aic->msg_outq) {
+ case AIC_MSG_TAG_Q:
+ scb = aic->nexus;
+ ti = &aic->tinfo[scb->target];
+ ti->flags &= ~TINFO_TAG_ENB;
+ break;
+ case AIC_MSG_SDTR:
+ scb = aic->nexus;
+ ti = &aic->tinfo[scb->target];
+ ti->current.period = ti->goal.period = 0;
+ ti->current.offset = ti->goal.offset = 0;
+ ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
+ ti->scsirate = 0;
+ aic_outb(aic, SCSIRATE, ti->scsirate);
+ neg.sync_period = ti->current.period;
+ neg.sync_offset = ti->current.offset;
+ neg.valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID;
+ ccb_h = &scb->ccb->ccb_h;
+ xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1);
+ xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg);
+ break;
+ default:
+ break;
+ }
+ break;
+ case MSG_SAVEDATAPOINTER:
+ break;
+ case MSG_RESTOREPOINTERS:
+ break;
+ case MSG_NOOP:
+ break;
+ default:
+ aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
+ break;
+ }
+}
+
+static void
+aic_sched_msgout(struct aic_softc *aic, u_int8_t msg)
+{
+ if (msg) {
+ aic->msg_buf[0] = msg;
+ aic->msg_len = 1;
+ }
+ aic->msg_outq |= AIC_MSG_MSGBUF;
+ aic_outb(aic, SCSISIGO, aic_inb(aic, SCSISIGI) | ATNO);
+}
+
+static void
+aic_msgout(struct aic_softc *aic)
+{
+ struct aic_scb *scb;
+ union ccb *ccb;
+ struct aic_tinfo *ti;
+ int msgidx = 0;
+
+ CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgout\n"));
+
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
+ aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);
+
+ if (aic->prev_phase == PH_MSGOUT)
+ aic->msg_outq = aic->msg_sent;
+
+ do {
+ int q = aic->msg_outq;
+ if (msgidx > 0 && msgidx == aic->msg_len) {
+ q &= -q;
+ aic->msg_sent |= q;
+ aic->msg_outq ^= q;
+ q = aic->msg_outq;
+ msgidx = 0;
+ }
+ if (msgidx == 0) {
+ switch (q & -q) {
+ case AIC_MSG_IDENTIFY:
+ scb = aic->nexus;
+ ccb = scb->ccb;
+ ti = &aic->tinfo[scb->target];
+ aic->msg_buf[0] = MSG_IDENTIFY(scb->lun,
+ (ti->flags & TINFO_DISC_ENB) &&
+ (ccb->ccb_h.flags & CAM_DIS_DISCONNECT));
+ aic->msg_len = 1;
+ break;
+ case AIC_MSG_TAG_Q:
+ scb = aic->nexus;
+ ccb = scb->ccb;
+ aic->msg_buf[0] = ccb->csio.tag_action;
+ aic->msg_buf[1] = scb->tag;
+ aic->msg_len = 2;
+ break;
+ case AIC_MSG_SDTR:
+ scb = aic->nexus;
+ ti = &aic->tinfo[scb->target];
+ aic->msg_buf[0] = MSG_EXTENDED;
+ aic->msg_buf[1] = MSG_EXT_SDTR_LEN;
+ aic->msg_buf[2] = MSG_EXT_SDTR;
+ aic->msg_buf[3] = ti->goal.period;
+ aic->msg_buf[4] = ti->goal.offset;
+ aic->msg_len = MSG_EXT_SDTR_LEN + 2;
+ ti->flags |= TINFO_SDTR_SENT;
+ break;
+ case AIC_MSG_MSGBUF:
+ if (aic->msg_buf[0] == MSG_BUS_DEV_RESET ||
+ aic->msg_buf[0] == MSG_ABORT ||
+ aic->msg_buf[0] == MSG_ABORT_TAG)
+ aic->flags |= AIC_BUSFREE_OK;
+ break;
+ }
+ }
+ if ((q & (q - 1)) == 0 && msgidx == aic->msg_len - 1)
+ aic_outb(aic, CLRSINT1, CLRATNO);
+ aic_outb(aic, SCSIDAT, aic->msg_buf[msgidx++]);
+ } while (aic_spiordy(aic));
+
+ aic_outb(aic, SXFRCTL0, CHEN);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
+}
+
+static void
+aic_datain(struct aic_softc *aic)
+{
+ struct aic_scb *scb = aic->nexus;
+ u_int8_t dmastat;
+ int n;
+
+ CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_datain\n"));
+
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
+ aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);
+
+ while (scb->data_len > 0) {
+ for (;;) {
+ dmastat = aic_inb(aic, DMASTAT);
+ if (dmastat & (INTSTAT|DFIFOFULL))
+ break;
+ }
+ if (dmastat & DFIFOFULL) {
+ n = FIFOSIZE;
+ } else {
+ while (!(aic_inb(aic, SSTAT2) & SEMPTY))
+ ;
+ n = aic_inb(aic, FIFOSTAT);
+ }
+ n = imin(scb->data_len, n);
+ if (n >= 12 && (aic->flags & AIC_DWIO_ENABLE)) {
+ aic_outb(aic, DMACNTRL0, ENDMA|DWORDPIO);
+ aic_insl(aic, DMADATALONG, scb->data_ptr, n >> 2);
+ scb->data_ptr += n & ~3;
+ scb->data_len -= n & ~3;
+ n &= 3;
+ }
+ if (n >= 8) {
+ aic_outb(aic, DMACNTRL0, ENDMA);
+ aic_insw(aic, DMADATA, scb->data_ptr, n >> 1);
+ scb->data_ptr += n & ~1;
+ scb->data_len -= n & ~1;
+ n &= 1;
+ }
+ if (n) {
+ aic_outb(aic, DMACNTRL0, ENDMA|B8MODE);
+ aic_insb(aic, DMADATA, scb->data_ptr, n);
+ scb->data_ptr += n;
+ scb->data_len -= n;
+ }
+
+ if (dmastat & INTSTAT)
+ break;
+ }
+
+ aic_outb(aic, SXFRCTL0, CHEN);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
+}
+
+static void
+aic_dataout(struct aic_softc *aic)
+{
+ struct aic_scb *scb = aic->nexus;
+ u_int8_t dmastat;
+ int n;
+
+ CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_dataout\n"));
+
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
+ aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);
+
+ while (scb->data_len > 0) {
+ for (;;) {
+ dmastat = aic_inb(aic, DMASTAT);
+ if (dmastat & (INTSTAT|DFIFOEMP))
+ break;
+ }
+ if (dmastat & INTSTAT)
+ break;
+ n = imin(scb->data_len, FIFOSIZE);
+ if (n >= 12 && (aic->flags & AIC_DWIO_ENABLE)) {
+ aic_outb(aic, DMACNTRL0, ENDMA|WRITE|DWORDPIO);
+ aic_outsl(aic, DMADATALONG, scb->data_ptr, n >> 2);
+ scb->data_ptr += n & ~3;
+ scb->data_len -= n & ~3;
+ n &= 3;
+ }
+ if (n >= 8) {
+ aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
+ aic_outsl(aic, DMADATA, scb->data_ptr, n >> 2);
+ scb->data_ptr += n & ~1;
+ scb->data_len -= n & ~1;
+ n &= 1;
+ }
+ if (n) {
+ aic_outb(aic, DMACNTRL0, ENDMA|WRITE|B8MODE);
+ aic_outsb(aic, DMADATA, scb->data_ptr, n);
+ scb->data_ptr += n;
+ scb->data_len -= n;
+ }
+ }
+
+ for (;;) {
+ dmastat = aic_inb(aic, DMASTAT);
+ if (dmastat & INTSTAT)
+ break;
+ if ((dmastat & DFIFOEMP) && (aic_inb(aic, SSTAT2) & SEMPTY))
+ break;
+ }
+
+ aic_outb(aic, SXFRCTL0, CHEN);
+ aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
+}
+
+static void
+aic_cmd(struct aic_softc *aic)
+{
+ struct aic_scb *scb = aic->nexus;
+ struct scsi_request_sense sense_cmd;
+
+ if (scb->flags & SCB_SENSE) {
+ sense_cmd.opcode = REQUEST_SENSE;
+ sense_cmd.byte2 = scb->lun << 2;
+ sense_cmd.length = scb->ccb->csio.sense_len;
+ sense_cmd.control = 0;
+ sense_cmd.unused[0] = 0;
+ sense_cmd.unused[1] = 0;
+ scb->cmd_ptr = (u_int8_t *)&sense_cmd;
+ scb->cmd_len = sizeof(sense_cmd);
+ scb->data_ptr = (u_int8_t *)&scb->ccb->csio.sense_data;
+ scb->data_len = scb->ccb->csio.sense_len;
+ }
+
+ aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
+ aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);
+ aic_outsw(aic, DMADATA, (u_int16_t *)scb->cmd_ptr,
+ scb->cmd_len >> 1);
+ while ((aic_inb(aic, SSTAT2) & SEMPTY) == 0 &&
+ (aic_inb(aic, DMASTAT) & INTSTAT) == 0)
+ ;
+ aic_outb(aic, SXFRCTL0, CHEN);
+}
+
+static void
+aic_done(struct aic_softc *aic, struct aic_scb *scb)
+{
+ union ccb *ccb = scb->ccb;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("aic_done - ccb %p status %x resid %d\n",
+ ccb, ccb->ccb_h.status, ccb->csio.resid));
+
+ untimeout(aic_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch);
+
+ if ((scb->flags & SCB_DEVICE_RESET) != 0 &&
+ ccb->ccb_h.func_code != XPT_RESET_DEV) {
+ struct cam_path *path;
+ struct ccb_hdr *ccb_h;
+ cam_status error;
+
+ error = xpt_create_path(&path, /*periph*/NULL,
+ cam_sim_path(aic->sim),
+ scb->target,
+ CAM_LUN_WILDCARD);
+
+ if (error == CAM_REQ_CMP)
+ xpt_async(AC_SENT_BDR, path, NULL);
+
+ ccb_h = TAILQ_FIRST(&aic->pending_ccbs);
+ while (ccb_h != NULL) {
+ struct aic_scb *pending_scb;
+
+ pending_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
+ if (ccb_h->target_id == scb->target) {
+ ccb_h->status = CAM_BDR_SENT;
+ ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
+ TAILQ_REMOVE(&aic->pending_ccbs,
+ &pending_scb->ccb->ccb_h, sim_links.tqe);
+ aic_done(aic, pending_scb);
+ } else {
+ ccb_h->timeout_ch =
+ timeout(aic_timeout, (caddr_t)pending_scb,
+ (ccb_h->timeout * hz) / 1000);
+ ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
+ }
+ }
+
+ ccb_h = TAILQ_FIRST(&aic->nexus_ccbs);
+ while (ccb_h != NULL) {
+ struct aic_scb *nexus_scb;
+
+ nexus_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
+ if (ccb_h->target_id == scb->target) {
+ ccb_h->status = CAM_BDR_SENT;
+ ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
+ TAILQ_REMOVE(&aic->nexus_ccbs,
+ &nexus_scb->ccb->ccb_h, sim_links.tqe);
+ aic_done(aic, nexus_scb);
+ } else {
+ ccb_h->timeout_ch =
+ timeout(aic_timeout, (caddr_t)nexus_scb,
+ (ccb_h->timeout * hz) / 1000);
+ ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
+ }
+ }
+ }
+
+ if (aic->nexus == scb || scb->flags & SCB_DISCONNECTED)
+ aic->tinfo[scb->target].lubusy &= ~(1 << scb->lun);
+
+ if (aic->nexus == scb) {
+ aic->nexus = NULL;
+ aic->state = AIC_IDLE;
+ }
+ aic_free_scb(aic, scb);
+ xpt_done(ccb);
+}
+
+static void
+aic_poll(struct cam_sim *sim)
+{
+ aic_intr(cam_sim_softc(sim));
+}
+
+static void
+aic_timeout(void *arg)
+{
+ struct aic_scb *scb = (struct aic_scb *)arg;
+ union ccb *ccb = scb->ccb;
+ struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr;
+ int s;
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("ccb %p - timed out", ccb);
+ if (aic->nexus && aic->nexus != scb)
+ printf(", nexus %p", aic->nexus->ccb);
+ printf("\n");
+
+ s = splcam();
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ splx(s);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("ccb %p - timed out already completed\n", ccb);
+ return;
+ }
+
+ if ((scb->flags & SCB_DEVICE_RESET) == 0 && aic->nexus == scb) {
+ struct ccb_hdr *ccb_h = &scb->ccb->ccb_h;
+
+ if ((ccb_h->status & CAM_RELEASE_SIMQ) == 0) {
+ xpt_freeze_simq(aic->sim, /*count*/1);
+ ccb_h->status |= CAM_RELEASE_SIMQ;
+ }
+
+ TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) {
+ untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr,
+ ccb_h->timeout_ch);
+ }
+
+ TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
+ untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr,
+ ccb_h->timeout_ch);
+ }
+
+ scb->flags |= SCB_DEVICE_RESET;
+ ccb->ccb_h.timeout_ch =
+ timeout(aic_timeout, (caddr_t)scb, 5 * hz);
+ aic_sched_msgout(aic, MSG_BUS_DEV_RESET);
+ } else {
+ if (aic->nexus == scb) {
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
+ aic_done(aic, scb);
+ }
+ aic_reset(aic, /*initiate_reset*/TRUE);
+ }
+
+ splx(s);
+}
+
+void
+aic_intr(void *arg)
+{
+ struct aic_softc *aic = (struct aic_softc *)arg;
+ u_int8_t sstat0, sstat1;
+ union ccb *ccb;
+ struct aic_scb *scb;
+
+ if (!(aic_inb(aic, DMASTAT) & INTSTAT))
+ return;
+
+ aic_outb(aic, DMACNTRL0, 0);
+
+ sstat0 = aic_inb(aic, SSTAT0);
+ sstat1 = aic_inb(aic, SSTAT1);
+
+ if ((sstat1 & SCSIRSTI) != 0) {
+ aic_outb(aic, CLRSINT1, CLRSCSIRSTI);
+ aic_reset(aic, /*initiate_reset*/FALSE);
+ return;
+ }
+
+ if ((sstat1 & SCSIPERR) != 0) {
+ aic_outb(aic, CLRSINT1, CLRSCSIPERR);
+ aic_sched_msgout(aic, MSG_PARITY_ERROR);
+ aic_outb(aic, DMACNTRL0, INTEN);
+ return;
+ }
+
+ if (aic_inb(aic, SSTAT4)) {
+ aic_outb(aic, CLRSERR, CLRSYNCERR|CLRFWERR|CLRFRERR);
+ aic_reset(aic, /*initiate_reset*/TRUE);
+ return;
+ }
+
+ if (aic->state != AIC_HASNEXUS) {
+ if ((sstat0 & SELDI) != 0) {
+ aic_reselected(aic);
+ aic_outb(aic, DMACNTRL0, INTEN);
+ return;
+ }
+
+ if ((sstat0 & SELDO) != 0) {
+ aic_selected(aic);
+ aic_outb(aic, DMACNTRL0, INTEN);
+ return;
+ }
+
+ if ((sstat1 & SELTO) != 0) {
+ scb = aic->nexus;
+ ccb = scb->ccb;
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ aic_done(aic, scb);
+ while ((sstat1 & BUSFREE) == 0)
+ sstat1 = aic_inb(aic, SSTAT1);
+ aic->flags |= AIC_BUSFREE_OK;
+ }
+ }
+
+ if ((sstat1 & REQINIT) != 0) {
+ u_int8_t phase = aic_inb(aic, SCSISIGI) & PH_MASK;
+ aic_outb(aic, SCSISIGO, phase);
+ aic_outb(aic, CLRSINT1, CLRPHASECHG);
+
+ switch (phase) {
+ case PH_MSGOUT:
+ aic_msgout(aic);
+ break;
+ case PH_MSGIN:
+ aic_msgin(aic);
+ break;
+ case PH_STAT:
+ scb = aic->nexus;
+ ccb = scb->ccb;
+ aic_outb(aic, DMACNTRL0, 0);
+ aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);
+ scb->status = aic_inb(aic, SCSIDAT);
+ aic_outb(aic, SXFRCTL0, CHEN);
+ break;
+ case PH_CMD:
+ aic_cmd(aic);
+ break;
+ case PH_DATAIN:
+ aic_datain(aic);
+ break;
+ case PH_DATAOUT:
+ aic_dataout(aic);
+ break;
+ }
+ aic->prev_phase = phase;
+ aic_outb(aic, DMACNTRL0, INTEN);
+ return;
+ }
+
+ if ((sstat1 & BUSFREE) != 0) {
+ aic_outb(aic, SCSISEQ, 0);
+ aic_outb(aic, CLRSINT0, sstat0);
+ aic_outb(aic, CLRSINT1, sstat1);
+ aic_outb(aic, SIMODE0, ENSELDI);
+ aic_outb(aic, SIMODE1, ENSCSIRST);
+ aic_outb(aic, SCSISEQ, ENRESELI);
+ if ((scb = aic->nexus)) {
+ if ((aic->flags & AIC_BUSFREE_OK) == 0) {
+ ccb = scb->ccb;
+ ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
+ aic_done(aic, scb);
+ } else if (scb->flags & SCB_SENSE) {
+ aic->flags &= ~AIC_BUSFREE_OK;
+ aic_select(aic);
+ aic_outb(aic, DMACNTRL0, INTEN);
+ return;
+ }
+ }
+ aic->flags &= ~AIC_BUSFREE_OK;
+ aic->state = AIC_IDLE;
+ aic_start(aic);
+ aic_outb(aic, DMACNTRL0, INTEN);
+ return;
+ }
+
+ printf("aic_intr: unexpected intr sstat0 %x sstat1 %x\n",
+ sstat0, sstat1);
+ aic_outb(aic, DMACNTRL0, INTEN);
+}
+
+static void
+aic_chip_reset(struct aic_softc *aic)
+{
+ /*
+ * Doc. recommends to clear these two registers before
+ * operations commence
+ */
+ aic_outb(aic, SCSITEST, 0);
+ aic_outb(aic, TEST, 0);
+
+ /* Reset SCSI-FIFO and abort any transfers */
+ aic_outb(aic, SXFRCTL0, CHEN|CLRCH|CLRSTCNT);
+
+ /* Reset HOST-FIFO */
+ aic_outb(aic, DMACNTRL0, RSTFIFO);
+ aic_outb(aic, DMACNTRL1, 0);
+
+ /* Disable all selection features */
+ aic_outb(aic, SCSISEQ, 0);
+ aic_outb(aic, SXFRCTL1, 0);
+
+ /* Disable interrupts */
+ aic_outb(aic, SIMODE0, 0);
+ aic_outb(aic, SIMODE1, 0);
+
+ /* Clear interrupts */
+ aic_outb(aic, CLRSINT0, 0x7f);
+ aic_outb(aic, CLRSINT1, 0xef);
+
+ /* Disable synchronous transfers */
+ aic_outb(aic, SCSIRATE, 0);
+
+ /* Haven't seen ant errors (yet) */
+ aic_outb(aic, CLRSERR, 0x07);
+
+ /* Set our SCSI-ID */
+ aic_outb(aic, SCSIID, aic->initiator << OID_S);
+ aic_outb(aic, BRSTCNTRL, EISA_BRST_TIM);
+}
+
+static void
+aic_scsi_reset(struct aic_softc *aic)
+{
+ aic_outb(aic, SCSISEQ, SCSIRSTO);
+ DELAY(500);
+ aic_outb(aic, SCSISEQ, 0);
+ DELAY(50);
+}
+
+static void
+aic_reset(struct aic_softc *aic, int initiate_reset)
+{
+ struct ccb_hdr *ccb_h;
+
+ CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reset\n"));
+
+ if (initiate_reset)
+ aic_scsi_reset(aic);
+ aic_chip_reset(aic);
+
+ xpt_async(AC_BUS_RESET, aic->path, NULL);
+
+ while ((ccb_h = TAILQ_FIRST(&aic->pending_ccbs)) != NULL) {
+ TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
+ ccb_h->status &= ~CAM_STATUS_MASK;
+ ccb_h->status |= CAM_SCSI_BUS_RESET;
+ aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
+ }
+
+ while ((ccb_h = TAILQ_FIRST(&aic->nexus_ccbs)) != NULL) {
+ TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
+ ccb_h->status &= ~CAM_STATUS_MASK;
+ ccb_h->status |= CAM_SCSI_BUS_RESET;
+ aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
+ }
+
+ if (aic->nexus) {
+ ccb_h = &aic->nexus->ccb->ccb_h;
+ ccb_h->status &= ~CAM_STATUS_MASK;
+ ccb_h->status |= CAM_SCSI_BUS_RESET;
+ aic_done(aic, aic->nexus);
+ }
+
+ aic->state = AIC_IDLE;
+
+ aic_outb(aic, DMACNTRL0, INTEN);
+}
+
+static void
+aic_init(struct aic_softc *aic)
+{
+ struct aic_scb *scb;
+ struct aic_tinfo *ti;
+ u_int8_t porta, portb;
+ int i;
+
+ TAILQ_INIT(&aic->pending_ccbs);
+ TAILQ_INIT(&aic->nexus_ccbs);
+ aic->nexus = NULL;
+ aic->state = AIC_IDLE;
+ aic->prev_phase = -1;
+ aic->flags = 0;
+
+ aic_chip_reset(aic);
+ aic_scsi_reset(aic);
+
+ porta = aic_inb(aic, PORTA);
+ portb = aic_inb(aic, PORTB);
+
+ aic->initiator = PORTA_ID(porta);
+ if (PORTA_PARITY(porta))
+ aic->flags |= AIC_PARITY_ENABLE;
+ if (PORTB_DISC(portb))
+ aic->flags |= AIC_DISC_ENABLE;
+ if (PORTB_DMA(portb))
+ aic->flags |= AIC_DMA_ENABLE;
+ if (aic_inb(aic, REV))
+ aic->flags |= AIC_DWIO_ENABLE;
+
+ free_scbs = NULL;
+ for (i = 255; i >= 0; i--) {
+ scb = &aic->scbs[i];
+ scb->tag = i;
+ aic_free_scb(aic, scb);
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (i == aic->initiator)
+ continue;
+ ti = &aic->tinfo[i];
+ bzero(ti, sizeof(*ti));
+ ti->flags = 0;
+ ti->user.period = AIC_SYNC_PERIOD;
+ ti->user.offset = AIC_SYNC_OFFSET;
+ ti->scsirate = 0;
+ }
+
+ aic_outb(aic, DMACNTRL0, INTEN);
+}
+
+int
+aic_probe(struct aic_softc *aic)
+{
+ int i;
+
+ /* Remove aic6360 from possible powerdown mode */
+ aic_outb(aic, DMACNTRL0, 0);
+
+#define STSIZE 16
+ aic_outb(aic, DMACNTRL1, 0); /* Reset stack pointer */
+ for (i = 0; i < STSIZE; i++)
+ aic_outb(aic, STACK, i);
+
+ /* See if we can pull out the same sequence */
+ aic_outb(aic, DMACNTRL1, 0);
+ for (i = 0; i < STSIZE && aic_inb(aic, STACK) == i; i++)
+ ;
+ if (i != STSIZE)
+ return (ENXIO);
+#undef STSIZE
+ return (0);
+}
+
+int
+aic_attach(struct aic_softc *aic)
+{
+ struct cam_devq *devq;
+
+ /*
+ * Create the device queue for our SIM.
+ */
+ devq = cam_simq_alloc(256);
+ if (devq == NULL)
+ return (ENOMEM);
+
+ /*
+ * Construct our SIM entry
+ */
+ aic->sim = cam_sim_alloc(aic_action, aic_poll, "aic", aic,
+ aic->unit, 2, 256, devq);
+ if (aic->sim == NULL) {
+ cam_simq_free(devq);
+ return (ENOMEM);
+ }
+
+ if (xpt_bus_register(aic->sim, 0) != CAM_SUCCESS) {
+ cam_sim_free(aic->sim, /*free_devq*/TRUE);
+ return (ENXIO);
+ }
+
+ if (xpt_create_path(&aic->path, /*periph*/NULL,
+ cam_sim_path(aic->sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(aic->sim));
+ cam_sim_free(aic->sim, /*free_devq*/TRUE);
+ return (ENXIO);
+ }
+
+ aic_init(aic);
+ return (0);
+}
+
+int
+aic_detach(struct aic_softc *aic)
+{
+ xpt_async(AC_LOST_DEVICE, aic->path, NULL);
+ xpt_free_path(aic->path);
+ xpt_bus_deregister(cam_sim_path(aic->sim));
+ cam_sim_free(aic->sim, /*free_devq*/TRUE);
+ return (0);
+}
diff --git a/sys/dev/aic/aic6360reg.h b/sys/dev/aic/aic6360reg.h
new file mode 100644
index 0000000..7dafac4
--- /dev/null
+++ b/sys/dev/aic/aic6360reg.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1994 Charles Hannum.
+ * Copyright (c) 1994 Jarle Greipsland.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jarle Greipsland
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define SCSISEQ 0x00 /* SCSI sequence control */
+#define SXFRCTL0 0x01 /* SCSI transfer control 0 */
+#define SXFRCTL1 0x02 /* SCSI transfer control 1 */
+#define SCSISIGI 0x03 /* SCSI signal in */
+#define SCSISIGO 0x03 /* SCSI signal out */
+#define SCSIRATE 0x04 /* SCSI rate control */
+#define SCSIID 0x05 /* SCSI ID */
+#define SELID 0x05 /* Selection/Reselection ID */
+#define SCSIDAT 0x06 /* SCSI Latched Data */
+#define SCSIBUS 0x07 /* SCSI Data Bus*/
+#define STCNT0 0x08 /* SCSI transfer count */
+#define STCNT1 0x09
+#define STCNT2 0x0a
+#define CLRSINT0 0x0b /* Clear SCSI interrupts 0 */
+#define SSTAT0 0x0b /* SCSI interrupt status 0 */
+#define CLRSINT1 0x0c /* Clear SCSI interrupts 1 */
+#define SSTAT1 0x0c /* SCSI status 1 */
+#define SSTAT2 0x0d /* SCSI status 2 */
+#define SCSITEST 0x0e /* SCSI test control */
+#define SSTAT3 0x0e /* SCSI status 3 */
+#define CLRSERR 0x0f /* Clear SCSI errors */
+#define SSTAT4 0x0f /* SCSI status 4 */
+#define SIMODE0 0x10 /* SCSI interrupt mode 0 */
+#define SIMODE1 0x11 /* SCSI interrupt mode 1 */
+#define DMACNTRL0 0x12 /* DMA control 0 */
+#define DMACNTRL1 0x13 /* DMA control 1 */
+#define DMASTAT 0x14 /* DMA status */
+#define FIFOSTAT 0x15 /* FIFO status */
+#define DMADATA 0x16 /* DMA data */
+#define DMADATAL 0x16 /* DMA data low byte */
+#define DMADATAH 0x17 /* DMA data high byte */
+#define BRSTCNTRL 0x18 /* Burst Control */
+#define DMADATALONG 0x18
+#define PORTA 0x1a /* Port A */
+#define PORTB 0x1b /* Port B */
+#define REV 0x1c /* Revision (001 for 6360) */
+#define STACK 0x1d /* Stack */
+#define TEST 0x1e /* Test register */
+#define ID 0x1f /* ID register */
+
+#define IDSTRING "(C)1991ADAPTECAIC6360 "
+
+/* What all the bits do */
+
+/* SCSISEQ */
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRESELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/* SXFRCTL0 */
+#define SCSIEN 0x80
+#define DMAEN 0x40
+#define CHEN 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define CLRCH 0x02
+
+/* SXFRCTL1 */
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL1 0x10
+#define STIMESEL0 0x08
+#define STIMO_256ms 0x00
+#define STIMO_128ms 0x08
+#define STIMO_64ms 0x10
+#define STIMO_32ms 0x18
+#define ENSTIMER 0x04
+#define BYTEALIGN 0x02
+
+/* SCSISIGI */
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+/* Important! The 3 most significant bits of this register, in initiator mode,
+ * represents the "expected" SCSI bus phase and can be used to trigger phase
+ * mismatch and phase change interrupts. But more important: If there is a
+ * phase mismatch the chip will not transfer any data! This is actually a nice
+ * feature as it gives us a bit more control over what is happening when we are
+ * bursting data (in) through the FIFOs and the phase suddenly changes from
+ * DATA IN to STATUS or MESSAGE IN. The transfer will stop and wait for the
+ * proper phase to be set in this register instead of dumping the bits into the
+ * FIFOs.
+ */
+/* SCSISIGO */
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+/* Information transfer phases */
+#define PH_DATAOUT (0)
+#define PH_DATAIN (IOI)
+#define PH_CMD (CDI)
+#define PH_STAT (CDI|IOI)
+#define PH_MSGOUT (MSGI|CDI)
+#define PH_MSGIN (MSGI|CDI|IOI)
+#define PH_MASK (MSGI|CDI|IOI)
+
+/* SCSIRATE */
+#define SXFR2 0x40
+#define SXFR1 0x20
+#define SXFR0 0x10
+#define SOFS3 0x08
+#define SOFS2 0x04
+#define SOFS1 0x02
+#define SOFS0 0x01
+
+/* SCSI ID */
+#define OID2 0x40
+#define OID1 0x20
+#define OID0 0x10
+#define OID_S 4 /* shift value */
+#define TID2 0x04
+#define TID1 0x02
+#define TID0 0x01
+#define SCSI_ID_MASK 0x7
+
+/* SCSI selection/reselection ID (both target *and* initiator) */
+#define SELID7 0x80
+#define SELID6 0x40
+#define SELID5 0x20
+#define SELID4 0x10
+#define SELID3 0x08
+#define SELID2 0x04
+#define SELID1 0x02
+#define SELID0 0x01
+
+/* CLRSINT0 Clears what? (interrupt and/or status bit) */
+#define SETSDONE 0x80
+#define CLRSELDO 0x40 /* I */
+#define CLRSELDI 0x20 /* I+ */
+#define CLRSELINGO 0x10 /* I */
+#define CLRSWRAP 0x08 /* I+S */
+#define CLRSDONE 0x04 /* I+S */
+#define CLRSPIORDY 0x02 /* I */
+#define CLRDMADONE 0x01 /* I */
+
+/* SSTAT0 Howto clear */
+#define TARGET 0x80
+#define SELDO 0x40 /* Selfclearing */
+#define SELDI 0x20 /* Selfclearing when CLRSELDI is set */
+#define SELINGO 0x10 /* Selfclearing */
+#define SWRAP 0x08 /* CLRSWAP */
+#define SDONE 0x04 /* Not used in initiator mode */
+#define SPIORDY 0x02 /* Selfclearing (op on SCSIDAT) */
+#define DMADONE 0x01 /* Selfclearing (all FIFOs empty & T/C */
+
+/* CLRSINT1 Clears what? */
+#define CLRSELTIMO 0x80 /* I+S */
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20 /* I+S */
+#define CLRBUSFREE 0x08 /* I+S */
+#define CLRSCSIPERR 0x04 /* I+S */
+#define CLRPHASECHG 0x02 /* I+S */
+#define CLRREQINIT 0x01 /* I+S */
+
+/* SSTAT1 How to clear? When set?*/
+#define SELTO 0x80 /* C select out timeout */
+#define ATNTARG 0x40 /* Not used in initiator mode */
+#define SCSIRSTI 0x20 /* C RST asserted */
+#define PHASEMIS 0x10 /* Selfclearing */
+#define BUSFREE 0x08 /* C bus free condition */
+#define SCSIPERR 0x04 /* C parity error on inbound data */
+#define PHASECHG 0x02 /* C phase in SCSISIGI doesn't match */
+#define REQINIT 0x01 /* C or ACK asserting edge of REQ */
+
+/* SSTAT2 */
+#define SOFFSET 0x20
+#define SEMPTY 0x10
+#define SFULL 0x08
+#define SFCNT2 0x04
+#define SFCNT1 0x02
+#define SFCNT0 0x01
+
+/* SCSITEST */
+#define SCTESTU 0x08
+#define SCTESTD 0x04
+#define STCTEST 0x01
+
+/* SSTAT3 */
+#define SCSICNT3 0x80
+#define SCSICNT2 0x40
+#define SCSICNT1 0x20
+#define SCSICNT0 0x10
+#define OFFCNT3 0x08
+#define OFFCNT2 0x04
+#define OFFCNT1 0x02
+#define OFFCNT0 0x01
+
+/* CLRSERR */
+#define CLRSYNCERR 0x04
+#define CLRFWERR 0x02
+#define CLRFRERR 0x01
+
+/* SSTAT4 */
+#define SYNCERR 0x04
+#define FWERR 0x02
+#define FRERR 0x01
+
+/* SIMODE0 */
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+/* SIMODE1 */
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/* DMACNTRL0 */
+#define ENDMA 0x80
+#define B8MODE 0x40
+#define DMA 0x20
+#define DWORDPIO 0x10
+#define WRITE 0x08
+#define INTEN 0x04
+#define RSTFIFO 0x02
+#define SWINT 0x01
+
+/* DMACNTRL1 */
+#define PWRDWN 0x80
+#define ENSTK32 0x40
+#define STK4 0x10
+#define STK3 0x08
+#define STK2 0x04
+#define STK1 0x02
+#define STK0 0x01
+
+/* DMASTAT */
+#define ATDONE 0x80
+#define WORDRDY 0x40
+#define INTSTAT 0x20
+#define DFIFOFULL 0x10
+#define DFIFOEMP 0x08
+#define DFIFOHF 0x04
+#define DWORDRDY 0x02
+
+/* BRSTCNTRL */
+#define BON3 0x80
+#define BON2 0x40
+#define BON1 0x20
+#define BON0 0x10
+#define BOFF3 0x08
+#define BOFF2 0x04
+#define BOFF1 0x02
+#define BOFF0 0x01
+
+/* TEST */
+#define BOFFTMR 0x40
+#define BONTMR 0x20
+#define STCNTH 0x10
+#define STCNTM 0x08
+#define STCNTL 0x04
+#define SCSIBLK 0x02
+#define DMABLK 0x01
+
+/* PORTA */
+#define PORTA_ID(a) ((a) & 7)
+#define PORTA_IRQ(a) ((((a) >> 3) & 3) + 9)
+#define PORTA_DRQ(a) ((((a) >> 5) & 3) ? (((a) >> 5) & 3) + 4 : 0)
+#define PORTA_PARITY(a) ((a) & 0x80)
+
+/* PORTB */
+#define PORTB_DISC(b) ((b) & 4)
+#define PORTB_SYNC(b) ((b) & 8)
+#define PORTB_BOOT(b) ((b) & 0x40)
+#define PORTB_DMA(b) ((b) & 0x80)
+
+/* How to behave on the (E)ISA bus when/if DMAing (on<<4) + off in us */
+#define EISA_BRST_TIM ((15<<4) + 1) /* 15us on, 1us off */
+
+#define FIFOSIZE 128
diff --git a/sys/dev/aic/aic_isa.c b/sys/dev/aic/aic_isa.c
new file mode 100644
index 0000000..cdf49cc
--- /dev/null
+++ b/sys/dev/aic/aic_isa.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 1999 Luoqi Chen.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+#include <dev/aic/aic6360reg.h>
+#include <dev/aic/aicvar.h>
+
+struct aic_isa_softc {
+ struct aic_softc sc_aic;
+ struct resource *sc_port;
+ struct resource *sc_irq;
+ struct resource *sc_drq;
+ void *sc_ih;
+};
+
+static int aic_isa_alloc_resources __P((device_t));
+static void aic_isa_release_resources __P((device_t));
+static int aic_isa_probe __P((device_t dev));
+static int aic_isa_attach __P((device_t dev));
+
+static u_int aic_isa_ports[] = { 0x340, 0x140 };
+#define AIC_ISA_NUMPORTS (sizeof(aic_isa_ports) / sizeof(aic_isa_ports[0]))
+#define AIC_ISA_PORTSIZE 0x20
+
+static int
+aic_isa_alloc_resources(device_t dev)
+{
+ struct aic_isa_softc *sc = device_get_softc(dev);
+ int rid;
+
+ sc->sc_port = sc->sc_irq = sc->sc_drq = 0;
+
+ rid = 0;
+ sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0ul, ~0ul, AIC_ISA_PORTSIZE, RF_ACTIVE);
+ if (!sc->sc_port)
+ return (ENOMEM);
+
+ if (isa_get_irq(dev) != -1) {
+ rid = 0;
+ sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
+ 0ul, ~0ul, 1, RF_ACTIVE);
+ if (!sc->sc_irq) {
+ aic_isa_release_resources(dev);
+ return (ENOMEM);
+ }
+ }
+
+ if (isa_get_drq(dev) != -1) {
+ rid = 0;
+ sc->sc_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &rid,
+ 0ul, ~0ul, 1, RF_ACTIVE);
+ if (!sc->sc_drq) {
+ aic_isa_release_resources(dev);
+ return (ENOMEM);
+ }
+ }
+
+ sc->sc_aic.unit = device_get_unit(dev);
+ sc->sc_aic.tag = rman_get_bustag(sc->sc_port);
+ sc->sc_aic.bsh = rman_get_bushandle(sc->sc_port);
+ return (0);
+}
+
+static void
+aic_isa_release_resources(device_t dev)
+{
+ struct aic_isa_softc *sc = device_get_softc(dev);
+
+ if (sc->sc_port)
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->sc_port);
+ if (sc->sc_irq)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
+ if (sc->sc_drq)
+ bus_release_resource(dev, SYS_RES_DRQ, 0, sc->sc_drq);
+ sc->sc_port = sc->sc_irq = sc->sc_drq = 0;
+}
+
+static int
+aic_isa_probe(device_t dev)
+{
+ struct aic_isa_softc *sc = device_get_softc(dev);
+ struct aic_softc *aic = &sc->sc_aic;
+ int numports, i;
+ u_int port, *ports;
+ u_int8_t porta;
+
+ if (isa_get_vendorid(dev))
+ return (ENXIO);
+
+ port = isa_get_port(dev);
+ if (port != -1) {
+ ports = &port;
+ numports = 1;
+ } else {
+ ports = aic_isa_ports;
+ numports = AIC_ISA_NUMPORTS;
+ }
+
+ for (i = 0; i < numports; i++) {
+ if (bus_set_resource(dev, SYS_RES_IOPORT, 0, ports[i],
+ AIC_ISA_PORTSIZE))
+ continue;
+ if (aic_isa_alloc_resources(dev))
+ continue;
+ if (!aic_probe(aic)) {
+ aic_isa_release_resources(dev);
+ break;
+ }
+ aic_isa_release_resources(dev);
+ }
+
+ if (i == numports)
+ return (ENXIO);
+
+ porta = aic_inb(aic, PORTA);
+ if (isa_get_irq(dev) == -1)
+ bus_set_resource(dev, SYS_RES_IRQ, 0, PORTA_IRQ(porta), 1);
+ if ((aic->flags & AIC_DMA_ENABLE) && isa_get_drq(dev) == -1)
+ bus_set_resource(dev, SYS_RES_DRQ, 0, PORTA_DRQ(porta), 1);
+ return (0);
+}
+
+static int
+aic_isa_attach(device_t dev)
+{
+ struct aic_isa_softc *sc = device_get_softc(dev);
+ struct aic_softc *aic = &sc->sc_aic;
+ int error;
+
+ error = aic_isa_alloc_resources(dev);
+ if (error) {
+ device_printf(dev, "resource allocation failed\n");
+ return (error);
+ }
+
+ error = aic_attach(aic);
+ if (error) {
+ device_printf(dev, "attach failed\n");
+ aic_isa_release_resources(dev);
+ return (error);
+ }
+
+ error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_CAM, aic_intr,
+ aic, &sc->sc_ih);
+ if (error) {
+ device_printf(dev, "failed to register interrupt handler\n");
+ aic_isa_release_resources(dev);
+ return (error);
+ }
+ return (0);
+}
+
+static int
+aic_isa_detach(device_t dev)
+{
+ struct aic_isa_softc *sc = device_get_softc(dev);
+ struct aic_softc *aic = &sc->sc_aic;
+ int error;
+
+ error = aic_detach(aic);
+ if (error) {
+ device_printf(dev, "detach failed\n");
+ return (error);
+ }
+
+ error = bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
+ if (error) {
+ device_printf(dev, "failed to unregister interrupt handler\n");
+ }
+
+ aic_isa_release_resources(dev);
+ return (0);
+}
+
+static device_method_t aic_isa_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aic_isa_probe),
+ DEVMETHOD(device_attach, aic_isa_attach),
+ DEVMETHOD(device_detach, aic_isa_detach),
+ { 0, 0 }
+};
+
+static driver_t aic_isa_driver = {
+ "aic",
+ aic_isa_methods, sizeof(struct aic_isa_softc),
+};
+
+static devclass_t aic_devclass;
+
+DRIVER_MODULE(aic, isa, aic_isa_driver, aic_devclass, 0, 0);
diff --git a/sys/dev/aic/aicvar.h b/sys/dev/aic/aicvar.h
new file mode 100644
index 0000000..540a25a
--- /dev/null
+++ b/sys/dev/aic/aicvar.h
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 1999 Luoqi Chen.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "aic.h"
+
+struct aic_transinfo {
+ u_int8_t period;
+ u_int8_t offset;
+};
+
+struct aic_tinfo {
+ u_int16_t lubusy;
+ u_int8_t flags;
+ u_int8_t scsirate;
+ struct aic_transinfo current;
+ struct aic_transinfo goal;
+ struct aic_transinfo user;
+};
+
+#define TINFO_DISC_ENB 0x01
+#define TINFO_TAG_ENB 0x02
+#define TINFO_SDTR_NEGO 0x04
+#define TINFO_SDTR_SENT 0x08
+
+struct aic_scb {
+ union ccb *ccb;
+ u_int8_t flags;
+ u_int8_t tag;
+ u_int8_t target;
+ u_int8_t lun;
+ u_int8_t status;
+ u_int8_t cmd_len;
+ u_int8_t *cmd_ptr;
+ u_int32_t data_len;
+ u_int8_t *data_ptr;
+};
+
+#define ccb_scb_ptr spriv_ptr0
+#define ccb_aic_ptr spriv_ptr1
+
+#define SCB_ACTIVE 0x01
+#define SCB_DISCONNECTED 0x02
+#define SCB_DEVICE_RESET 0x04
+#define SCB_SENSE 0x08
+
+struct aic_softc {
+ int unit;
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
+ bus_dma_tag_t dmat;
+
+ struct cam_sim *sim;
+ struct cam_path *path;
+ TAILQ_HEAD(,ccb_hdr) pending_ccbs, nexus_ccbs;
+ struct aic_scb *nexus;
+
+ u_int32_t flags;
+ u_int8_t initiator;
+ u_int8_t state;
+ u_int8_t target;
+ u_int8_t lun;
+ u_int8_t prev_phase;
+
+ u_int8_t msg_outq;
+ u_int8_t msg_sent;
+ int msg_len;
+ char msg_buf[8];
+
+ struct aic_tinfo tinfo[8];
+ struct aic_scb scbs[256];
+};
+
+#define AIC_DISC_ENABLE 0x01
+#define AIC_DMA_ENABLE 0x02
+#define AIC_PARITY_ENABLE 0x04
+#define AIC_DWIO_ENABLE 0x08
+#define AIC_RESOURCE_SHORTAGE 0x10
+#define AIC_DROP_MSGIN 0x20
+#define AIC_BUSFREE_OK 0x40
+
+#define AIC_IDLE 0x00
+#define AIC_SELECTING 0x01
+#define AIC_RESELECTED 0x02
+#define AIC_HASNEXUS 0x03
+
+#define AIC_MSG_IDENTIFY 0x01
+#define AIC_MSG_TAG_Q 0x02
+#define AIC_MSG_SDTR 0x04
+#define AIC_MSG_WDTR 0x08
+#define AIC_MSG_MSGBUF 0x80
+
+#define AIC_SYNC_PERIOD (200 / 4)
+#define AIC_SYNC_OFFSET 8
+
+#define aic_inb(aic, port) \
+ bus_space_read_1((aic)->tag, (aic)->bsh, (port))
+
+#define aic_outb(aic, port, value) \
+ bus_space_write_1((aic)->tag, (aic)->bsh, (port), (value))
+
+#define aic_insb(aic, port, addr, count) \
+ bus_space_read_multi_1((aic)->tag, (aic)->bsh, (port), (addr), (count))
+
+#define aic_outsb(aic, port, addr, count) \
+ bus_space_write_multi_1((aic)->tag, (aic)->bsh, (port), (addr), (count))
+
+#define aic_insw(aic, port, addr, count) \
+ bus_space_read_multi_2((aic)->tag, (aic)->bsh, (port), \
+ (u_int16_t *)(addr), (count))
+
+#define aic_outsw(aic, port, addr, count) \
+ bus_space_write_multi_2((aic)->tag, (aic)->bsh, (port), \
+ (u_int16_t *)(addr), (count))
+
+#define aic_insl(aic, port, addr, count) \
+ bus_space_read_multi_4((aic)->tag, (aic)->bsh, (port), \
+ (u_int32_t *)(addr), (count))
+
+#define aic_outsl(aic, port, addr, count) \
+ bus_space_write_multi_4((aic)->tag, (aic)->bsh, (port), \
+ (u_int32_t *)(addr), (count))
+
+extern int aic_probe __P((struct aic_softc *));
+extern int aic_attach __P((struct aic_softc *));
+extern int aic_detach __P((struct aic_softc *));
+extern void aic_intr __P((void *));
OpenPOWER on IntegriCloud