diff options
author | luoqi <luoqi@FreeBSD.org> | 1999-10-21 08:56:53 +0000 |
---|---|---|
committer | luoqi <luoqi@FreeBSD.org> | 1999-10-21 08:56:53 +0000 |
commit | 5dab878b88e91782e452b9ce86c4fde1506ae95e (patch) | |
tree | 9ffd6f41f5fa9cac5a2e4b5072cc82c92688700a | |
parent | 37b81ac9d3bca142c50c370b5f86752e070dc23f (diff) | |
download | FreeBSD-src-5dab878b88e91782e452b9ce86c4fde1506ae95e.zip FreeBSD-src-5dab878b88e91782e452b9ce86c4fde1506ae95e.tar.gz |
Adaptec 6260/6360 CAM driver.
-rw-r--r-- | sys/dev/aic/aic.c | 1373 | ||||
-rw-r--r-- | sys/dev/aic/aic6360reg.h | 331 | ||||
-rw-r--r-- | sys/dev/aic/aic_isa.c | 227 | ||||
-rw-r--r-- | sys/dev/aic/aicvar.h | 151 |
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 *)); |