diff options
author | 2006-05-27 19:03:55 +0000 | |
---|---|---|
committer | 2006-05-27 19:03:55 +0000 | |
commit | 93cbb4bdfaba4b310b06a0c64008e22392c65afd (patch) | |
tree | 78a4e0f2917f223cbac0daebce0cd260fac42aa9 /sys/dev/ic/mpi.c | |
parent | scan for new timedelta sensors every five minutes for now, ok deraadt (diff) | |
download | wireguard-openbsd-93cbb4bdfaba4b310b06a0c64008e22392c65afd.tar.xz wireguard-openbsd-93cbb4bdfaba4b310b06a0c64008e22392c65afd.zip |
add mpi(4), an alternative (replacement) driver for lsi logic fusion mpt
controllers currently supported by mpt(4).
ok marco@
Diffstat (limited to 'sys/dev/ic/mpi.c')
-rw-r--r-- | sys/dev/ic/mpi.c | 1509 |
1 files changed, 1509 insertions, 0 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); +} |