diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/mpi.c | 1509 | ||||
-rw-r--r-- | sys/dev/ic/mpireg.h | 694 | ||||
-rw-r--r-- | sys/dev/ic/mpivar.h | 93 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 6 | ||||
-rw-r--r-- | sys/dev/pci/mpi_pci.c | 168 |
5 files changed, 2469 insertions, 1 deletions
diff --git a/sys/dev/ic/mpi.c b/sys/dev/ic/mpi.c new file mode 100644 index 00000000000..cda1b2c374e --- /dev/null +++ b/sys/dev/ic/mpi.c @@ -0,0 +1,1509 @@ +/* $OpenBSD: mpi.c,v 1.1 2006/05/27 19:03:55 dlg Exp $ */ + +/* + * Copyright (c) 2005, 2006 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <machine/bus.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/mpireg.h> +#include <dev/ic/mpivar.h> + +#define MPI_DEBUG + +#ifdef MPI_DEBUG +#define DPRINTF(x...) do { if (mpidebug) printf(x); } while (0) +#define DPRINTFN(n, x...) do { if (mpidebug > (n)) printf(x); } while (0) +int mpidebug = 0; +#else +#define DPRINTF(x...) /* x */ +#define DPRINTFN(n, x...) /* n, x */ +#endif + +struct cfdriver mpi_cd = { + NULL, "mpi", DV_DULL +}; + +int mpi_scsi_cmd(struct scsi_xfer *); +void mpi_scsi_cmd_done(struct mpi_ccb *, void *, paddr_t); +void mpi_minphys(struct buf *bp); +int mpi_scsi_ioctl(struct scsi_link *, u_long, caddr_t, + int, struct proc *); + +struct scsi_adapter mpi_switch = { + mpi_scsi_cmd, mpi_minphys, NULL, NULL, mpi_scsi_ioctl +}; + +struct scsi_device mpi_dev = { + NULL, NULL, NULL, NULL +}; + +struct mpi_dmamem *mpi_dmamem_alloc(struct mpi_softc *, size_t); +void mpi_dmamem_free(struct mpi_softc *, + struct mpi_dmamem *); +int mpi_alloc_ccbs(struct mpi_softc *); +struct mpi_ccb *mpi_get_ccb(struct mpi_softc *); +void mpi_put_ccb(struct mpi_softc *, struct mpi_ccb *); +int mpi_alloc_replies(struct mpi_softc *); + +void mpi_start(struct mpi_softc *, struct mpi_ccb *); +void mpi_complete(struct mpi_softc *, struct mpi_ccb *); +void mpi_timeout_xs(void *); +int mpi_load_xs(struct mpi_ccb *); + +u_int32_t mpi_read(struct mpi_softc *, bus_size_t); +void mpi_write(struct mpi_softc *, bus_size_t, u_int32_t); +int mpi_wait_eq(struct mpi_softc *, bus_size_t, u_int32_t, + u_int32_t); +int mpi_wait_ne(struct mpi_softc *, bus_size_t, u_int32_t, + u_int32_t); + +int mpi_init(struct mpi_softc *); +int mpi_reset_soft(struct mpi_softc *); +int mpi_reset_hard(struct mpi_softc *); + +int mpi_handshake_send(struct mpi_softc *, void *, size_t); +int mpi_handshake_recv_dword(struct mpi_softc *, + u_int32_t *); +int mpi_handshake_recv(struct mpi_softc *, void *, size_t); + +int mpi_iocinit(struct mpi_softc *); +int mpi_iocfacts(struct mpi_softc *); +int mpi_portfacts(struct mpi_softc *); +void mpi_portfacts_done(struct mpi_ccb *, void *, paddr_t); +int mpi_oportfacts(struct mpi_softc *); +int mpi_eventnotify(struct mpi_softc *); +void mpi_eventnotify_done(struct mpi_ccb *, void *, paddr_t); +int mpi_portenable(struct mpi_softc *); +void mpi_portenable_done(struct mpi_ccb *, void *, paddr_t); + +#define DEVNAME(s) ((s)->sc_dev.dv_xname) + +#define dwordsof(s) (sizeof(s) / sizeof(u_int32_t)) +#define sizeofa(s) (sizeof(s) / sizeof((s)[0])) + +#define mpi_read_db(s) mpi_read((s), MPI_DOORBELL) +#define mpi_write_db(s, v) mpi_write((s), MPI_DOORBELL, (v)) +#define mpi_read_intr(s) mpi_read((s), MPI_INTR_STATUS) +#define mpi_write_intr(s, v) mpi_write((s), MPI_INTR_STATUS, (v)) +#define mpi_pop_reply(s) mpi_read((s), MPI_REPLY_QUEUE) +#define mpi_push_reply(s, v) mpi_write((s), MPI_REPLY_QUEUE, (v)) + +#define mpi_wait_db_int(s) mpi_wait_ne((s), MPI_INTR_STATUS, \ + MPI_INTR_STATUS_DOORBELL, 0) +#define mpi_wait_db_ack(s) mpi_wait_eq((s), MPI_INTR_STATUS, \ + MPI_INTR_STATUS_IOCDOORBELL, 0) + + + +int +mpi_attach(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + + printf("\n"); + + /* disable interrupts */ + mpi_write(sc, MPI_INTR_MASK, + MPI_INTR_MASK_REPLY | MPI_INTR_MASK_DOORBELL); + + if (mpi_init(sc) != 0) { + printf("%s: unable to initialise\n", DEVNAME(sc)); + return (1); + } + + if (mpi_iocfacts(sc) != 0) { + printf("%s: unable to get iocfacts\n", DEVNAME(sc)); + return (1); + } + + if (mpi_alloc_ccbs(sc) != 0) { + /* error already printed */ + return (1); + } + + if (mpi_iocinit(sc) != 0) { + printf("%s: unable to send iocinit\n", DEVNAME(sc)); + goto free_ccbs; + } + + /* spin until we're operational */ + if (mpi_wait_eq(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_OPER) != 0) { + printf("%s: state: 0x%08x\n", DEVNAME(sc), + mpi_read_db(sc) & MPI_DOORBELL_STATE); + printf("%s: operational state timeout\n", DEVNAME(sc)); + goto free_ccbs; + } + + if (mpi_alloc_replies(sc) != 0) { + /* error already printed */ + goto free_ccbs; + } + + if (mpi_portfacts(sc) != 0) { + printf("%s: unable to get portfacts\n", DEVNAME(sc)); + goto free_replies; + } + +#if notyet + if (mpi_eventnotify(sc) != 0) { + printf("%s: unable to get portfacts\n", DEVNAME(sc)); + goto free_replies; + } +#endif + + if (mpi_portenable(sc) != 0) { + printf("%s: unable to enable port\n", DEVNAME(sc)); + goto free_replies; + } + + /* we should be good to go now, attach scsibus */ + sc->sc_link.device = &mpi_dev; + sc->sc_link.adapter = &mpi_switch; + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_target; + sc->sc_link.adapter_buswidth = sc->sc_buswidth; + sc->sc_link.openings = sc->sc_maxcmds / sc->sc_buswidth; + + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); + + /* XXX enable interrupts */ + mpi_write(sc, MPI_INTR_MASK, MPI_INTR_MASK_DOORBELL); + + return (0); + +free_replies: + bus_dmamap_sync(sc->sc_dmat, MPI_DMA_MAP(sc->sc_replies), + 0, PAGE_SIZE, BUS_DMASYNC_POSTREAD); + mpi_dmamem_free(sc, sc->sc_replies); +free_ccbs: + while ((ccb = mpi_get_ccb(sc)) != NULL) + bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); + mpi_dmamem_free(sc, sc->sc_requests); + free(sc->sc_ccbs, M_DEVBUF); + + return(1); +} + +void +mpi_detach(struct mpi_softc *sc) +{ + +} + +int +mpi_intr(void *arg) +{ + struct mpi_softc *sc = arg; + struct mpi_ccb *ccb; + struct mpi_msg_reply *reply = NULL; + paddr_t reply_dva; + char *reply_addr; + u_int32_t reg, id; + int rv = 0; + + while ((reg = mpi_pop_reply(sc)) != 0xffffffff) { + + DPRINTF("%s: %s reply_queue: 0x%08x\n", DEVNAME(sc), __func__, + reg); + + if (reg & MPI_REPLY_QUEUE_ADDRESS) { + bus_dmamap_sync(sc->sc_dmat, + MPI_DMA_MAP(sc->sc_replies), 0, PAGE_SIZE, + BUS_DMASYNC_POSTREAD); + + reply_dva = (reg & MPI_REPLY_QUEUE_ADDRESS_MASK) << 1; + + reply_addr = MPI_DMA_KVA(sc->sc_replies) + + (reply_dva - MPI_DMA_DVA(sc->sc_replies)); + reply = (struct mpi_msg_reply *)reply_addr; + + id = letoh32(reply->msg_context); + + bus_dmamap_sync(sc->sc_dmat, + MPI_DMA_MAP(sc->sc_replies), 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + } else { + switch (reg & MPI_REPLY_QUEUE_TYPE_MASK) { + case MPI_REPLY_QUEUE_TYPE_INIT: + id = reg & MPI_REPLY_QUEUE_CONTEXT; + break; + + default: + panic("%s: unsupported context reply\n", + DEVNAME(sc)); + } + } + + DPRINTF("%s: %s id: %d\n", DEVNAME(sc), __func__, id); + + ccb = &sc->sc_ccbs[id]; + + bus_dmamap_sync(sc->sc_dmat, MPI_DMA_MAP(sc->sc_requests), + ccb->ccb_offset, MPI_REQUEST_SIZE, BUS_DMASYNC_POSTWRITE); + + ccb->ccb_done(ccb, reply, reply_dva); + rv = 1; + } + + return (rv); +} + +struct mpi_dmamem * +mpi_dmamem_alloc(struct mpi_softc *sc, size_t size) +{ + struct mpi_dmamem *mdm; + int nsegs; + + mdm = malloc(sizeof(struct mpi_dmamem), M_DEVBUF, M_NOWAIT); + if (mdm == NULL) + return (NULL); + + bzero(mdm, sizeof(struct mpi_dmamem)); + mdm->mdm_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &mdm->mdm_map) != 0) + goto mdmfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &mdm->mdm_seg, + 1, &nsegs, BUS_DMA_NOWAIT) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &mdm->mdm_seg, nsegs, size, + &mdm->mdm_kva, BUS_DMA_NOWAIT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, mdm->mdm_map, mdm->mdm_kva, size, + NULL, BUS_DMA_NOWAIT) != 0) + goto unmap; + + bzero(mdm->mdm_kva, size); + return (mdm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, mdm->mdm_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &mdm->mdm_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, mdm->mdm_map); +mdmfree: + free(mdm, M_DEVBUF); + + return (NULL); +} + +void +mpi_dmamem_free(struct mpi_softc *sc, struct mpi_dmamem *mdm) +{ + bus_dmamap_unload(sc->sc_dmat, mdm->mdm_map); + bus_dmamem_unmap(sc->sc_dmat, mdm->mdm_kva, mdm->mdm_size); + bus_dmamem_free(sc->sc_dmat, &mdm->mdm_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, mdm->mdm_map); + free(mdm, M_DEVBUF); +} + +int +mpi_alloc_ccbs(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + u_int8_t *cmd; + int i; + + TAILQ_INIT(&sc->sc_ccb_free); + TAILQ_INIT(&sc->sc_ccb_runq); + + sc->sc_ccbs = malloc(sizeof(struct mpi_ccb) * sc->sc_maxcmds, + M_DEVBUF, M_WAITOK); + if (sc->sc_ccbs == NULL) { + printf("%s: unable to allocate ccbs\n", DEVNAME(sc)); + return (1); + } + bzero(sc->sc_ccbs, sizeof(struct mpi_ccb) * sc->sc_maxcmds); + + sc->sc_requests = mpi_dmamem_alloc(sc, + MPI_REQUEST_SIZE * sc->sc_maxcmds); + if (sc->sc_requests == NULL) { + printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc)); + goto free_ccbs; + } + cmd = MPI_DMA_KVA(sc->sc_requests); + bzero(cmd, MPI_REQUEST_SIZE * sc->sc_maxcmds); + + for (i = 0; i < sc->sc_maxcmds; i++) { + ccb = &sc->sc_ccbs[i]; + + if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, MPI_MAX_SGL, + MAXPHYS, 0, 0, &ccb->ccb_dmamap) != 0) { + printf("%s: unable to create dma map\n", DEVNAME(sc)); + goto free_maps; + } + + ccb->ccb_sc = sc; + ccb->ccb_id = i; + ccb->ccb_offset = MPI_REQUEST_SIZE * i; + + ccb->ccb_cmd = &cmd[ccb->ccb_offset]; + ccb->ccb_cmd_dva = MPI_DMA_DVA(sc->sc_requests) + + ccb->ccb_offset; + + mpi_put_ccb(sc, ccb); + } + + return (0); + +free_maps: + while ((ccb = mpi_get_ccb(sc)) != NULL) + bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); + + mpi_dmamem_free(sc, sc->sc_requests); +free_ccbs: + free(sc->sc_ccbs, M_DEVBUF); + + return (1); +} + +struct mpi_ccb * +mpi_get_ccb(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + + ccb = TAILQ_FIRST(&sc->sc_ccb_free); + if (ccb == NULL) + return (NULL); + + TAILQ_REMOVE(&sc->sc_ccb_free, ccb, ccb_link); + + DPRINTFN(10, "%s: %s: ccb_id: %d\n", DEVNAME(sc), __func__, + ccb->ccb_id); + + return (ccb); +} + +void +mpi_put_ccb(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + DPRINTFN(10, "%s: %s: ccb_id: %d\n", DEVNAME(sc), __func__, + ccb->ccb_id); + + ccb->ccb_state = MPI_CCB_FREE; + ccb->ccb_xs = NULL; + ccb->ccb_done = NULL; + bzero(ccb->ccb_cmd, MPI_REQUEST_SIZE); + TAILQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_link); +} + +int +mpi_alloc_replies(struct mpi_softc *sc) +{ + paddr_t reply; + int i; + + sc->sc_replies = mpi_dmamem_alloc(sc, PAGE_SIZE); + if (sc->sc_replies == NULL) { + printf("%s: unable to allocate replies\n", DEVNAME(sc)); + return (1); + } + + bus_dmamap_sync(sc->sc_dmat, MPI_DMA_MAP(sc->sc_replies), + 0, PAGE_SIZE, BUS_DMASYNC_PREREAD); + + for (i = 0; i < PAGE_SIZE / MPI_REPLY_SIZE; i++) { + reply = MPI_DMA_DVA(sc->sc_replies) + MPI_REPLY_SIZE * i; + mpi_push_reply(sc, reply); + } + + return (0); +} + +void +mpi_start(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + bus_dmamap_sync(sc->sc_dmat, MPI_DMA_MAP(sc->sc_requests), + ccb->ccb_offset, MPI_REQUEST_SIZE, BUS_DMASYNC_PREWRITE); + + ccb->ccb_state = MPI_CCB_QUEUED; + TAILQ_INSERT_TAIL(&sc->sc_ccb_runq, ccb, ccb_link); + mpi_write(sc, MPI_REQ_QUEUE, ccb->ccb_cmd_dva); +} + +void +mpi_complete(struct mpi_softc *sc, struct mpi_ccb *nccb) +{ + struct mpi_ccb *ccb; + struct mpi_msg_reply *reply = NULL; + paddr_t reply_dva; + char *reply_addr; + u_int32_t reg, id; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + do { + reg = mpi_pop_reply(sc); + if (reg == 0xffffffff) { + delay(1000); + continue; + } + + DPRINTF("%s: %s reply_queue: 0x%08x\n", DEVNAME(sc), __func__, + reg); + + if (reg & MPI_REPLY_QUEUE_ADDRESS) { + bus_dmamap_sync(sc->sc_dmat, + MPI_DMA_MAP(sc->sc_replies), 0, PAGE_SIZE, + BUS_DMASYNC_POSTREAD); + + reply_dva = (reg & MPI_REPLY_QUEUE_ADDRESS_MASK) << 1; + + reply_addr = MPI_DMA_KVA(sc->sc_replies) + + (reply_dva - MPI_DMA_DVA(sc->sc_replies)); + reply = (struct mpi_msg_reply *)reply_addr; + + id = letoh32(reply->msg_context); + + bus_dmamap_sync(sc->sc_dmat, + MPI_DMA_MAP(sc->sc_replies), 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + } else { + switch (reg & MPI_REPLY_QUEUE_TYPE_MASK) { + case MPI_REPLY_QUEUE_TYPE_INIT: + id = reg & MPI_REPLY_QUEUE_CONTEXT; + break; + + default: + panic("%s: unsupported context reply\n", + DEVNAME(sc)); + } + } + + DPRINTF("%s: %s id: %d\n", DEVNAME(sc), __func__, id); + + ccb = &sc->sc_ccbs[id]; + + bus_dmamap_sync(sc->sc_dmat, MPI_DMA_MAP(sc->sc_requests), + ccb->ccb_offset, MPI_REQUEST_SIZE, BUS_DMASYNC_POSTWRITE); + + ccb->ccb_done(ccb, reply, reply_dva); + + } while (nccb->ccb_id != id); +} + +int +mpi_scsi_cmd(struct scsi_xfer *xs) +{ + struct scsi_link *link = xs->sc_link; + struct mpi_softc *sc = link->adapter_softc; + struct mpi_ccb *ccb; + struct mpi_ccb_bundle *mcb; + struct mpi_msg_scsi_io *io; + int s; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + if (xs->cmdlen > MPI_CDB_LEN) { + DPRINTF("%s: CBD too big %d", DEVNAME(sc), xs->cmdlen); + bzero(&xs->sense, sizeof(xs->sense)); + xs->sense.error_code = SSD_ERRCODE_VALID | 0x70; + xs->sense.flags = SKEY_ILLEGAL_REQUEST; + xs->sense.add_sense_code = 0x20; + xs->error = XS_SENSE; + scsi_done(xs); + return (COMPLETE); + } + + s = splbio(); + ccb = mpi_get_ccb(sc); + splx(s); + if (ccb == NULL) { + xs->error = XS_DRIVER_STUFFUP; + scsi_done(xs); + return (COMPLETE); + } + DPRINTF("%s: ccb_id: %d xs->flags: 0x%x\n", DEVNAME(sc), ccb->ccb_id, + xs->flags); + + ccb->ccb_xs = xs; + ccb->ccb_done = mpi_scsi_cmd_done; + + mcb = ccb->ccb_cmd; + io = &mcb->mcb_io; + + io->function = MPI_FUNCTION_SCSI_IO_REQUEST; +// io->chain_offset = dwordsof(mcb->mcb_io); +// io->bus = htole16(sc->sc_bus); + io->target_id = link->target; + + io->cdb_length = xs->cmdlen; + io->sense_buf_len = sizeof(xs->sense); + + io->msg_context = htole32(ccb->ccb_id); + + /* XXX */ + io->lun[0] = htole16(link->lun); + + switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + case SCSI_DATA_IN: + io->control = htole32(MPI_SCSIIO_DATA_DIR_READ); + break; + case SCSI_DATA_OUT: + io->control = htole32(MPI_SCSIIO_DATA_DIR_WRITE); + break; + default: + io->control = htole32(MPI_SCSIIO_DATA_DIR_NONE); + break; + } + + bcopy(xs->cmd, io->cdb, xs->cmdlen); + + io->data_length = htole32(xs->datalen); + + io->sense_buf_low_addr = htole32(ccb->ccb_cmd_dva + + ((u_int8_t *)&mcb->mcb_sense - (u_int8_t *)mcb)); + + if (mpi_load_xs(ccb) != 0) { + s = splbio(); + mpi_put_ccb(sc, ccb); + splx(s); + xs->error = XS_DRIVER_STUFFUP; + scsi_done(xs); + return (COMPLETE); + } + + timeout_set(&xs->stimeout, mpi_timeout_xs, ccb); + + if (xs->flags & SCSI_POLL) { + s = splbio(); + mpi_start(sc, ccb); + mpi_complete(sc, ccb); + splx(s); + + return (COMPLETE); + } + + mpi_start(sc, ccb); + return (SUCCESSFULLY_QUEUED); +} + +void +mpi_scsi_cmd_done(struct mpi_ccb *ccb, void *reply, paddr_t reply_dva) +{ + struct mpi_softc *sc = ccb->ccb_sc; + struct scsi_xfer *xs = ccb->ccb_xs; + struct mpi_ccb_bundle *mcb = ccb->ccb_cmd; + bus_dmamap_t dmap = ccb->ccb_dmamap; + struct mpi_msg_scsi_io_error *sie = reply; + + if (xs->datalen != 0) { + bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, dmap); + } + + /* timeout_del */ + xs->error = XS_NOERROR; + xs->resid = 0; + xs->flags |= ITSDONE; + DPRINTFN(10, "%s: xs cmd: 0x%02x len: %d error: 0x%02x flags 0x%x\n", + DEVNAME(sc), xs->cmd->opcode, xs->datalen, xs->error, xs->flags); + + if (sie == NULL) { + /* no scsi error, we're ok so drop out early */ + xs->status = SCSI_OK; + mpi_put_ccb(sc, ccb); + scsi_done(xs); + return; + } + +#ifdef MPI_DEBUG + if (mpidebug > 10) { + printf("%s: target_id: %d bus: %d msg_length: %d " + "function: 0x%02x\n", DEVNAME(sc), sie->target_id, + sie->bus, sie->msg_length, sie->function); + + printf("%s: cdb_length: %d sense_buf_length: %d " + "msg_flags: 0x%02x\n", DEVNAME(sc), sie->cdb_length, + sie->bus, sie->msg_flags); + + printf("%s: msg_context: 0x%08x\n", DEVNAME(sc), + letoh32(sie->msg_context)); + + printf("%s: scsi_status: 0x%02x scsi_state: 0x%02x " + "ioc_status: 0x%04x\n", DEVNAME(sc), sie->scsi_status, + sie->scsi_state, letoh16(sie->ioc_status)); + + printf("%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(sie->ioc_loginfo)); + + printf("%s: transfer_count: %d\n", DEVNAME(sc), + letoh32(sie->transfer_count)); + + printf("%s: sense_count: %d\n", DEVNAME(sc), + letoh32(sie->sense_count)); + + printf("%s: response_info: 0x%08x\n", DEVNAME(sc), + letoh32(sie->response_info)); + + printf("%s: tag: 0x%04x\n", DEVNAME(sc), letoh16(sie->tag)); + } +#endif /* MPI_DEBUG */ + + + xs->status = sie->scsi_status; + switch (sie->ioc_status) { + case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: + xs->error = XS_DRIVER_STUFFUP; + break; + + case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: + /* + * Yikes! Tagged queue full comes through this path! + * + * So we'll change it to a status error and anything + * that returns status should probably be a status + * error as well. + */ + xs->resid = xs->datalen - letoh32(sie->transfer_count); + if (sie->scsi_state & MPI_SCSIIO_ERR_STATE_NO_SCSI_STATUS) { + xs->error = XS_DRIVER_STUFFUP; + break; + } + /* FALLTHROUGH */ + case MPI_IOCSTATUS_SUCCESS: + case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: + switch (xs->status) { + case SCSI_OK: + xs->resid = 0; + break; + + case SCSI_CHECK: + xs->error = XS_SENSE; + break; + + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + + case SCSI_QUEUE_FULL: + xs->error = XS_TIMEOUT; + xs->retries++; + break; + default: + printf("%s: invalid status code %d\n", xs->status); + xs->error = XS_DRIVER_STUFFUP; + break; + } + break; + + case MPI_IOCSTATUS_BUSY: + case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: + xs->error = XS_BUSY; + break; + + case MPI_IOCSTATUS_SCSI_INVALID_BUS: + case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: + case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + xs->error = XS_SELTIMEOUT; + break; + + case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: + xs->error = XS_DRIVER_STUFFUP; + break; + + case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: + xs->error = XS_DRIVER_STUFFUP; + break; + + case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: + /* XXX */ + xs->error = XS_DRIVER_STUFFUP; + break; + + case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: + /* XXX */ + xs->error = XS_DRIVER_STUFFUP; + break; + + case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: + /* XXX This is a bus-reset */ + xs->error = XS_DRIVER_STUFFUP; + break; + + default: + /* XXX unrecognized HBA error */ + xs->error = XS_DRIVER_STUFFUP; + break; + } + + if (sie->scsi_state & MPI_SCSIIO_ERR_STATE_AUTOSENSE_VALID) { + bcopy(&mcb->mcb_sense, &xs->sense, sizeof(xs->sense)); + } else if (sie->scsi_state & MPI_SCSIIO_ERR_STATE_AUTOSENSE_FAILED) { + /* This will cause the scsi layer to issue a REQUEST SENSE */ + if (xs->status == SCSI_CHECK) + xs->error = XS_BUSY; + } + + DPRINTFN(10, "%s: xs error: 0x%02x len: %d\n", DEVNAME(sc), + xs->error, xs->status); + mpi_push_reply(sc, reply_dva); + mpi_put_ccb(sc, ccb); + scsi_done(xs); +} + +void +mpi_timeout_xs(void *arg) +{ + /* XXX */ +} + +int +mpi_load_xs(struct mpi_ccb *ccb) +{ + struct mpi_softc *sc = ccb->ccb_sc; + struct scsi_xfer *xs = ccb->ccb_xs; + struct mpi_ccb_bundle *mcb = ccb->ccb_cmd; + struct mpi_sge32 *sge; + bus_dmamap_t dmap = ccb->ccb_dmamap; + u_int32_t flags; + int i, error; + + if (xs->datalen == 0) { + sge = &mcb->mcb_sgl[0]; + sge->sg_hdr = htole32(MPI_SGE_FL_TYPE_SIMPLE | + MPI_SGE_FL_LAST | MPI_SGE_FL_EOB | MPI_SGE_FL_EOL); + return (0); + } + +#if 0 + error = bus_dmamap_load(sc->sc_dmat, dmap, + xs->data, xs->datalen, NULL, BUS_DMA_STREAMING | + (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK | + (xs->flags & SCSI_DATA_IN) ? BUS_DMA_READ : BUS_DMA_WRITE); +#endif + error = bus_dmamap_load(sc->sc_dmat, dmap, + xs->data, xs->datalen, NULL, + (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + if (error) { + printf("%s: error %d loading dmamap\n", DEVNAME(sc), error); + return (1); + } + + flags = MPI_SGE_FL_TYPE_SIMPLE; + if (xs->flags & SCSI_DATA_OUT) + flags |= MPI_SGE_FL_DIR_OUT; + + for (i = 0; i < dmap->dm_nsegs; i++) { + sge = &mcb->mcb_sgl[i]; + sge->sg_hdr = htole32(flags | dmap->dm_segs[i].ds_len); + sge->sg_addr = htole32(dmap->dm_segs[i].ds_addr); + } + + /* terminate list */ + sge->sg_hdr |= htole32(MPI_SGE_FL_LAST | MPI_SGE_FL_EOB | + MPI_SGE_FL_EOL); + +#ifdef MPI_DEBUG + if (mpidebug > 11) { + for (i = 0; i < dmap->dm_nsegs; i++) { + printf("%s: %d: 0x%08x 0x%08x\n", DEVNAME(sc), i, + mcb->mcb_sgl[i].sg_hdr, mcb->mcb_sgl[i].sg_addr); + } + } +#endif + + bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); + + return (0); +} + +void +mpi_minphys(struct buf *bp) +{ + /* XXX */ + if (bp->b_bcount > MAXPHYS) + bp->b_bcount = MAXPHYS; + minphys(bp); +} + +int +mpi_scsi_ioctl(struct scsi_link *a, u_long b, caddr_t c, int d, struct proc *e) +{ + return (0); +} + +u_int32_t +mpi_read(struct mpi_softc *sc, bus_size_t r) +{ + bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, + BUS_SPACE_BARRIER_READ); + return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, r)); +} + +void +mpi_write(struct mpi_softc *sc, bus_size_t r, u_int32_t v) +{ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, r, v); + bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, + BUS_SPACE_BARRIER_WRITE); +} + +int +mpi_wait_eq(struct mpi_softc *sc, bus_size_t r, u_int32_t mask, + u_int32_t target) +{ + int i; + + for (i = 0; i < 10000; i++) { + if ((mpi_read(sc, r) & mask) == target) + return (0); + delay(1000); + } + + return (1); +} + +int +mpi_wait_ne(struct mpi_softc *sc, bus_size_t r, u_int32_t mask, + u_int32_t target) +{ + int i; + + for (i = 0; i < 10000; i++) { + if ((mpi_read(sc, r) & mask) != target) + return (0); + delay(1000); + } + + return (1); +} + +int +mpi_init(struct mpi_softc *sc) +{ + u_int32_t db; + int i; + + /* spin until the IOC leaves the RESET state */ + if (mpi_wait_ne(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_RESET) != 0) { + DPRINTF("%s: %s timeout waiting to leave reset state\n", + DEVNAME(sc), __func__); + return (1); + } + + /* check current ownership */ + db = mpi_read_db(sc); + if ((db & MPI_DOORBELL_WHOINIT) == MPI_DOORBELL_WHOINIT_PCIPEER) { + DPRINTF("%s: %s initialised by pci peer\n", DEVNAME(sc), + __func__); + return (0); + } + + for (i = 0; i < 5; i++) { + switch (db & MPI_DOORBELL_STATE) { + case MPI_DOORBELL_STATE_READY: + DPRINTF("%s: %s ioc is ready\n", DEVNAME(sc), + __func__); + return (0); + + case MPI_DOORBELL_STATE_OPER: + case MPI_DOORBELL_STATE_FAULT: + DPRINTF("%s: %s ioc is being reset\n", DEVNAME(sc), + __func__); + if (mpi_reset_soft(sc) != 0) + mpi_reset_hard(sc); + break; + + case MPI_DOORBELL_STATE_RESET: + DPRINTF("%s: %s waiting to come out of reset\n", + DEVNAME(sc), __func__); + if (mpi_wait_ne(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_RESET) != 0) + return (1); + break; + } + db = mpi_read_db(sc); + } + + return (1); +} + +int +mpi_reset_soft(struct mpi_softc *sc) +{ + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + if (mpi_read_db(sc) & MPI_DOORBELL_INUSE) + return (1); + + mpi_write_db(sc, + MPI_DOORBELL_FUNCTION(MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET)); + if (mpi_wait_eq(sc, MPI_INTR_STATUS, + MPI_INTR_STATUS_IOCDOORBELL, 0) != 0) + return (1); + + if (mpi_wait_eq(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_READY) != 0) + return (1); + + return (0); +} + +int +mpi_reset_hard(struct mpi_softc *sc) +{ + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + /* enable diagnostic register */ + mpi_write(sc, MPI_WRITESEQ, 0xff); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_1); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_2); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_3); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_4); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_5); + + /* reset ioc */ + mpi_write(sc, MPI_HOSTDIAG, MPI_HOSTDIAG_RESET_ADAPTER); + + delay(10000); + + /* disable diagnostic register */ + mpi_write(sc, MPI_WRITESEQ, 0xff); + + /* restore pci bits? */ + + /* firmware bits? */ + return (0); +} + +int +mpi_handshake_send(struct mpi_softc *sc, void *buf, size_t dwords) +{ + u_int32_t *query = buf; + int i; + + /* make sure the doorbell is not in use. */ + if (mpi_read_db(sc) & MPI_DOORBELL_INUSE) + return (1); + + /* clear pending doorbell interrupts */ + if (mpi_read_intr(sc) & MPI_INTR_STATUS_DOORBELL) + mpi_write_intr(sc, 0); + + /* + * first write the doorbell with the handshake function and the + * dword count. + */ + mpi_write_db(sc, MPI_DOORBELL_FUNCTION(MPI_FUNCTION_HANDSHAKE) | + MPI_DOORBELL_DWORDS(dwords)); + + /* + * the doorbell used bit will be set because a doorbell function has + * started. Wait for the interrupt and then ack it. + */ + if (mpi_wait_db_int(sc) != 0) + return (1); + mpi_write_intr(sc, 0); + + /* poll for the acknowledgement. */ + if (mpi_wait_db_ack(sc) != 0) + return (1); + + /* write the query through the doorbell. */ + for (i = 0; i < dwords; i++) { + mpi_write_db(sc, htole32(query[i])); + if (mpi_wait_db_ack(sc) != 0) + return (1); + } + + return (0); +} + +int +mpi_handshake_recv_dword(struct mpi_softc *sc, u_int32_t *dword) +{ + u_int16_t *words = (u_int16_t *)dword; + int i; + + for (i = 0; i < 2; i++) { + if (mpi_wait_db_int(sc) != 0) + return (1); + words[i] = letoh16(mpi_read_db(sc) & MPI_DOORBELL_DATA_MASK); + mpi_write_intr(sc, 0); + } + + return (0); +} + +int +mpi_handshake_recv(struct mpi_softc *sc, void *buf, size_t dwords) +{ + struct mpi_msg_reply *reply = buf; + u_int32_t *dbuf = buf, dummy; + int i; + + /* get the first dword so we can read the length out of the header. */ + if (mpi_handshake_recv_dword(sc, &dbuf[0]) != 0) + return (1); + + DPRINTFN(10, "%s: %s dwords: %d reply: %d\n", DEVNAME(sc), __func__, + dwords, reply->msg_length); + + /* + * the total length, in dwords, is in the message length field of the + * reply header. + */ + for (i = 1; i < MIN(dwords, reply->msg_length); i++) { + if (mpi_handshake_recv_dword(sc, &dbuf[i]) != 0) + return (1); + } + + /* if there's extra stuff to come off the ioc, discard it */ + while (i++ < reply->msg_length) { + if (mpi_handshake_recv_dword(sc, &dummy) != 0) + return (1); + DPRINTFN(10, "%s: %s dummy read: 0x%08x\n", DEVNAME(sc), + __func__, dummy); + } + + /* wait for the doorbell used bit to be reset and clear the intr */ + if (mpi_wait_db_int(sc) != 0) + return (1); + mpi_write_intr(sc, 0); + + return (0); +} + +int +mpi_iocfacts(struct mpi_softc *sc) +{ + struct mpi_msg_iocfacts_request ifq; + struct mpi_msg_iocfacts_reply ifp; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + bzero(&ifq, sizeof(ifq)); + bzero(&ifp, sizeof(ifp)); + + ifq.function = MPI_FUNCTION_IOC_FACTS; + ifq.chain_offset = 0; + ifq.msg_flags = 0; + ifq.msg_context = htole32(0xdeadbeef); + + if (mpi_handshake_send(sc, &ifq, dwordsof(ifq)) != 0) { + DPRINTF("%s: %s send failed\n", DEVNAME(sc), __func__); + return (1); + } + + if (mpi_handshake_recv(sc, &ifp, dwordsof(ifp)) != 0) { + DPRINTF("%s: %s recv failed\n", DEVNAME(sc), __func__); + return (1); + } + +#ifdef MPI_DEBUG + printf("%s: func: 0x%02x len: %d msgver: %d.%d\n", + DEVNAME(sc), ifp.function, ifp.msg_length, + ifp.msg_version_maj, ifp.msg_version_min); + + printf("%s: msgflags: 0x%02x iocnumber: 0x%02x hdrver: %d.%d\n", + DEVNAME(sc), ifp.msg_flags, ifp.ioc_number, + ifp.header_version_maj, ifp.header_version_min); + + printf("%s: message context: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.msg_context)); + + printf("%s: iocstatus: 0x%04x ioexcept: 0x%04x\n", DEVNAME(sc), + letoh16(ifp.ioc_status), letoh16(ifp.ioc_exceptions)); + + printf("%s: iocloginfo: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.ioc_loginfo)); + + printf("%s: flags: 0x%02x blocksize: %d whoinit: 0x%02x " + "maxchdepth: %d\n", DEVNAME(sc), ifp.flags, ifp.block_size, + ifp.whoinit, ifp.max_chain_depth); + + printf("%s: reqfrsize: %d replyqdepth: %d\n", DEVNAME(sc), + letoh16(ifp.request_frame_size), letoh16(ifp.reply_queue_depth)); + + printf("%s: productid: 0x%04x\n", DEVNAME(sc), + letoh16(ifp.product_id)); + + printf("%s: hostmfahiaddr: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.current_host_mfa_hi_addr)); + + printf("%s: event_state: 0x%02x number_of_ports: %d " + "global_credits: %d\n", + DEVNAME(sc), ifp.event_state, ifp.number_of_ports, + letoh16(ifp.global_credits)); + + printf("%s: sensebufhiaddr: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.current_sense_buffer_hi_addr)); + + printf("%s: maxbus: %d maxdev: %d replyfrsize: %d\n", DEVNAME(sc), + ifp.max_buses, ifp.max_devices, + letoh16(ifp.current_reply_frame_size)); + + printf("%s: fw_image_size: %d\n", DEVNAME(sc), + letoh32(ifp.fw_image_size)); + + printf("%s: ioc_capabilities: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.ioc_capabilities)); + + printf("%s: fw_version: %d.%d fw_version_unit: 0x%02x " + "fw_version_dev: 0x%02x\n", DEVNAME(sc), ifp.fw_version_maj, + ifp.fw_version_min, ifp.fw_version_unit, ifp.fw_version_dev); + + printf("%s: hi_priority_queue_depth: 0x%04x\n", DEVNAME(sc), + letoh16(ifp.hi_priority_queue_depth)); + + printf("%s: host_page_buffer_sge: hdr: 0x%08x addr 0x%08x %08x\n", + DEVNAME(sc), letoh32(ifp.host_page_buffer_sge.sg_hdr), + letoh32(ifp.host_page_buffer_sge.sg_hiaddr), + letoh32(ifp.host_page_buffer_sge.sg_loaddr)); +#endif /* MPI_DEBUG */ + + sc->sc_maxcmds = letoh16(ifp.global_credits); + sc->sc_maxchdepth = ifp.max_chain_depth; + sc->sc_buswidth = ifp.max_devices; + + return (0); +} + +int +mpi_iocinit(struct mpi_softc *sc) +{ + struct mpi_msg_iocinit_request iiq; + struct mpi_msg_iocinit_reply iip; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + bzero(&iiq, sizeof(iiq)); + bzero(&iip, sizeof(iip)); + + iiq.function = MPI_FUNCTION_IOC_INIT; + iiq.whoinit = MPI_WHOINIT_HOST_DRIVER; + + iiq.max_devices = sc->sc_buswidth; + iiq.max_buses = 1; + + iiq.msg_context = htole32(0xd00fd00f); + + iiq.reply_frame_size = htole16(MPI_REPLY_SIZE); + + iiq.msg_version_maj = 0x01; + iiq.msg_version_min = 0x02; + + iiq.hdr_version_unit = 0x0d; + iiq.hdr_version_dev = 0x00; + + if (mpi_handshake_send(sc, &iiq, dwordsof(iiq)) != 0) { + DPRINTF("%s: %s send failed\n", DEVNAME(sc), __func__); + return (1); + } + + if (mpi_handshake_recv(sc, &iip, dwordsof(iip)) != 0) { + DPRINTF("%s: %s recv failed\n", DEVNAME(sc), __func__); + return (1); + } + +#ifdef MPI_DEBUG + printf("%s: function: 0x%02x msg_length: %d whoinit: 0x%02x\n", + DEVNAME(sc), iip.function, iip.msg_length, iip.whoinit); + + printf("%s: msg_flags: 0x%02x max_buses: %d max_devices: %d " + "flags: 0x%02x\n", DEVNAME(sc), iip.msg_flags, iip.max_buses, + iip.max_devices, iip.flags); + + printf("%s: msg_context: 0x%08x\n", DEVNAME(sc), + letoh32(iip.msg_context)); + + printf("%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(iip.ioc_status)); + + printf("%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(iip.ioc_loginfo)); +#endif /* MPI_DEBUG */ + + return (0); +} + +int +mpi_portfacts(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + struct mpi_msg_portfacts_request *pfq; + int s; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + s = splbio(); + ccb = mpi_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DPRINTF("%s: %s ccb_get\n", DEVNAME(sc), __func__); + return (1); + } + + ccb->ccb_done = mpi_portfacts_done; + pfq = ccb->ccb_cmd; + + pfq->function = MPI_FUNCTION_PORT_FACTS; + pfq->chain_offset = 0; + pfq->msg_flags = 0; + pfq->port_number = 0; + pfq->msg_context = htole32(ccb->ccb_id); + + s = splbio(); + mpi_start(sc, ccb); + mpi_complete(sc, ccb); + splx(s); + + return (0); +} + +void +mpi_portfacts_done(struct mpi_ccb *ccb, void *reply, paddr_t reply_dva) +{ + struct mpi_softc *sc = ccb->ccb_sc; + struct mpi_msg_portfacts_reply *pfp = reply; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + if (pfp == NULL) + panic("%s: empty portfacts reply\n", DEVNAME(sc)); + +#ifdef MPI_DEBUG + printf("%s: function: 0x%02x msg_length: %d\n", DEVNAME(sc), + pfp->function, pfp->msg_length); + + printf("%s: msg_flags: 0x%02x port_number: %d\n", DEVNAME(sc), + pfp->msg_flags, pfp->port_number); + + printf("%s: msg_context: 0x%08x\n", DEVNAME(sc), + letoh32(pfp->msg_context)); + + printf("%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(pfp->ioc_status)); + + printf("%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(pfp->ioc_loginfo)); + + printf("%s: max_devices: %d port_type: 0x%02x\n", DEVNAME(sc), + letoh16(pfp->max_devices), pfp->port_type); + + printf("%s: protocol_flags: 0x%04x port_scsi_id: %d\n", + DEVNAME(sc), letoh16(pfp->protocol_flags), + letoh16(pfp->port_scsi_id)); + + printf("%s: max_persistent_ids: %d max_posted_cmd_buffers: %d\n", + DEVNAME(sc), letoh16(pfp->max_persistent_ids), + letoh16(pfp->max_posted_cmd_buffers)); + + printf("%s: max_lan_buckets: %d\n", DEVNAME(sc), + letoh16(pfp->max_lan_buckets)); +#endif /* MPI_DEBUG */ + + sc->sc_porttype = pfp->port_type; + sc->sc_target = letoh16(pfp->port_scsi_id); + + mpi_push_reply(sc, reply_dva); + mpi_put_ccb(sc, ccb); +} + +int +mpi_eventnotify(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + struct mpi_msg_event_request *enq; + int s; + + s = splbio(); + ccb = mpi_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DPRINTF("%s: %s ccb_get\n", DEVNAME(sc), __func__); + return (1); + } + + ccb->ccb_done = mpi_eventnotify_done; + enq = ccb->ccb_cmd; + + enq->function = MPI_FUNCTION_EVENT_NOTIFICATION; + enq->chain_offset = 0; + enq->ev_switch = 1; + enq->msg_context = htole32(ccb->ccb_id); + + mpi_start(sc, ccb); + + return (0); +} + +void +mpi_eventnotify_done(struct mpi_ccb *ccb, void *reply, paddr_t reply_dva) +{ + struct mpi_softc *sc = ccb->ccb_sc; + struct mpi_msg_event_reply *enp = reply; + u_int32_t *data; + int i; + + printf("%s: %s\n", DEVNAME(sc), __func__); + + printf("%s: function: 0x%02x msg_length: %d data_length: %d\n", + DEVNAME(sc), enp->function, enp->msg_length, + letoh16(enp->data_length)); + + printf("%s: ack_required: %d msg_flags 0x%02x\n", DEVNAME(sc), + enp->msg_flags, enp->msg_flags); + + printf("%s: msg_context: 0x%08x\n", DEVNAME(sc), + letoh32(enp->msg_context)); + + printf("%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(enp->ioc_status)); + + printf("%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(enp->ioc_loginfo)); + + data = reply; + data += dwordsof(struct mpi_msg_event_reply); + for (i = 0; i < letoh16(enp->data_length); i++) { + printf("%s: data[%d]: 0x%08x\n", DEVNAME(sc), i, data[i]); + } +} + +int +mpi_portenable(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + struct mpi_msg_portenable_request *peq; + int s; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + s = splbio(); + ccb = mpi_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DPRINTF("%s: %s ccb_get\n", DEVNAME(sc), __func__); + return (1); + } + + ccb->ccb_done = mpi_portenable_done; + peq = ccb->ccb_cmd; + + peq->function = MPI_FUNCTION_PORT_ENABLE; + peq->port_number = 0; + peq->msg_context = htole32(ccb->ccb_id); + + s = splbio(); + mpi_start(sc, ccb); + mpi_complete(sc, ccb); + splx(s); + + return (0); +} + +void +mpi_portenable_done(struct mpi_ccb *ccb, void *reply, paddr_t reply_dva) +{ + struct mpi_softc *sc = ccb->ccb_sc; + struct mpi_msg_portenable_reply *pep = reply; + + DPRINTF("%s: %s\n", DEVNAME(sc), __func__); + + if (pep == NULL) + panic("%s: empty portfacts reply\n", DEVNAME(sc)); + + mpi_push_reply(sc, reply_dva); + mpi_put_ccb(sc, ccb); +} + +int +mpi_oportfacts(struct mpi_softc *sc) +{ + struct mpi_msg_portfacts_request pfq; + struct mpi_msg_portfacts_reply pfp; + + bzero(&pfq, sizeof(pfq)); + bzero(&pfp, sizeof(pfp)); + + pfq.function = MPI_FUNCTION_PORT_FACTS; + pfq.chain_offset = 0; + pfq.msg_flags = 0; + pfq.port_number = 0; + pfq.msg_context = htole32(0xdeadbeef); + + if (mpi_handshake_send(sc, &pfq, dwordsof(pfq)) != 0) { + DPRINTF("%s: %s send failed\n", DEVNAME(sc), __func__); + return (1); + } + + if (mpi_handshake_recv(sc, &pfp, dwordsof(pfp)) != 0) { + DPRINTF("%s: %s recv failed\n", DEVNAME(sc), __func__); + return (1); + } + +#ifdef MPI_DEBUG + printf("%s: function: 0x%02x msg_length: %d\n", DEVNAME(sc), + pfp.function, pfp.msg_length); + + printf("%s: msg_flags: 0x%02x port_number: %d\n", DEVNAME(sc), + pfp.msg_flags, pfp.port_number); + + printf("%s: msg_context: 0x%08x\n", DEVNAME(sc), + letoh32(pfp.msg_context)); + + printf("%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(pfp.ioc_status)); + + printf("%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(pfp.ioc_loginfo)); + + printf("%s: max_devices: %d port_type: 0x%02x\n", DEVNAME(sc), + letoh16(pfp.max_devices), pfp.port_type); + + printf("%s: protocol_flags: 0x%04x port_scsi_id: %d\n", + DEVNAME(sc), letoh16(pfp.protocol_flags), + letoh16(pfp.port_scsi_id)); + + printf("%s: max_persistent_ids: %d max_posted_cmd_buffers: %d\n", + DEVNAME(sc), letoh16(pfp.max_persistent_ids), + letoh16(pfp.max_posted_cmd_buffers)); + + printf("%s: max_lan_buckets: %d\n", DEVNAME(sc), + letoh16(pfp.max_lan_buckets)); +#endif /* MPI_DEBUG */ + + sc->sc_porttype = pfp.port_type; + sc->sc_target = letoh16(pfp.port_scsi_id); + + return (0); +} diff --git a/sys/dev/ic/mpireg.h b/sys/dev/ic/mpireg.h new file mode 100644 index 00000000000..c8fa1e27f75 --- /dev/null +++ b/sys/dev/ic/mpireg.h @@ -0,0 +1,694 @@ +/* $OpenBSD: mpireg.h,v 1.1 2006/05/27 19:03:55 dlg Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * System Interface Register Set + */ + +#define MPI_DOORBELL 0x00 +/* doorbell read bits */ +#define MPI_DOORBELL_STATE (0xf<<28) /* ioc state */ +#define MPI_DOORBELL_STATE_RESET (0x0<<28) +#define MPI_DOORBELL_STATE_READY (0x1<<28) +#define MPI_DOORBELL_STATE_OPER (0x2<<28) +#define MPI_DOORBELL_STATE_FAULT (0x4<<28) +#define MPI_DOORBELL_INUSE (0x1<<27) /* doorbell used */ +#define MPI_DOORBELL_WHOINIT (0x7<<24) /* last to reset ioc */ +#define MPI_DOORBELL_WHOINIT_NOONE (0x0<<24) /* not initialized */ +#define MPI_DOORBELL_WHOINIT_SYSBIOS (0x1<<24) /* system bios */ +#define MPI_DOORBELL_WHOINIT_ROMBIOS (0x2<<24) /* rom bios */ +#define MPI_DOORBELL_WHOINIT_PCIPEER (0x3<<24) /* pci peer */ +#define MPI_DOORBELL_WHOINIT_DRIVER (0x4<<24) /* host driver */ +#define MPI_DOORBELL_WHOINIT_MANUFACT (0x5<<24) /* manufacturing */ +#define MPI_DOORBELL_FAULT (0xffff<<0) /* fault code */ +#define MPI_DOORBELL_FAULT_REQ_PCIPAR 0x8111 /* req msg pci parity err */ +#define MPI_DOORBELL_FAULT_REQ_PCIBUS 0x8112 /* req msg pci bus err */ +#define MPI_DOORBELL_FAULT_REP_PCIPAR 0x8113 /* reply msg pci parity err */ +#define MPI_DOORBELL_FAULT_REP_PCIBUS 0x8114 /* reply msg pci bus err */ +#define MPI_DOORBELL_FAULT_SND_PCIPAR 0x8115 /* data send pci parity err */ +#define MPI_DOORBELL_FAULT_SND_PCIBUS 0x8116 /* data send pci bus err */ +#define MPI_DOORBELL_FAULT_RCV_PCIPAR 0x8117 /* data recv pci parity err */ +#define MPI_DOORBELL_FAULT_RCV_PCIBUS 0x8118 /* data recv pci bus err */ +/* doorbell write bits */ +#define MPI_DOORBELL_FUNCTION_SHIFT 24 +#define MPI_DOORBELL_FUNCTION_MASK (0xff << MPI_DOORBELL_FUNCTION_SHIFT) +#define MPI_DOORBELL_FUNCTION(x) \ + (((x) << MPI_DOORBELL_FUNCTION_SHIFT) & MPI_DOORBELL_FUNCTION_MASK) +#define MPI_DOORBELL_DWORDS_SHIFT 16 +#define MPI_DOORBELL_DWORDS_MASK (0xff << MPI_DOORBELL_DWORDS_SHIFT) +#define MPI_DOORBELL_DWORDS(x) \ + (((x) << MPI_DOORBELL_DWORDS_SHIFT) & MPI_DOORBELL_DWORDS_MASK) +#define MPI_DOORBELL_DATA_MASK 0xffff + +#define MPI_WRITESEQ 0x04 +#define MPI_WRITESEQ_VALUE 0x0000000f /* key value */ +#define MPI_WRITESEQ_1 0x04 +#define MPI_WRITESEQ_2 0x0b +#define MPI_WRITESEQ_3 0x02 +#define MPI_WRITESEQ_4 0x07 +#define MPI_WRITESEQ_5 0x0d + +#define MPI_HOSTDIAG 0x08 +#define MPI_HOSTDIAG_CLEARFBS (1<<10) /* clear flash bad sig */ +#define MPI_HOSTDIAG_POICB (1<<9) /* prevent ioc boot */ +#define MPI_HOSTDIAG_DWRE (1<<7) /* diag reg write enabled */ +#define MPI_HOSTDIAG_FBS (1<<6) /* flash bad sig */ +#define MPI_HOSTDIAG_RESET_HIST (1<<5) /* reset history */ +#define MPI_HOSTDIAG_DIAGWR_EN (1<<4) /* diagnostic write enabled */ +#define MPI_HOSTDIAG_RESET_ADAPTER (1<<2) /* reset adapter */ +#define MPI_HOSTDIAG_DISABLE_ARM (1<<1) /* disable arm */ +#define MPI_HOSTDIAG_DIAGMEM_EN (1<<0) /* diag mem enable */ + +#define MPI_TESTBASE 0x0c + +#define MPI_DIAGRWDATA 0x10 + +#define MPI_DIAGRWADDR 0x18 + +#define MPI_INTR_STATUS 0x30 +#define MPI_INTR_STATUS_IOCDOORBELL (1<<31) /* ioc doorbell status */ +#define MPI_INTR_STATUS_REPLY (1<<3) /* reply message interrupt */ +#define MPI_INTR_STATUS_DOORBELL (1<<0) /* doorbell interrupt */ + +#define MPI_INTR_MASK 0x34 +#define MPI_INTR_MASK_REPLY (1<<3) /* reply message intr mask */ +#define MPI_INTR_MASK_DOORBELL (1<<0) /* doorbell interrupt mask */ + +#define MPI_REQ_QUEUE 0x40 + +#define MPI_REPLY_QUEUE 0x44 +#define MPI_REPLY_QUEUE_ADDRESS (1<<31) /* address reply */ +#define MPI_REPLY_QUEUE_ADDRESS_MASK 0x7fffffff +#define MPI_REPLY_QUEUE_TYPE_MASK (3<<29) +#define MPI_REPLY_QUEUE_TYPE_INIT (0<<29) /* scsi initiator reply */ +#define MPI_REPLY_QUEUE_TYPE_TARGET (1<<29) /* scsi target reply */ +#define MPI_REPLY_QUEUE_TYPE_LAN (2<<29) /* lan reply */ +#define MPI_REPLY_QUEUE_CONTEXT 0x1fffffff /* not address and type */ + +#define MPI_PRIREQ_QUEUE 0x48 + +/* + * Scatter Gather Lists + */ + +#define MPI_SGE_FL_LAST (0x1<<31) /* last element in segment */ +#define MPI_SGE_FL_EOB (0x1<<30) /* last element of buffer */ +#define MPI_SGE_FL_TYPE (0x3<<28) /* element type */ +#define MPI_SGE_FL_TYPE_SIMPLE (0x1<<28) /* simple element */ +#define MPI_SGE_FL_TYPE_CHAIN (0x3<<28) /* chain element */ +#define MPI_SGE_FL_TYPE_XACTCTX (0x0<<28) /* transaction context */ +#define MPI_SGE_FL_LOCAL (0x1<<27) /* local address */ +#define MPI_SGE_FL_DIR (0x1<<26) /* direction */ +#define MPI_SGE_FL_DIR_OUT (0x1<<26) +#define MPI_SGE_FL_DIR_IN (0x0<<26) +#define MPI_SGE_FL_SIZE (0x1<<25) /* address size */ +#define MPI_SGE_FL_SIZE_32 (0x0<<25) /* address size */ +#define MPI_SGE_FL_SIZE_64 (0x1<<25) /* address size */ +#define MPI_SGE_FL_EOL (0x1<<24) /* end of list */ + +struct mpi_sge32 { + u_int32_t sg_hdr; + u_int32_t sg_addr; +} __packed; + +struct mpi_sge64 { + u_int32_t sg_hdr; + u_int32_t sg_loaddr; + u_int32_t sg_hiaddr; +} __packed; + +/* XXX */ +struct mpi_sgl_se { + u_int32_t sg_hdr; + u_int32_t sg_loaddr; + u_int32_t sg_hiaddr; +} __packed; + +struct mpi_sgl_ce { + u_int32_t sg_hdr; + u_int32_t sg_loaddr; + u_int32_t sg_hiaddr; +} __packed; + +struct mpi_sgl_tce { + u_int32_t sg_hdr; + u_int32_t sg_loaddr; + u_int32_t sg_hiaddr; +} __packed; + +/* + * Messages + */ + +/* functions */ +#define MPI_FUNCTION_SCSI_IO_REQUEST (0x00) +#define MPI_FUNCTION_SCSI_TASK_MGMT (0x01) +#define MPI_FUNCTION_IOC_INIT (0x02) +#define MPI_FUNCTION_IOC_FACTS (0x03) +#define MPI_FUNCTION_CONFIG (0x04) +#define MPI_FUNCTION_PORT_FACTS (0x05) +#define MPI_FUNCTION_PORT_ENABLE (0x06) +#define MPI_FUNCTION_EVENT_NOTIFICATION (0x07) +#define MPI_FUNCTION_EVENT_ACK (0x08) +#define MPI_FUNCTION_FW_DOWNLOAD (0x09) +#define MPI_FUNCTION_TARGET_CMD_BUFFER_POST (0x0A) +#define MPI_FUNCTION_TARGET_ASSIST (0x0B) +#define MPI_FUNCTION_TARGET_STATUS_SEND (0x0C) +#define MPI_FUNCTION_TARGET_MODE_ABORT (0x0D) +#define MPI_FUNCTION_TARGET_FC_BUF_POST_LINK_SRVC (0x0E) /* obsolete */ +#define MPI_FUNCTION_TARGET_FC_RSP_LINK_SRVC (0x0F) /* obsolete */ +#define MPI_FUNCTION_TARGET_FC_EX_SEND_LINK_SRVC (0x10) /* obsolete */ +#define MPI_FUNCTION_TARGET_FC_ABORT (0x11) /* obsolete */ +#define MPI_FUNCTION_FC_LINK_SRVC_BUF_POST (0x0E) +#define MPI_FUNCTION_FC_LINK_SRVC_RSP (0x0F) +#define MPI_FUNCTION_FC_EX_LINK_SRVC_SEND (0x10) +#define MPI_FUNCTION_FC_ABORT (0x11) +#define MPI_FUNCTION_FW_UPLOAD (0x12) +#define MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND (0x13) +#define MPI_FUNCTION_FC_PRIMITIVE_SEND (0x14) + +#define MPI_FUNCTION_RAID_ACTION (0x15) +#define MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) + +#define MPI_FUNCTION_TOOLBOX (0x17) + +#define MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) + +#define MPI_FUNCTION_MAILBOX (0x19) + +#define MPI_FUNCTION_LAN_SEND (0x20) +#define MPI_FUNCTION_LAN_RECEIVE (0x21) +#define MPI_FUNCTION_LAN_RESET (0x22) + +#define MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) +#define MPI_FUNCTION_IO_UNIT_RESET (0x41) +#define MPI_FUNCTION_HANDSHAKE (0x42) +#define MPI_FUNCTION_REPLY_FRAME_REMOVAL (0x43) + +/* reply flags */ +#define MPI_REP_FLAGS_CONT (1<<7) /* continuation reply */ + +#define MPI_REP_IOCSTATUS_AVAIL (1<<15) /* logging info available */ +#define MPI_REP_IOCSTATUS (0x7fff) /* status */ + +/* Common IOCStatus values for all replies */ +#define MPI_IOCSTATUS_SUCCESS (0x0000) +#define MPI_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI_IOCSTATUS_BUSY (0x0002) +#define MPI_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI_IOCSTATUS_RESERVED (0x0005) +#define MPI_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI_IOCSTATUS_INVALID_STATE (0x0008) +#define MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) +/* Config IOCStatus values */ +#define MPI_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) +/* SCSIIO Reply (SPI & FCP) initiator values */ +#define MPI_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI_IOCSTATUS_SCSI_INVALID_BUS (0x0041) +#define MPI_IOCSTATUS_SCSI_INVALID_TARGETID (0x0042) +#define MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPI_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPI_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) +/* For use by SCSI Initiator and SCSI Target end-to-end data protection */ +#define MPI_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) +#define MPI_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) +#define MPI_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) +/* SCSI (SPI & FCP) target values */ +#define MPI_IOCSTATUS_TARGET_PRIORITY_IO (0x0060) +#define MPI_IOCSTATUS_TARGET_INVALID_PORT (0x0061) +#define MPI_IOCSTATUS_TARGET_INVALID_IOCINDEX (0x0062) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) +#define MPI_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPI_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) +#define MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x006B) +#define MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) +#define MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) +#define MPI_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) +/* Additional FCP target values */ +#define MPI_IOCSTATUS_TARGET_FC_ABORTED (0x0066) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_RX_ID_INVALID (0x0067) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_DID_INVALID (0x0068) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_NODE_LOGGED_OUT (0x0069) /* obsolete */ +/* Fibre Channel Direct Access values */ +#define MPI_IOCSTATUS_FC_ABORTED (0x0066) +#define MPI_IOCSTATUS_FC_RX_ID_INVALID (0x0067) +#define MPI_IOCSTATUS_FC_DID_INVALID (0x0068) +#define MPI_IOCSTATUS_FC_NODE_LOGGED_OUT (0x0069) +#define MPI_IOCSTATUS_FC_EXCHANGE_CANCELED (0x006C) +/* LAN values */ +#define MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND (0x0080) +#define MPI_IOCSTATUS_LAN_DEVICE_FAILURE (0x0081) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ERROR (0x0082) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED (0x0083) +#define MPI_IOCSTATUS_LAN_RECEIVE_ERROR (0x0084) +#define MPI_IOCSTATUS_LAN_RECEIVE_ABORTED (0x0085) +#define MPI_IOCSTATUS_LAN_PARTIAL_PACKET (0x0086) +#define MPI_IOCSTATUS_LAN_CANCELED (0x0087) +/* Serial Attached SCSI values */ +#define MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) +#define MPI_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) +/* Inband values */ +#define MPI_IOCSTATUS_INBAND_ABORTED (0x0098) +#define MPI_IOCSTATUS_INBAND_NO_CONNECTION (0x0099) +/* Diagnostic Tools values */ +#define MPI_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) + +#define MPI_REP_IOCLOGINFO_TYPE (0xf<<28) /* logging info type */ +#define MPI_REP_IOCLOGINFO_TYPE_NONE (0x0<<28) +#define MPI_REP_IOCLOGINFO_TYPE_SCSI (0x1<<28) +#define MPI_REP_IOCLOGINFO_TYPE_FC (0x2<<28) +#define MPI_REP_IOCLOGINFO_TYPE_SAS (0x3<<28) +#define MPI_REP_IOCLOGINFO_TYPE_ISCSI (0x4<<28) +#define MPI_REP_IOCLOGINFO_DATA (0x0fffffff) /* logging info data */ + +/* messages */ + +#define MPI_WHOINIT_NOONE 0x00 +#define MPI_WHOINIT_SYSTEM_BIOS 0x01 +#define MPI_WHOINIT_ROM_BIOS 0x02 +#define MPI_WHOINIT_PCI_PEER 0x03 +#define MPI_WHOINIT_HOST_DRIVER 0x04 +#define MPI_WHOINIT_MANUFACTURER 0x05 + +/* default messages */ + +struct mpi_msg_request { + u_int8_t reserved1; + u_int8_t reserved2; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved3; + u_int8_t reserved4; + u_int8_t reserved5; + u_int8_t msg_flags; + + u_int32_t msg_context; +} __packed; + +struct mpi_msg_reply { + u_int8_t reserved1; + u_int8_t reserved2; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t reserved3; + u_int8_t reserved4; + u_int8_t reserved5; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int8_t reserved6; + u_int8_t reserved7; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + +/* ioc init */ + +struct mpi_msg_iocinit_request { + u_int8_t whoinit; + u_int8_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t flags; +#define MPI_IOCINIT_F_DISCARD_FW (1<<0) +#define MPI_IOCINIT_F_ENABLE_HOST_FIFO (1<<1) +#define MPI_IOCINIT_F_HOST_PG_BUF_PERSIST (1<<2) + u_int8_t max_devices; + u_int8_t max_buses; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t reply_frame_size; + u_int16_t reserved2; + + u_int32_t host_mfa_hi_addr; + u_int32_t sense_buffer_hi_addr; + u_int32_t reply_fifo_host_signalling_addr; + struct mpi_sgl_se host_page_buffer_sge; + + u_int8_t msg_version_min; + u_int8_t msg_version_maj; + + u_int8_t hdr_version_unit; + u_int8_t hdr_version_dev; +} __packed; + +struct mpi_msg_iocinit_reply { + u_int8_t whoinit; + u_int8_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t flags; + u_int8_t max_devices; + u_int8_t max_buses; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t reserved2; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + + +/* ioc facts */ +struct mpi_msg_iocfacts_request { + u_int8_t reserved1; + u_int8_t reserved2; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved3; + u_int8_t reserved4; + u_int8_t reserved5; + u_int8_t msg_flags; + + u_int32_t msg_context; +} __packed; + +struct mpi_msg_iocfacts_reply { + u_int8_t msg_version_min; + u_int8_t msg_version_maj; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t header_version_min; + u_int8_t header_version_maj; + u_int8_t ioc_number; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t ioc_exceptions; +#define MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (1<<0) +#define MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (1<<1) +#define MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (1<<2) +#define MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL (1<<3) + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int8_t max_chain_depth; + u_int8_t whoinit; + u_int8_t block_size; + u_int8_t flags; +#define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (1<<0) +#define MPI_IOCFACTS_FLAGS_REPLY_FIFO_HOST_SIGNAL (1<<1) +#define MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT (1<<2) + + u_int16_t reply_queue_depth; + u_int16_t request_frame_size; + + u_int16_t reserved1; + u_int16_t product_id; /* product id */ + + u_int32_t current_host_mfa_hi_addr; + + u_int16_t global_credits; + u_int8_t number_of_ports; + u_int8_t event_state; + + u_int32_t current_sense_buffer_hi_addr; + + u_int16_t current_reply_frame_size; + u_int8_t max_devices; + u_int8_t max_buses; + + u_int32_t fw_image_size; + + u_int32_t ioc_capabilities; +#define MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q (1<<0) +#define MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL (1<<1) +#define MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING (1<<2) +#define MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (1<<3) +#define MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (1<<4) +#define MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (1<<5) +#define MPI_IOCFACTS_CAPABILITY_EEDP (1<<6) +#define MPI_IOCFACTS_CAPABILITY_BIDIRECTIONAL (1<<7) +#define MPI_IOCFACTS_CAPABILITY_MULTICAST (1<<8) +#define MPI_IOCFACTS_CAPABILITY_SCSIIO32 (1<<9) +#define MPI_IOCFACTS_CAPABILITY_NO_SCSIIO16 (1<<10) + + u_int8_t fw_version_dev; + u_int8_t fw_version_unit; + u_int8_t fw_version_min; + u_int8_t fw_version_maj; + + u_int16_t hi_priority_queue_depth; + u_int16_t reserved2; + + struct mpi_sgl_se host_page_buffer_sge; + + u_int32_t reply_fifo_host_signalling_addr; +} __packed; + +struct mpi_msg_portfacts_request { + u_int8_t reserved1; + u_int8_t reserved2; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved3; + u_int8_t reserved4; + u_int8_t port_number; + u_int8_t msg_flags; + + u_int32_t msg_context; + +} __packed; + +struct mpi_msg_portfacts_reply { + u_int16_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t port_number; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t reserved3; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int8_t reserved4; + u_int8_t port_type; +#define MPI_PORTFACTS_PORTTYPE_INACTIVE 0x00 +#define MPI_PORTFACTS_PORTTYPE_SCSI 0x01 +#define MPI_PORTFACTS_PORTTYPE_FC 0x10 +#define MPI_PORTFACTS_PORTTYPE_ISCSI 0x20 +#define MPI_PORTFACTS_PORTTYPE_SAS 0x30 + + u_int16_t max_devices; + + u_int16_t port_scsi_id; + u_int16_t protocol_flags; +#define MPI_PORTFACTS_PROTOCOL_LOGBUSADDR (1<<0) +#define MPI_PORTFACTS_PROTOCOL_LAN (1<<1) +#define MPI_PORTFACTS_PROTOCOL_TARGET (1<<2) +#define MPI_PORTFACTS_PROTOCOL_INITIATOR (1<<3) + + u_int16_t max_posted_cmd_buffers; + u_int16_t max_persistent_ids; + + u_int16_t max_lan_buckets; + u_int16_t reserved5; + + u_int32_t reserved6; +} __packed; + +struct mpi_msg_portenable_request { + u_int16_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t port_number; + u_int8_t msg_flags; + + u_int32_t msg_context; +} __packed; + +struct mpi_msg_portenable_reply { + u_int16_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t port_number; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t reserved3; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + +struct mpi_msg_event_request { + u_int8_t ev_switch; + u_int8_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved2[3]; + u_int8_t msg_flags; + + u_int32_t msg_context; +} __packed; + +struct mpi_msg_event_reply { + u_int16_t data_length; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved1; + u_int8_t ack_required; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t reserved2; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int32_t event; + + u_int32_t event_context; + + /* event data follows */ +} __packed; + +struct mpi_msg_scsi_io { + u_int8_t target_id; + u_int8_t bus; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t cdb_length; + u_int8_t sense_buf_len; + u_int8_t reserved; + u_int8_t msg_flags; +#define MPI_SCSIIO_EEDP 0xf0 +#define MPI_SCSIIO_CMD_DATA_DIR (1<<2) +#define MPI_SCSIIO_SENSE_BUF_LOC (1<<1) +#define MPI_SCSIIO_SENSE_BUF_ADDR_WIDTH (1<<0) + + u_int32_t msg_context; + + u_int16_t lun[4]; + + u_int32_t control; +#define MPI_SCSIIO_ADDITIONAL_CDB_LEN (0xf<<26) +#define MPI_SCSIIO_DATA_DIR (0x3<<24) +#define MPI_SCSIIO_DATA_DIR_NONE (0x0<<24) +#define MPI_SCSIIO_DATA_DIR_WRITE (0x1<<24) +#define MPI_SCSIIO_DATA_DIR_READ (0x2<<24) +#define MPI_SCSIIO_TASK_ATTR (0x7<<8) +#define MPI_SCSIIO_TASK_ATTR_SIMPLE_Q (0x0<<8) +#define MPI_SCSIIO_TASK_ATTR_HEAD_OF_Q (0x1<<8) +#define MPI_SCSIIO_TASK_ATTR_ORDERED_Q (0x2<<8) +#define MPI_SCSIIO_TASK_ATTR_ACA_Q (0x4<<8) +#define MPI_SCSIIO_TASK_ATTR_UNTAGGED (0x5<<8) +#define MPI_SCSIIO_TASK_ATTR_NO_DISCONNECT (0x7<<8) + +#define MPI_CDB_LEN 16 + u_int8_t cdb[MPI_CDB_LEN]; + + u_int32_t data_length; + + u_int32_t sense_buf_low_addr; + + /* followed by an sgl */ +} __packed; + +struct mpi_msg_scsi_io_error { + u_int8_t target_id; + u_int8_t bus; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t cdb_length; + u_int8_t sense_buf_len; + u_int8_t reserved1; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int8_t scsi_status; +#if notyet +#define MPI_SCSIIO_ERR_STATUS_SUCCESS +#define MPI_SCSIIO_ERR_STATUS_CHECK_COND +#define MPI_SCSIIO_ERR_STATUS_BUSY +#define MPI_SCSIIO_ERR_STATUS_INTERMEDIATE +#define MPI_SCSIIO_ERR_STATUS_INTERMEDIATE_CONDMET +#define MPI_SCSIIO_ERR_STATUS_RESERVATION_CONFLICT +#define MPI_SCSIIO_ERR_STATUS_CMD_TERM +#define MPI_SCSIIO_ERR_STATUS_TASK_SET_FULL +#define MPI_SCSIIO_ERR_STATUS_ACA_ACTIVE +#endif + u_int8_t scsi_state; +#define MPI_SCSIIO_ERR_STATE_AUTOSENSE_VALID (1<<0) +#define MPI_SCSIIO_ERR_STATE_AUTOSENSE_FAILED (1<<2) +#define MPI_SCSIIO_ERR_STATE_NO_SCSI_STATUS (1<<3) +#define MPI_SCSIIO_ERR_STATE_TERMINATED (1<<4) +#define MPI_SCSIIO_ERR_STATE_RESPONSE_INFO_VALID (1<<5) +#define MPI_SCSIIO_ERR_STATE_QUEUE_TAG_REJECTED (1<<6) + + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int32_t transfer_count; + + u_int32_t sense_count; + + u_int32_t response_info; + + u_int16_t tag; + u_int16_t reserved2; +} __packed; diff --git a/sys/dev/ic/mpivar.h b/sys/dev/ic/mpivar.h new file mode 100644 index 00000000000..d1a70619e71 --- /dev/null +++ b/sys/dev/ic/mpivar.h @@ -0,0 +1,93 @@ +/* $OpenBSD: mpivar.h,v 1.1 2006/05/27 19:03:55 dlg Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* XXX */ +#define MPI_MAX_SGL 32 + +#define MPI_REQUEST_SIZE 512 +#define MPI_REPLY_SIZE 128 + +struct mpi_dmamem { + bus_dmamap_t mdm_map; + bus_dma_segment_t mdm_seg; + size_t mdm_size; + caddr_t mdm_kva; +}; +#define MPI_DMA_MAP(_mdm) ((_mdm)->mdm_map) +#define MPI_DMA_DVA(_mdm) ((_mdm)->mdm_map->dm_segs[0].ds_addr) +#define MPI_DMA_KVA(_mdm) ((void *)(_mdm)->mdm_kva) + +struct mpi_ccb_bundle { + struct mpi_msg_scsi_io mcb_io; /* sgl must follow */ + struct mpi_sge32 mcb_sgl[MPI_MAX_SGL]; + struct scsi_sense_data mcb_sense; +} __packed; + +struct mpi_softc; + +struct mpi_ccb { + struct mpi_softc *ccb_sc; + int ccb_id; + + struct scsi_xfer *ccb_xs; + bus_dmamap_t ccb_dmamap; + + bus_addr_t ccb_offset; + void *ccb_cmd; + paddr_t ccb_cmd_dva; + + volatile enum { + MPI_CCB_FREE, + MPI_CCB_READY, + MPI_CCB_QUEUED + } ccb_state; + void (*ccb_done)(struct mpi_ccb *, void *, paddr_t); + + TAILQ_ENTRY(mpi_ccb) ccb_link; +}; + +TAILQ_HEAD(mpi_ccb_list, mpi_ccb); + +struct mpi_softc { + struct device sc_dev; + struct scsi_link sc_link; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_ios; + bus_dma_tag_t sc_dmat; + + int sc_maxcmds; + int sc_maxchdepth; + u_int8_t sc_porttype; + + int sc_buswidth; + int sc_target; + + struct mpi_dmamem *sc_requests; + struct mpi_ccb *sc_ccbs; + struct mpi_ccb_list sc_ccb_free, sc_ccb_runq; + + struct mpi_dmamem *sc_replies; +}; + +int mpi_attach(struct mpi_softc *); +void mpi_detach(struct mpi_softc *); + +int mpi_intr(void *); diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index e8ced38bbfc..1f2b12c9682 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.205 2006/05/14 19:00:48 damien Exp $ +# $OpenBSD: files.pci,v 1.206 2006/05/27 19:03:55 dlg Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -192,6 +192,10 @@ file dev/pci/isp_pci.c isp_pci attach mpt at pci with mpt_pci file dev/pci/mpt_pci.c mpt_pci +# LSI Logic Fusion-MPT Message Passing Interface +attach mpi at pci with mpi_pci +file dev/pci/mpi_pci.c mpi_pci + # Ethernet driver for DC21040-based boards device de: ether, ifnet, ifmedia attach de at pci diff --git a/sys/dev/pci/mpi_pci.c b/sys/dev/pci/mpi_pci.c new file mode 100644 index 00000000000..371a4c61427 --- /dev/null +++ b/sys/dev/pci/mpi_pci.c @@ -0,0 +1,168 @@ +/* $OpenBSD: mpi_pci.c,v 1.1 2006/05/27 19:03:55 dlg Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <machine/bus.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/mpireg.h> +#include <dev/ic/mpivar.h> + +int mpi_pci_match(struct device *, void *, void *); +void mpi_pci_attach(struct device *, struct device *, void *); +int mpi_pci_detach(struct device *, int); + +struct mpi_pci_softc { + struct mpi_softc psc_mpi; + + pci_chipset_tag_t psc_pc; + pcitag_t psc_tag; + + void *psc_ih; +}; + +struct cfattach mpi_pci_ca = { + sizeof(struct mpi_pci_softc), mpi_pci_match, mpi_pci_attach, + mpi_pci_detach +}; + +#define PREAD(s, r) pci_conf_read((s)->psc_pc, (s)->psc_tag, (r)) +#define PWRITE(s, r, v) pci_conf_write((s)->psc_pc, (s)->psc_tag, (r), (v)) + +#define MPP_DUAL 0x01 /* Dual port adapter */ + +static const struct pci_matchid mpi_devices[] = { + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_1030 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_FC909 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_FC909A }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_FC919 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_FC919_1 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_FC929 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_FC929_1 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS1068 } + +}; + +int +mpi_pci_match(struct device *parent, void *match, void *aux) +{ + struct pci_attach_args *pa = aux; + + return (pci_matchbyid(pa, mpi_devices, + sizeof(mpi_devices) / sizeof(mpi_devices[0])) * 2); +} + +void +mpi_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct mpi_pci_softc *psc = (void *)self; + struct mpi_softc *sc = &psc->psc_mpi; + struct pci_attach_args *pa = aux; + pcireg_t memtype; + int r; + pci_intr_handle_t ih; + const char *intrstr; + + psc->psc_pc = pa->pa_pc; + psc->psc_tag = pa->pa_tag; + psc->psc_ih = NULL; + sc->sc_dmat = pa->pa_dmat; + sc->sc_ios = 0; + + /* find the appropriate memory base */ + for (r = PCI_MAPREG_START; r < PCI_MAPREG_END; r += sizeof(memtype)) { + memtype = pci_mapreg_type(psc->psc_pc, psc->psc_tag, r); + if ((memtype & PCI_MAPREG_TYPE_MASK) == PCI_MAPREG_TYPE_MEM) + break; + } + if (r >= PCI_MAPREG_END) { + printf(": unable to locate system interface registers\n"); + return; + } + + if (pci_mapreg_map(pa, r, memtype, 0, &sc->sc_iot, &sc->sc_ioh, + NULL, &sc->sc_ios, 0) != 0) { + printf(": unable to map system interface registers\n"); + return; + } + + /* disable the expansion rom */ + PWRITE(psc, PCI_ROM_REG, PREAD(psc, PCI_ROM_REG & ~PCI_ROM_ENABLE)); + + /* hook up the interrupt */ + if (pci_intr_map(pa, &ih)) { + printf(": unable to map interrupt\n"); + goto unmap; + } + intrstr = pci_intr_string(psc->psc_pc, ih); + psc->psc_ih = pci_intr_establish(psc->psc_pc, ih, IPL_BIO, + mpi_intr, sc, sc->sc_dev.dv_xname); + if (psc->psc_ih == NULL) { + printf(": unable to map interrupt%s%s\n", + intrstr == NULL ? "" : " at ", + intrstr == NULL ? "" : intrstr); + goto unmap; + } + printf(": %s", intrstr); + + if (mpi_attach(sc) != 0) { + /* error printed by mpi_attach */ + goto deintr; + } + + return; + +deintr: + pci_intr_disestablish(psc->psc_pc, psc->psc_ih); + psc->psc_ih = NULL; +unmap: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + sc->sc_ios = 0; +} + +int +mpi_pci_detach(struct device *self, int flags) +{ + struct mpi_pci_softc *psc = (struct mpi_pci_softc *)self; + struct mpi_softc *sc = &psc->psc_mpi; + + mpi_detach(sc); + + if (psc->psc_ih != NULL) { + pci_intr_disestablish(psc->psc_pc, psc->psc_ih); + psc->psc_ih = NULL; + } + if (sc->sc_ios != 0) { + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + sc->sc_ios = 0; + } + + return (0); +} |