diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/ami.c | 1053 | ||||
-rw-r--r-- | sys/dev/ic/amireg.h | 540 | ||||
-rw-r--r-- | sys/dev/ic/amivar.h | 121 | ||||
-rw-r--r-- | sys/dev/pci/ami_pci.c | 243 |
4 files changed, 1957 insertions, 0 deletions
diff --git a/sys/dev/ic/ami.c b/sys/dev/ic/ami.c new file mode 100644 index 00000000000..4a8d375df6e --- /dev/null +++ b/sys/dev/ic/ami.c @@ -0,0 +1,1053 @@ +/* $OpenBSD: ami.c,v 1.1 2001/03/09 11:14:21 mickey Exp $ */ + +/* + * Copyright (c) 2001 Michael Shalayeff + * All rights reserved. + * + * The SCSI emulation layer is derived from gdt(4) driver, + * Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Michael Shalayeff. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define AMI_DEBUG + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <uvm/uvm_extern.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/amireg.h> +#include <dev/ic/amivar.h> + +#ifdef AMI_DEBUG +#define AMI_DPRINTF(m,a) if (ami_debug & (m)) printf a +#define AMI_D_CMD 0x0001 +#define AMI_D_INTR 0x0002 +#define AMI_D_MISC 0x0004 +#define AMI_D_DMA 0x0008 +int ami_debug = 0xffff; +#else +#define AMI_DPRINTF(m,a) /* m, a */ +#endif + +struct cfdriver ami_cd = { + NULL, "ami", DV_DULL +}; + +int ami_scsi_cmd __P((struct scsi_xfer *xs)); +void amiminphys __P((struct buf *bp)); + +struct scsi_adapter ami_switch = { + ami_scsi_cmd, amiminphys, 0, 0, +}; + +struct scsi_device ami_dev = { + NULL, NULL, NULL, NULL +}; + +int ami_scsi_raw_cmd __P((struct scsi_xfer *xs)); + +struct scsi_adapter ami_raw_switch = { + ami_scsi_raw_cmd, amiminphys, 0, 0, +}; + +struct scsi_device ami_raw_dev = { + NULL, NULL, NULL, NULL +}; + +static __inline struct ami_ccb *ami_get_ccb __P((struct ami_softc *sc)); +static __inline void ami_put_ccb __P((struct ami_ccb *ccb)); +void ami_copyhds __P((struct ami_softc *sc, const u_int32_t *sizes, + const u_int8_t *props, const u_int8_t *stats)); +void *ami_allocmem __P((bus_dma_tag_t dmat, bus_dmamap_t *map, + bus_dma_segment_t *segp, size_t isize, size_t nent, const char *iname)); +void ami_freemem __P((bus_dma_tag_t dmat, bus_dmamap_t *map, + bus_dma_segment_t *segp, size_t isize, size_t nent, const char *iname)); +void ami_dispose __P((struct ami_softc *sc)); +void ami_requeue __P((void *v)); +int ami_cmd __P((struct ami_ccb *ccb, int flags, int wait)); +int ami_start __P((struct ami_ccb *ccb, int wait)); +int ami_complete __P((struct ami_ccb *ccb)); +int ami_done __P((struct ami_softc *sc, int idx)); +void ami_copy_internal_data __P((struct scsi_xfer *xs, void *v, size_t size)); +int ami_inquire __P((struct ami_softc *sc, u_int8_t op)); + + +static __inline struct ami_ccb * +ami_get_ccb(sc) + struct ami_softc *sc; +{ + struct ami_ccb *ccb; + + ccb = TAILQ_LAST(&sc->sc_free_ccb, ami_queue_head); + if (ccb) { + ccb->ccb_state = AMI_CCB_READY; + TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ccb_link); + } + return ccb; +} + +static __inline void +ami_put_ccb(ccb) + struct ami_ccb *ccb; +{ + struct ami_softc *sc = ccb->ccb_sc; + + ccb->ccb_state = AMI_CCB_FREE; + TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_link); +} + +void * +ami_allocmem(dmat, map, segp, isize, nent, iname) + bus_dma_tag_t dmat; + bus_dmamap_t *map; + bus_dma_segment_t *segp; + size_t isize, nent; + const char *iname; +{ + size_t total = isize * nent; + caddr_t p; + int error, rseg; + + /* XXX this is because we might have no dmamem_load_raw */ + if ((error = bus_dmamem_alloc(dmat, total, PAGE_SIZE, 0, segp, 1, + &rseg, BUS_DMA_NOWAIT))) { + printf(": cannot allocate %s%s (%d)\n", + iname, nent==1? "": "s", error); + return (NULL); + } + + if ((error = bus_dmamem_map(dmat, segp, rseg, total, &p, + BUS_DMA_NOWAIT))) { + printf(": cannot map %s%s (%d)\n", + iname, nent==1? "": "s", error); + return (NULL); + } + + bzero(p, total); + if ((error = bus_dmamap_create(dmat, total, 1, + total, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, map))) { + printf(": cannot create %s dmamap (%d)\n", iname, error); + return (NULL); + } + if ((error = bus_dmamap_load(dmat, *map, p, total, NULL, + BUS_DMA_NOWAIT))) { + printf(": cannot load %s dma map (%d)\n", iname, error); + return (NULL); + } + + return (p); +} + +void +ami_freemem(dmat, map, segp, isize, nent, iname) + bus_dma_tag_t dmat; + bus_dmamap_t *map; + bus_dma_segment_t *segp; + size_t isize, nent; + const char *iname; +{ + bus_dmamem_free(dmat, segp, 1); + bus_dmamap_destroy(dmat, *map); + *map = NULL; +} + +void +ami_dispose(sc) + struct ami_softc *sc; +{ + register struct ami_ccb *ccb; + + /* traverse the ccbs and destroy the maps */ + for (ccb = &sc->sc_ccbs[AMI_MAXCMDS - 1]; ccb > sc->sc_ccbs; ccb--) + if (ccb->ccb_dmamap) + bus_dmamap_destroy(sc->dmat, ccb->ccb_dmamap); + ami_freemem(sc->dmat, &sc->sc_sgmap, sc->sc_sgseg, + sizeof(struct ami_sgent) * AMI_SGEPERCMD, AMI_MAXCMDS, "sglist"); + ami_freemem(sc->dmat, &sc->sc_cmdmap, sc->sc_cmdseg, + sizeof(struct ami_iocmd), AMI_MAXCMDS + 1, "command"); +} + + +void +ami_copyhds(sc, sizes, props, stats) + struct ami_softc *sc; + const u_int32_t *sizes; + const u_int8_t *props, *stats; +{ + int i; + + for (i = 0; i < sc->sc_nunits; i++) { + sc->sc_hdr[i].hd_present = 1; + sc->sc_hdr[i].hd_is_logdrv = 1; + sc->sc_hdr[i].hd_size = sizes[i]; + sc->sc_hdr[i].hd_prop = props[i]; + sc->sc_hdr[i].hd_stat = stats[i]; + if (sizes[i] > 0x200000) { + sc->sc_hdr[i].hd_heads = 255; + sc->sc_hdr[i].hd_secs = 63; + } else { + sc->sc_hdr[i].hd_heads = 64; + sc->sc_hdr[i].hd_secs = 32; + } + } +} + +int +ami_attach(sc) + struct ami_softc *sc; +{ + struct ami_ccb *ccb; + struct ami_iocmd *cmd; + struct ami_sgent *sg; + bus_dmamap_t idatamap; + bus_dma_segment_t idataseg[1]; + const char *p; + void *idata; + int error; + + if (!(idata = ami_allocmem(sc->dmat, &idatamap, idataseg, + NBPG, 1, "init data"))) { + ami_freemem(sc->dmat, &idatamap, idataseg, + NBPG, 1, "init data"); + return 1; + } + + sc->sc_cmds = ami_allocmem(sc->dmat, &sc->sc_cmdmap, sc->sc_cmdseg, + sizeof(struct ami_iocmd), AMI_MAXCMDS + 1, "command"); + if (!sc->sc_cmds) { + ami_dispose(sc); + ami_freemem(sc->dmat, &idatamap, + idataseg, NBPG, 1, "init data"); + return 1; + } + sc->sc_sgents = ami_allocmem(sc->dmat, &sc->sc_sgmap, sc->sc_sgseg, + sizeof(struct ami_sgent) * AMI_SGEPERCMD, AMI_MAXCMDS, "sglist"); + if (!sc->sc_sgents) { + ami_dispose(sc); + ami_freemem(sc->dmat, &idatamap, + idataseg, NBPG, 1, "init data"); + return 1; + } + + TAILQ_INIT(&sc->sc_ccbq); + TAILQ_INIT(&sc->sc_ccb2q); + TAILQ_INIT(&sc->sc_ccbdone); + TAILQ_INIT(&sc->sc_free_ccb); + + /* 0th command is a mailbox */ + for (ccb = &sc->sc_ccbs[AMI_MAXCMDS-1], + cmd = sc->sc_cmds + sizeof(*cmd) * AMI_MAXCMDS, + sg = sc->sc_sgents + sizeof(*sg) * AMI_MAXCMDS * AMI_SGEPERCMD; + cmd >= (struct ami_iocmd *)sc->sc_cmds; + cmd--, ccb--, sg -= AMI_SGEPERCMD) { + + cmd->acc_id = cmd - (struct ami_iocmd *)sc->sc_cmds; + if (cmd->acc_id) { + error = bus_dmamap_create(sc->dmat, + AMI_MAXFER, AMI_MAXOFFSETS, AMI_MAXFER, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &ccb->ccb_dmamap); + if (error) { + printf(": cannot create ccb dmamap (%d)\n", + error); + ami_dispose(sc); + ami_freemem(sc->dmat, &idatamap, + idataseg, NBPG, 1, "init data"); + return (1); + } + ccb->ccb_sc = sc; + ccb->ccb_cmd = cmd; + ccb->ccb_state = AMI_CCB_FREE; + ccb->ccb_cmdpa = sc->sc_cmdseg[0].ds_addr + + cmd->acc_id * sizeof(*cmd); + ccb->ccb_sglist = sg; + ccb->ccb_sglistpa = sc->sc_sgseg[0].ds_addr + + cmd->acc_id * sizeof(*sg) * AMI_SGEPERCMD; + TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_link); + } else { + sc->sc_mbox = cmd; + sc->sc_mbox_pa = sc->sc_cmdseg[0].ds_addr; + } + } + + (sc->sc_init)(sc); + { + struct ami_inquiry *inq = idata; + struct ami_fc_einquiry *einq = idata; + paddr_t pa = idataseg[0].ds_addr; + + ccb = ami_get_ccb(sc); + cmd = ccb->ccb_cmd; + + /* try FC inquiry first */ + cmd->acc_cmd = AMI_FCOP; + cmd->acc_io.aio_channel = AMI_FC_EINQ3; + cmd->acc_io.aio_param = AMI_FC_EINQ3_SOLICITED_FULL; + cmd->acc_io.aio_data = pa; + if (ami_cmd(ccb, 0, 1) == 0) { + struct ami_fc_prodinfo *pi = idata; + + sc->sc_nunits = einq->ain_nlogdrv; + ami_copyhds(sc, einq->ain_ldsize, einq->ain_ldprop, + einq->ain_ldstat); + + ccb = ami_get_ccb(sc); + cmd = ccb->ccb_cmd; + + cmd->acc_cmd = AMI_FCOP; + cmd->acc_io.aio_channel = AMI_FC_PRODINF; + cmd->acc_io.aio_param = 0; + cmd->acc_io.aio_data = pa; + if (ami_cmd(ccb, 0, 1) == 0) { + sc->sc_maxunits = AMI_BIG_MAX_LDRIVES; + + bcopy (pi->api_fwver, sc->sc_fwver, 16); + sc->sc_fwver[16] = '\0'; + bcopy (pi->api_biosver, sc->sc_biosver, 16); + sc->sc_biosver[16] = '\0'; + sc->sc_channels = pi->api_channels; + sc->sc_targets = pi->api_fcloops; + sc->sc_memory = pi->api_ramsize; + sc->sc_maxcmds = pi->api_maxcmd; + p = "FC loop"; + } + } + + if (sc->sc_maxunits == 0) { + ccb = ami_get_ccb(sc); + cmd = ccb->ccb_cmd; + + cmd->acc_cmd = AMI_EINQUIRY; + cmd->acc_io.aio_channel = 0; + cmd->acc_io.aio_param = 0; + cmd->acc_io.aio_data = pa; + if (ami_cmd(ccb, 0, 1) != 0) { + ccb = ami_get_ccb(sc); + cmd = ccb->ccb_cmd; + + cmd->acc_cmd = AMI_INQUIRY; + cmd->acc_io.aio_channel = 0; + cmd->acc_io.aio_param = 0; + cmd->acc_io.aio_data = kvtop((caddr_t)&inq); + if (ami_cmd(ccb, 0, 1) != 0) { + printf(": cannot do inquiry\n"); + ami_dispose(sc); + ami_freemem(sc->dmat, &idatamap, + idataseg, NBPG, 1, "init data"); + return (1); + } + } + + sc->sc_maxunits = AMI_MAX_LDRIVES; + sc->sc_nunits = inq->ain_nlogdrv; + ami_copyhds(sc, inq->ain_ldsize, inq->ain_ldprop, + inq->ain_ldstat); + + bcopy (inq->ain_fwver, sc->sc_fwver, 4); + sc->sc_fwver[4] = '\0'; + bcopy (inq->ain_biosver, sc->sc_biosver, 4); + sc->sc_biosver[4] = '\0'; + sc->sc_channels = inq->ain_channels; + sc->sc_targets = inq->ain_targets; + sc->sc_memory = inq->ain_ramsize; + sc->sc_maxcmds = inq->ain_maxcmd; + p = "target"; + } + + if (sc->sc_maxcmds > AMI_MAXCMDS) + sc->sc_maxcmds = AMI_MAXCMDS; + } + ami_freemem(sc->dmat, &idatamap, idataseg, NBPG, 1, "init data"); + + /* TODO: fetch & print cache strategy */ + /* TODO: fetch & print scsi and raid info */ + printf(": FW %s, BIOS v%s, %dMB RAM\n" + "%s: %d channels, %d %ss, %d logical drives\n", + sc->sc_fwver, sc->sc_biosver, sc->sc_memory, + sc->sc_dev.dv_xname, + sc->sc_channels, sc->sc_targets, p, sc->sc_nunits); + + timeout_set(&sc->sc_requeue_tmo, ami_requeue, sc); + + sc->sc_link.device = &ami_dev; + sc->sc_link.openings = sc->sc_maxcmds; + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter = &ami_switch; + sc->sc_link.adapter_target = sc->sc_maxunits; + sc->sc_link.adapter_buswidth = sc->sc_maxunits; + sc->sc_link_raw = sc->sc_link; + + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); + +#if notyet + sc->sc_link_raw.device = &ami_raw_dev; + sc->sc_link_raw.adapter = &ami_raw_switch; + sc->sc_link_raw.adapter_target = AMI_MAX_PDRIVES; + sc->sc_link_raw.adapter_buswidth = AMI_MAX_PDRIVES; + + config_found(&sc->sc_dev, &sc->sc_link_raw, scsiprint); +#endif + + return 0; +} + +int +ami_quartz_init(sc) + struct ami_softc *sc; +{ + return 0; +} + +int +ami_quartz_exec(sc) + struct ami_softc *sc; +{ + u_int32_t qidb; + + qidb = bus_space_read_4(sc->iot, sc->ioh, AMI_QIDB); + if (letoh32(qidb) & AMI_QIDB_EXEC) + return EBUSY; + + qidb = sc->sc_mbox_pa | AMI_QIDB_EXEC; + bus_space_write_4(sc->iot, sc->ioh, AMI_QIDB, htole32(qidb)); + return 0; +} + +int +ami_quartz_done(sc, mbox) + struct ami_softc *sc; + struct ami_iocmd *mbox; +{ + u_int32_t qodb; + + qodb = bus_space_read_4(sc->iot, sc->ioh, AMI_QODB); + if (letoh32(qodb) == AMI_QODB_READY) { + + *mbox = *sc->sc_mbox; + + /* ack interrupt */ + bus_space_write_4(sc->iot, sc->ioh, AMI_QODB, AMI_QODB_READY); + + qodb = sc->sc_mbox_pa | AMI_QIDB_ACK; + bus_space_write_4(sc->iot, sc->ioh, AMI_QODB, htole32(qodb)); + return 1; + } + + return 0; +} + +int +ami_schwartz_init(sc) + struct ami_softc *sc; +{ + u_int32_t a = (u_int32_t)sc->sc_mbox_pa; + + bus_space_write_4(sc->iot, sc->ioh, AMI_SMBADDR, a); + /* XXX 40bit address ??? */ + bus_space_write_1(sc->iot, sc->ioh, AMI_SMBENA, 0); + + bus_space_write_1(sc->iot, sc->ioh, AMI_SCMD, AMI_SCMD_ACK); + bus_space_write_1(sc->iot, sc->ioh, AMI_SIEM, AMI_SEIM_ENA | + bus_space_read_1(sc->iot, sc->ioh, AMI_SIEM)); + + return 0; +} + +int +ami_schwartz_exec(sc) + struct ami_softc *sc; +{ + if (bus_space_read_1(sc->iot, sc->ioh, AMI_SMBSTAT) & AMI_SMBST_BUSY) + return EAGAIN; + bus_space_write_1(sc->iot, sc->ioh, AMI_SCMD, AMI_SCMD_EXEC); + return 0; +} + +int +ami_schwartz_done(sc, mbox) + struct ami_softc *sc; + struct ami_iocmd *mbox; +{ + u_int8_t stat; + + stat = bus_space_read_1(sc->iot, sc->ioh, AMI_ISTAT); + if (stat & AMI_ISTAT_PEND) { + bus_space_write_1(sc->iot, sc->ioh, AMI_ISTAT, stat); + + *mbox = *sc->sc_mbox; + + bus_space_write_1(sc->iot, sc->ioh, AMI_SCMD, AMI_SCMD_ACK); + + return 1; + } + + return 0; +} + +int +ami_cmd(ccb, flags, wait) + struct ami_ccb *ccb; + int flags, wait; +{ + struct ami_softc *sc = ccb->ccb_sc; + bus_dmamap_t dmap = ccb->ccb_dmamap; + int error, s, i; + + if (ccb->ccb_data) { + struct ami_iocmd *cmd = ccb->ccb_cmd; + bus_dma_segment_t *sgd; + + error = bus_dmamap_load(sc->dmat, dmap, ccb->ccb_data, + ccb->ccb_len, NULL, flags); + if (error) { + if (error == EFBIG) + printf("more than %d dma segs\n", AMI_MAXOFFSETS); + else + printf("error %d loading dma map\n", error); + + ami_put_ccb(ccb); + return error; + } + + sgd = dmap->dm_segs; + AMI_DPRINTF(AMI_D_DMA, ("data=%p/%u<0x%lx/%u", + ccb->ccb_data, ccb->ccb_len, + sgd->ds_addr, sgd->ds_len)); + + if(dmap->dm_nsegs > 1) { + struct ami_sgent *sgl = ccb->ccb_sglist; + + cmd->acc_mbox.amb_nsge = htole32(dmap->dm_nsegs); + cmd->acc_mbox.amb_data = htole32(ccb->ccb_sglistpa); + + for (i = 0; i < dmap->dm_nsegs; i++, sgd++) { + sgl[i].asg_addr = htole32(sgd->ds_addr); + sgl[i].asg_len = htole32(sgd->ds_len); + if (i) + AMI_DPRINTF(AMI_D_DMA, (",0x%lx/%u", + sgd->ds_addr, sgd->ds_len)); + } + } else { + cmd->acc_mbox.amb_nsge = htole32(0); + cmd->acc_mbox.amb_data = htole32(sgd->ds_addr); + } + AMI_DPRINTF(AMI_D_DMA, ("> ")); + + bus_dmamap_sync(sc->dmat, dmap, BUS_DMASYNC_PREWRITE); + } + bus_dmamap_sync(sc->dmat, sc->sc_cmdmap, BUS_DMASYNC_PREWRITE); + + /* XXX somehow interrupts have started to happen in autoconf() */ + if (wait) + s = splbio(); + + if ((error = ami_start(ccb, wait))) { + if (ccb->ccb_data) + bus_dmamap_unload(sc->dmat, dmap); + ami_put_ccb(ccb); + } else if (wait) + if ((error = ami_complete(ccb))) + ami_put_ccb(ccb); + + if (wait) + splx(s); + + return error; +} + +int +ami_start(ccb, wait) + struct ami_ccb *ccb; + int wait; +{ + struct ami_softc *sc = ccb->ccb_sc; + struct ami_iocmd *cmd = ccb->ccb_cmd; + volatile struct ami_iocmd *mbox = sc->sc_mbox; + int s, i; + + AMI_DPRINTF(AMI_D_CMD, ("start(%d) ", cmd->acc_id)); + + if (ccb->ccb_state != AMI_CCB_READY) { + printf("%s: ccb %d not ready <%d>\n", + sc->sc_dev.dv_xname, cmd->acc_id, ccb->ccb_state); + return EINVAL; + } + + if (mbox->acc_busy && !wait) { + + ccb->ccb_state = AMI_CCB_PREQUEUED; + s = splclock(); + TAILQ_INSERT_TAIL(&sc->sc_ccb2q, ccb, ccb_link); + if (!sc->sc_timeout) { + sc->sc_timeout++; + splx(s); + timeout_add(&sc->sc_requeue_tmo, 0); + } else + splx(s); + return 0; + } + + for (i = 10000; i-- && mbox->acc_busy; DELAY(100)); + + if (mbox->acc_busy) { + AMI_DPRINTF(AMI_D_CMD, ("mbox_busy ")); + return 1; + } + + AMI_DPRINTF(AMI_D_CMD, ("exec ")); + + ccb->ccb_state = AMI_CCB_QUEUED; + TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, ccb_link); + + cmd->acc_busy = 1; + cmd->acc_poll = 0; + cmd->acc_ack = 0; + *mbox = *cmd; + + if ((i = (sc->sc_exec)(sc))) + cmd->acc_busy = 0; + + return i; +} + +void +ami_requeue(v) + void *v; +{ + struct ami_softc *sc = v; + struct ami_ccb *ccb; + struct ami_iocmd *cmd; + volatile struct ami_iocmd *mbox = sc->sc_mbox; + int s; + + if (mbox->acc_busy) { + timeout_add(&sc->sc_requeue_tmo, 1); + return; + } + + s = splclock(); + ccb = TAILQ_FIRST(&sc->sc_ccb2q); + TAILQ_REMOVE(&sc->sc_ccb2q, ccb, ccb_link); + splx(s); + + cmd = ccb->ccb_cmd; + AMI_DPRINTF(AMI_D_CMD, ("requeue(%d) ", cmd->acc_id)); + ccb->ccb_state = AMI_CCB_READY; + + if (!ami_start(ccb, 0)) + AMI_DPRINTF(AMI_D_CMD, ("requeue(%d) again\n", cmd->acc_id)); + + if (!TAILQ_EMPTY(&sc->sc_ccb2q)) + timeout_add(&sc->sc_requeue_tmo, 1); + else + sc->sc_timeout = 0; +} + +int +ami_complete(ccb) + struct ami_ccb *ccb; +{ + struct ami_softc *sc = ccb->ccb_sc; + struct ami_iocmd mbox; + int i, j, rv, status; + + for (rv = 1, status = 0, i = 10000; !status && rv && i--; DELAY(100)) + if ((sc->sc_done)(sc, &mbox)) { + AMI_DPRINTF(AMI_D_CMD, ("got-%d ", mbox.acc_nstat)); + status = mbox.acc_status; + for (j = 0; j < mbox.acc_nstat; j++ ) { + int ready = mbox.acc_cmplidl[j]; + + AMI_DPRINTF(AMI_D_CMD, ("ready=%x ", ready)); + + /* XXX could it happen that scsi_done allocs it? */ + if (!ami_done(sc, ready) && + ccb->ccb_state == AMI_CCB_FREE) + rv = 0; + } + } + + if (status) { + AMI_DPRINTF(AMI_D_CMD, ("aborted\n")); + } else if (!rv) { + AMI_DPRINTF(AMI_D_CMD, ("complete\n")); + } else if (i < 0) { + AMI_DPRINTF(AMI_D_CMD, ("timeout\n")); + } else + AMI_DPRINTF(AMI_D_CMD, ("screwed\n")); + + return rv? rv : status; +} + +int +ami_done(sc, idx) + struct ami_softc *sc; + int idx; +{ + struct scsi_xfer *xs; + struct ami_ccb *ccb = &sc->sc_ccbs[idx - 1]; + + AMI_DPRINTF(AMI_D_CMD, ("done(%d) ", idx)); + + if (ccb->ccb_state != AMI_CCB_QUEUED) { + printf("%s: unqueued ccb %d ready, state = %d\n", + sc->sc_dev.dv_xname, idx, ccb->ccb_state); + return 1; + } + + TAILQ_REMOVE(&sc->sc_ccbq, ccb, ccb_link); + + if ((xs = ccb->ccb_xs)) { + if (xs->cmd->opcode != PREVENT_ALLOW && + xs->cmd->opcode != SYNCHRONIZE_CACHE) { + bus_dmamap_sync(sc->dmat, ccb->ccb_dmamap, + (xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->dmat, ccb->ccb_dmamap); + } + } else { + struct ami_iocmd *cmd = ccb->ccb_cmd; + + switch (cmd->acc_cmd) { + case AMI_INQUIRY: + case AMI_EINQUIRY: + case AMI_EINQUIRY3: + case AMI_READ: + bus_dmamap_sync(sc->dmat, ccb->ccb_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->dmat, ccb->ccb_dmamap); + break; + case AMI_WRITE: + bus_dmamap_sync(sc->dmat, ccb->ccb_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->dmat, ccb->ccb_dmamap); + break; + default: + /* no data */ + } + } + + ami_put_ccb(ccb); + + if (xs) { + xs->resid = 0; + xs->flags |= ITSDONE; + scsi_done(xs); + } + + return 0; +} + +void +amiminphys(bp) + struct buf *bp; +{ + if (bp->b_bcount > AMI_MAXFER) + bp->b_bcount = AMI_MAXFER; + minphys(bp); +} + +void +ami_copy_internal_data(xs, v, size) + struct scsi_xfer *xs; + void *v; + size_t size; +{ + size_t copy_cnt; + + AMI_DPRINTF(AMI_D_MISC, ("ami_copy_internal_data ")); + + if (!xs->datalen) + printf("uio move not yet supported\n"); + else { + copy_cnt = MIN(size, xs->datalen); + bcopy(v, xs->data, copy_cnt); + } +} + +int +ami_scsi_raw_cmd(xs) + struct scsi_xfer *xs; +{ + AMI_DPRINTF(AMI_D_CMD, ("ami_scsi_raw_cmd ")); + + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); +} + +int +ami_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *link = xs->sc_link; + struct ami_softc *sc = link->adapter_softc; + struct ami_ccb *ccb; + struct ami_iocmd *cmd; + struct scsi_inquiry_data inq; + struct scsi_sense_data sd; + struct { + struct scsi_mode_header hd; + struct scsi_blk_desc bd; + union scsi_disk_pages dp; + } mpd; + struct scsi_read_cap_data rcd; + u_int8_t target = link->target; + u_int32_t blockno, blockcnt; + struct scsi_rw *rw; + struct scsi_rw_big *rwb; + int error, flags; + ami_lock_t lock; + + if (target >= sc->sc_nunits || !sc->sc_hdr[target].hd_present || + link->lun != 0) { + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + AMI_DPRINTF(AMI_D_CMD, ("ami_scsi_cmd ")); + + xs->error = XS_NOERROR; + + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case START_STOP: +#if 0 + case VERIFY: +#endif + AMI_DPRINTF(AMI_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode, + target)); + break; + + case REQUEST_SENSE: + AMI_DPRINTF(AMI_D_CMD, ("REQUEST SENSE tgt %d ", target)); + bzero(&sd, sizeof sd); + sd.error_code = 0x70; + sd.segment = 0; + sd.flags = SKEY_NO_SENSE; + *(u_int32_t*)sd.info = htole32(0); + sd.extra_len = 0; + ami_copy_internal_data(xs, &sd, sizeof sd); + break; + + case INQUIRY: + AMI_DPRINTF(AMI_D_CMD, ("INQUIRY tgt %d ", target)); + bzero(&inq, sizeof inq); + inq.device = T_DIRECT; + inq.dev_qual2 = 0; + inq.version = 2; + inq.response_format = 2; + inq.additional_length = 32; + strcpy(inq.vendor, "3WARE "); + sprintf(inq.product, "Host drive #%02d", target); + strcpy(inq.revision, " "); + ami_copy_internal_data(xs, &inq, sizeof inq); + break; + + case MODE_SENSE: + AMI_DPRINTF(AMI_D_CMD, ("MODE SENSE tgt %d ", target)); + + bzero(&mpd, sizeof mpd); + switch (((struct scsi_mode_sense *)xs->cmd)->page) { + case 4: + /* scsi_disk.h says this should be 0x16 */ + mpd.dp.rigid_geometry.pg_length = 0x16; + mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd + + mpd.dp.rigid_geometry.pg_length; + mpd.hd.blk_desc_len = sizeof mpd.bd; + + mpd.hd.dev_spec = 0; /* writeprotect ? XXX */ + _lto3b(AMI_SECTOR_SIZE, mpd.bd.blklen); + mpd.dp.rigid_geometry.pg_code = 4; + _lto3b(sc->sc_hdr[target].hd_size / + sc->sc_hdr[target].hd_heads / + sc->sc_hdr[target].hd_secs, + mpd.dp.rigid_geometry.ncyl); + mpd.dp.rigid_geometry.nheads = + sc->sc_hdr[target].hd_heads; + ami_copy_internal_data(xs, (u_int8_t *)&mpd, + sizeof mpd); + break; + + default: + printf("%s: mode sense page %d not simulated\n", + sc->sc_dev.dv_xname, + ((struct scsi_mode_sense *)xs->cmd)->page); + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + break; + + case READ_CAPACITY: + AMI_DPRINTF(AMI_D_CMD, ("READ CAPACITY tgt %d ", target)); + bzero(&rcd, sizeof rcd); + _lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr); + _lto4b(AMI_SECTOR_SIZE, rcd.length); + ami_copy_internal_data(xs, &rcd, sizeof rcd); + break; + + case PREVENT_ALLOW: + AMI_DPRINTF(AMI_D_CMD, ("PREVENT/ALLOW ")); + return (COMPLETE); + + case READ_COMMAND: + case READ_BIG: + case WRITE_COMMAND: + case WRITE_BIG: + lock = AMI_LOCK_AMI(sc); + + flags = 0; + if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { + /* A read or write operation. */ + if (xs->cmdlen == 6) { + rw = (struct scsi_rw *)xs->cmd; + blockno = _3btol(rw->addr) & + (SRW_TOPADDR << 16 | 0xffff); + blockcnt = rw->length ? rw->length : 0x100; + } else { + rwb = (struct scsi_rw_big *)xs->cmd; + blockno = _4btol(rwb->addr); + blockcnt = _2btol(rwb->length); + /* TODO: reflect DPO & FUA flags */ + if (xs->cmd->opcode == WRITE_BIG && + rwb->byte2 & 0x18) + flags = 0; + } + if (blockno >= sc->sc_hdr[target].hd_size || + blockno + blockcnt > sc->sc_hdr[target].hd_size) { + AMI_UNLOCK_AMI(sc, lock); + printf("%s: out of bounds %u-%u >= %u\n", + sc->sc_dev.dv_xname, blockno, blockcnt, + sc->sc_hdr[target].hd_size); + scsi_done(xs); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + } + + if ((ccb = ami_get_ccb(sc)) == NULL) { + scsi_done(xs); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + ccb->ccb_xs = xs; + ccb->ccb_data = xs->data; + ccb->ccb_len = xs->datalen; + cmd = ccb->ccb_cmd; + cmd->acc_mbox.amb_nsect = blockcnt; + cmd->acc_mbox.amb_lba = blockno; + cmd->acc_mbox.amb_ldn = target; + cmd->acc_mbox.amb_data = 0; + + switch (xs->cmd->opcode) { + case SYNCHRONIZE_CACHE: + cmd->acc_cmd = AMI_FLUSH; + /* XXX do other fields matter ? */ + break; + case READ_COMMAND: case READ_BIG: + cmd->acc_cmd = AMI_READ; + break; + case WRITE_COMMAND: case WRITE_BIG: + cmd->acc_cmd = AMI_WRITE; + break; +#ifdef DIAGNOSTIC + default: + printf("%s: but how?\n", sc->sc_dev.dv_xname); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); +#endif + } + + if ((error = ami_cmd(ccb, ((xs->flags & SCSI_NOSLEEP)? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK), xs->flags & SCSI_POLL))) { + + AMI_UNLOCK_AMI(sc, lock); + AMI_DPRINTF(AMI_D_CMD, ("failed %p ", xs)); + if (xs->flags & SCSI_POLL) { + xs->error = XS_TIMEOUT; + return (TRY_AGAIN_LATER); + } else { + scsi_done(xs); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + } + + AMI_UNLOCK_AMI(sc, lock); + + if (xs->flags & SCSI_POLL) { + scsi_done(xs); + return (COMPLETE); + } + return (SUCCESSFULLY_QUEUED); + + default: + AMI_DPRINTF(AMI_D_CMD, ("unknown opc %d ", xs->cmd->opcode)); + xs->error = XS_DRIVER_STUFFUP; + } + + return (COMPLETE); +} + +int +ami_intr(v) + void *v; +{ + struct ami_softc *sc = v; + struct ami_iocmd mbox; + int i, rv; + + while ((sc->sc_done)(sc, &mbox)) { + AMI_DPRINTF(AMI_D_CMD, ("got-%d ", mbox.acc_nstat)); + for (i = 0; i < mbox.acc_nstat; i++ ) { + register int ready = mbox.acc_cmplidl[i]; + + AMI_DPRINTF(AMI_D_CMD, ("ready=%x ", ready)); + + if (!ami_done(sc, ready)) + rv++; + } + } + + return rv; +} diff --git a/sys/dev/ic/amireg.h b/sys/dev/ic/amireg.h new file mode 100644 index 00000000000..be16ecf510e --- /dev/null +++ b/sys/dev/ic/amireg.h @@ -0,0 +1,540 @@ +/* $OpenBSD: amireg.h,v 1.1 2001/03/09 11:14:21 mickey Exp $ */ + +/* + * Copyright (c) 2000 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Michael Shalayeff. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define AMI_MAX_PDRIVES (75) +#define AMI_MAX_LDRIVES 8 +#define AMI_MAX_SPANDEPTH 4 +#define AMI_MAX_DEVDEPTH 8 + +#define AMI_BIG_MAX_PDRIVES (256) +#define AMI_BIG_MAX_LDRIVES 40 +#define AMI_BIG_MAX_SPANDEPTH 8 +#define AMI_BIG_MAX_DEVDEPTH 32 + +#define AMI_MAXCMDS 120 /* theoretical limit is 255 */ +#define AMI_SECTOR_SIZE 512 +#define AMI_MAXOFFSETS 26 +#define AMI_SGEPERCMD 32 /* to prevent page boundary crossing */ + +#define AMI_MAXFER (AMI_MAXOFFSETS * PAGE_SIZE) + +#define AMI_QIDB 0x20 +#define AMI_QIDB_EXEC 0x01 +#define AMI_QIDB_ACK 0x02 +#define AMI_QODB 0x2c +#define AMI_QODB_READY 0x10001234 + +#define AMI_SCMD 0x10 +#define AMI_SCMD_EXEC 0x10 +#define AMI_SCMD_ACK 0x08 +#define AMI_SMBSTAT 0x10 +#define AMI_SMBST_BUSY 0x10 +#define AMI_SIEM 0x11 +#define AMI_SEIM_ENA 0xc0 +#define AMI_SMBADDR 0x14 +#define AMI_SMBENA 0x18 +#define AMI_ISTAT 0x1a +#define AMI_ISTAT_PEND 0x40 + +/* commands */ +#define AMI_READ 0x01 +#define AMI_WRITE 0x02 +#define AMI_SRB_DCDB 0x03 +#define AMI_EINQUIRY 0x04 /* extended inquiry */ +#define AMI_INQUIRY 0x05 /* inquiry */ +#define AMI_CHSTATE 0x06 /* pad[0] -- state */ +#define AMI_RCONFIG 0x07 /* read configuration up to 4 spans */ +#define AMI_REBUILDPD 0x08 /* rebuild physical drive */ +#define AMI_STATE_ON 3 +#define AMI_STATE_FAIL 4 +#define AMI_STATE_SPARE 6 +#define AMI_CHECK 0x09 /* check consistency */ +#define AMI_FLUSH 0x0a +#define AMI_ILDRIVE 0x0b /* init logical drive */ +#define AMI_EINQUIRY3 0x0c +#define AMI_DCHDR 0x14 /* ge/set dedicated channel/drives */ +#define AMI_GRBLDPROGR 0x18 /* get rebuild progress */ +#define AMI_GCHECKPROGR 0x19 /* get check consistency progress */ +#define AMI_GILDRPROGR 0x1b /* get init logical drive progress */ +#define AMI_WRCONFIG 0x20 /* write configuration up to 4 spans */ +#define AMI_RWRCONFIG 0x21 /* raid write config */ +#define AMI_RRDCONFIG 0x22 /* raid read config */ +#define AMI_GRBLDRATE 0x23 /* get rebuild rate */ +#define AMI_SRBLDRATE 0x24 /* set rebuild rate */ +#define AMI_UPLDCFGUT 0x25 /* upload config utility */ +#define AMI_UPLDRVPROP 0x26 /* update logical drive property */ +#define AMI_ABRTREBLD 0x28 /* abort rebuild */ +#define AMI_ABRTCHECK 0x29 /* abort check consistency */ +#define AMI_ABRTILDRV 0x2b /* abort init logical drive */ +#define AMI_WRBLOCK 0x2c /* flash write block */ +#define AMI_PRGFLASH 0x2d /* flash program */ +#define AMI_SFLUSHINTV 0x2e /* channel == cache flush interval */ +#define AMI_PCHIPSETVAL 0x2f /* program chipset values */ +#define AMI_CS_NEPTUNE 0x61 +#define AMI_CS_OTHER 0xe1 +#define AMI_CS_TRITON 0xe2 +#define AMI_SNEG 0x30 /* scsi sync negotiation get/ena/dis */ +#define AMI_SNEG_GET 1 +#define AMI_SNEG_SET 2 +#define AMI_QTAG 0x31 /* scsi queue tag get/set */ +#define AMI_QTAG_GET 1 +#define AMI_QTAG_SET 2 +#define AMI_GSUPARAM 0x32 /* get spinup parameters */ +#define AMI_SSUPARAM 0x33 /* set spinup parameters */ +#define AMI_GDROAMINFO 0x34 +#define AMI_GMACHID 0x36 /* get machine id */ +#define AMI_BIOSPDATA 0x40 /* get bios private data */ +#define AMI_I2OCFGDLG 0x41 /* I2O config dialog */ +#define AMI_GCACHESTAT 0x50 /* get cache statistics */ +#define AMI_SPEAKER 0x51 /* speaker control */ +#define AMI_SPKR_OFF 0 +#define AMI_SPKR_ON 1 +#define AMI_SPKR_SHUT 2 +#define AMI_SPKR_GVAL 3 +#define AMI_GDUMP 0x52 /* get error condition in text */ +#define AMI_SENSEDUMPA 0x53 /* get SCSI sense dump area */ +#define AMI_STDIAG 0x54 /* start diagnostics -- 2.1 */ +#define AMI_FRAID_PF 0x55 /* get/set flexraid power fail */ +#define AMI_GFRAIDPF 1 +#define AMI_SFRAIDPF 2 +#define AMI_FRAIDVS 0x56 /* get/set flesraid virtual sizing */ +#define AMI_GFRAIDVS 1 +#define AMI_SFRAIDVS 2 +#define AMI_BBMANAGE 0x57 /* bad block manage */ +#define AMI_RECONSTRUCT 0x60 /* begin reconstruction */ +#define AMI_GRECONSTRUCT 0x61 /* get reconstruction progress */ +#define AMI_BIOSSTAT 0x62 /* enable/disable bios */ +#define AMI_RDCFGDSK 0x63 /* read configuration from disk */ +#define AMI_AREBUILD 0x64 /* get/set autorebuild/battery charge */ +#define AMI_GUCAP 1 /* get ultra capabilities */ +#define AMI_SUCAP 2 /* set ultra capability */ +#define AMI_GARBLD 3 +#define AMI_SARBLD 4 +#define AMI_GFCC 5 /* get fast charge counter */ +#define AMI_SFCC 6 /* set fast charge counter */ +#define AMI_GCUCAP 7 /* get channel ultra capabilities */ +#define AMI_SCUCAP 8 /* set channel ultra capabilities */ +#define AMI_SFD 0x66 /* set factory defaults */ +#define AMI_RDCONFIG8 0x67 /* read configuration up to 8 spans */ +#define AMI_WRCONFIG8 0x68 /* write config up to 8 spans */ +#define AMI_ESENSEDUMPA 0x69 /* extended scsi dump area */ +#define AMI_RERRC 0x6a /* reset error counter */ +#define AMI_BOOTUP 0x6b /* ena/dis physical drive boot up */ +#define AMI_ENCLOSURE 0x6c /* get/set enclosure type */ +#define AMI_WRCFGD 0x6c /* write config disk -- 2.1 */ +#define AMI_HAPIRRLD 0x6e +#define AMI_LDRVRIGHTS 0x6f +#define AMI_CLUSTERING 0x70 +#define AMI_GCHPROP 0x71 /* get channel properties */ +#define AMI_SCHTERM 0x72 /* set channel termination */ +#define AMI_TERM_DISABLE 0 +#define AMI_TERM_ENABLE 1 +#define AMI_TERM_HIGH 2 +#define AMI_TERM_WIDE 3 +#define AMI_TERM_DFLT 16 +#define AMI_QUIETCH 0x73 /* quiet channel */ +#define AMI_ACTIVATECH 0x74 /* activate channel */ +#define AMI_STARTU 0x75 /* start unit, pad[0] -- sync/async */ +#define AMI_STARTU_SYNC 1 +#define AMI_STARTU_ASYN 2 +#define AMI_STOPU 0x76 /* stop unit */ +#define AMI_GERRC 0x77 /* get error counter */ +#define AMI_GBTDS 0x78 /* get boot time drive status */ +#define AMI_FMTPROG 0x79 +#define AMI_RCAPCMD 0x7a /* read capacity */ +#define AMI_WRCRX 0x7b +#define AMI_RDCRX 0x7c +#define AMI_GINID 0x7d /* get initiator id */ +#define AMI_HAPICMD 0x7e +#define AMI_SINID 0x7f /* set initiator id */ +#define AMI_SMARTMSEL 0x80 +#define AMI_SPSTARTU 0x85 /* special start unit command */ +#define AMI_NVFAILHIST 0x90 +#define AMI_DCMDABRT 0x91 +#define AMI_GDRIVEHIST 0x92 /* get drive history */ +#define AMI_GESENSE 0x93 /* get extended sense data dump */ +#define AMI_ADAPTER 0x95 /* save/restore adapter params */ +#define AMI_ADP_SAVE 0 +#define AMI_ADP_LOAD 1 +#define AMI_RESET 0x96 /* adapter reset */ +#define AMI_PRGCLASS 0x97 /* program class code */ +#define AMI_UPHTML 0x98 /* upload html utility */ +#define AMI_NEWCFG 0x99 +#define AMI_NEWOP 0xa0 +#define AMI_FCOP 0xa1 +#define AMI_FC_PROCEED 0x02 +#define AMI_FC_DELLDRV 0x03 +#define AMI_FC_RDCONF 0x04 +#define AMI_FC_RDFCONF 0x05 +#define AMI_FC_GCONFDSK 0x06 +#define AMI_FC_CHLDNO 0x07 +#define AMI_FC_CMPCTCFG 0x08 +#define AMI_FC_DRVGRP 0x09 +#define AMI_FC_GLOOPINF 0x0a +#define AMI_FC_CHLOOPID 0x0b +#define AMI_FC_GNSCH 0x0c +#define AMI_FC_WRCONF 0x0d +#define AMI_FC_PRODINF 0x0e +#define AMI_FC_EINQ3 0x0f +#define AMI_FC_EINQ3_SOLICITED_NOTIFY 0x01 +#define AMI_FC_EINQ3_SOLICITED_FULL 0x02 +#define AMI_FC_EINQ3_UNSOLICITED 0x03 +#define AMI_MISC 0xa4 +#define AMI_CHFUNC 0xa9 +#define AMI_MANAGE 0xb0 /* manage functions */ +#define AMI_MGR_LUN 0x00 +#define AMI_MGR_THERM 0x01 +#define AMI_MGR_EEPROM 0x02 +#define AMI_MGR_LDNAMES 0x03 +#define AMI_MGR_FCWWN 0x04 +#define AMI_MGR_CFGACC 0x05 +#define AMI_HSPDIAG 0xb1 +#define AMI_GESENSEINFO 0xb2 /* get extended sense info */ +#define AMI_SYSFLUSH 0xfe /* flush system */ + +/* command structures */ +#pragma pack(1) +struct ami_iocmd { + u_int8_t acc_cmd; + u_int8_t acc_id; + union { +#define acc_mbox _._ami_mbox + struct { + u_int16_t amb_nsect; + u_int32_t amb_lba; + u_int32_t amb_data; + u_int8_t amb_ldn; /* logical drive no */ + u_int8_t amb_nsge; + u_int8_t amb_reserved; + } _ami_mbox; + +#define acc_io _._ami_io + struct { + u_int8_t aio_channel; + u_int8_t aio_param; + u_int8_t aio_pad[4]; + u_int32_t aio_data; + u_int8_t aio_pad1[3]; + } _ami_io; + +#define acc_passthru _._ami_passthru + struct { + u_int16_t apt_dummy0; + u_int32_t apt_dummy1; + u_int32_t apt_data; + u_int8_t apt_dummy2; + u_int8_t apt_dummy3; + u_int8_t apt_reserved; + } _ami_passru; + +#define acc_ldrv _._ami_ldrv + struct { + u_int16_t ald_dummy0; + u_int32_t ald_dummy1; + u_int32_t ald_data; + u_int8_t ald_ldrv; + u_int8_t ald_dummy2; + u_int8_t ald_reserved; + } _ami_ldrv; + } _; + u_int8_t acc_busy; + u_int8_t acc_nstat; + u_int8_t acc_status; +#define AMI_MAXSTATACK 0x2e + u_int8_t acc_cmplidl[AMI_MAXSTATACK]; + u_int8_t acc_poll; + u_int8_t acc_ack; +}; + +struct ami_sgent { + u_int32_t asg_addr; + u_int32_t asg_len; +}; + +struct ami_iocmd64 { + u_int8_t acc_cmd; + u_int8_t acc_id; + union { + struct { + u_int16_t amb_nsect; + u_int32_t amb_lba; + u_int32_t amb_reserved1; + u_int8_t amb_ldn; /* logical drive no */ + u_int8_t amb_nsge; /* high bit == 1 */ + u_int8_t amb_reserved; + } _ami_mbox; + + struct { + u_int8_t aio_channel; + u_int8_t aio_param; + u_int8_t aio_pad[4]; + u_int32_t aio_data; + u_int8_t aio_pad1[3]; + } _ami_io; + + struct { + u_int16_t apt_dummy0; + u_int32_t apt_dummy1; + u_int32_t apt_data; + u_int8_t apt_dummy2; + u_int8_t apt_dummy3; + u_int8_t apt_reserved; + } _ami_passru; + + struct { + u_int16_t ald_dummy0; + u_int32_t ald_dummy1; + u_int32_t ald_data; + u_int8_t ald_ldrv; + u_int8_t ald_dummy2; + u_int8_t ald_reserved; + } _ami_ldrv; + } _; + u_int8_t acc_busy; + u_int32_t acc_data_l; + u_int32_t acc_data_h; + u_int32_t acc_reserved; + u_int8_t acc_nstat; + u_int8_t acc_status; + u_int8_t acc_cmplidl[AMI_MAXSTATACK]; + u_int8_t acc_poll; + u_int8_t acc_ack; +} __attribute__((packed)); + +struct ami_sgent64 { + u_int32_t asg_addr_l; + u_int32_t asg_addr_h; + u_int32_t asg_len; +}; + +struct ami_passthrough { + u_int8_t apt_param; +#define AMI_PTPARAM(t,a,l) (((l) << 7) | (((a) & 1) << 3) | ((t) & 3)) +#define AMI_TIMEOUT_6 0 +#define AMI_TIMEOUT_60 1 +#define AMI_TIMEOUT_10m 2 +#define AMI_TIMEOUT_3h 3 + u_int8_t apt_ldn; + u_int8_t apt_channel; + u_int8_t apt_target; + u_int8_t apt_qtag; + u_int8_t apt_qact; +#define AMI_MAX_CDB 10 + u_int8_t apt_cdb[AMI_MAX_CDB]; + u_int8_t apt_ncdb; + u_int8_t apt_nsense; +#define AMI_MAX_SENSE 32 + u_int8_t apt_sense[AMI_MAX_SENSE]; + u_int8_t apt_nsg; + u_int8_t apt_scsistat; + u_int32_t apt_data; + u_int32_t apt_datalen; +}; + +struct ami_inquiry { + u_int8_t ain_maxcmd; + u_int8_t ain_rbldrate; /* rebuild rate %% */ + u_int8_t ain_targets; /* max targets per channel */ + u_int8_t ain_channels; + u_int8_t ain_fwver[4]; + u_int16_t ain_flashage; + u_int8_t ain_chipset; /* parity generation policy */ + u_int8_t ain_ramsize; + u_int8_t ain_flushintv; + u_int8_t ain_biosver[4]; + u_int8_t ain_brdtype; + u_int8_t ain_scsisensealert; + u_int8_t ain_wrcfgcnt; /* write config count */ + u_int8_t ain_drvinscnt; /* drive insertion count */ + u_int8_t ain_insdrv; /* inserted drive */ + u_int8_t ain_battery; /* battery status */ + u_int8_t ain_reserved; + + u_int8_t ain_nlogdrv; + u_int8_t ain_reserved1[3]; + u_int32_t ain_ldsize[AMI_MAX_LDRIVES]; + u_int8_t ain_ldprop[AMI_MAX_LDRIVES]; + u_int8_t ain_ldstat[AMI_MAX_LDRIVES]; + + u_int8_t ain_pdstat[AMI_MAX_PDRIVES]; + u_int8_t ain_predictivefailure; + + u_int8_t ain_pdfmtinp[AMI_MAX_PDRIVES]; + u_int8_t ain_reserved2[AMI_MAX_PDRIVES]; + + u_int32_t ain_esize; /* extended data size */ + u_int16_t ain_ssid; /* subsystem id */ + u_int16_t ain_ssvid; /* subsystem vendor id */ + u_int32_t ain_signature; +#define AMI_SIGN431 0xfffe0001 +#define AMI_SIGN438 0xfffd0002 +#define AMI_SIGN762 0xfffc0003 +#define AMI_SIGNT5 0xfffb0004 +#define AMI_SIGN466 0xfffa0005 +}; + +struct ami_fc_einquiry { + u_int32_t ain_size; /* size of this structure */ + + /* notify */ + u_int8_t ain_notify[80]; + + u_int8_t ain_rbldrate; /* rebuild rate %% */ + u_int8_t ain_flushintvl; + u_int8_t ain_sensealert; + u_int8_t ain_drvinscnt; /* drive insertion count */ + u_int8_t ain_battery; /* battery status */ + + u_int8_t ain_nlogdrv; + u_int8_t ain_recon[AMI_BIG_MAX_LDRIVES / 8]; + u_int16_t ain_stat[AMI_BIG_MAX_LDRIVES / 8]; + + u_int32_t ain_ldsize[AMI_BIG_MAX_LDRIVES]; + u_int8_t ain_ldprop[AMI_BIG_MAX_LDRIVES]; + u_int8_t ain_ldstat[AMI_BIG_MAX_LDRIVES]; + + u_int16_t ain_pdfmtinp[AMI_BIG_MAX_PDRIVES]; + u_int8_t ain_pdrates [80]; /* ??? */ +}; + +struct ami_fc_prodinfo { + u_int32_t api_size; /* size of this structure */ + u_int32_t api_config; + u_int8_t api_fwver[16]; + u_int8_t api_biosver[16]; + u_int8_t api_product[80]; + u_int8_t api_maxcmd; + u_int8_t api_channels; + u_int8_t api_fcloops; + u_int8_t api_memtype; + u_int32_t api_signature; + u_int16_t api_ramsize; + u_int16_t api_ssid; + u_int16_t api_ssvid; + u_int8_t api_nnotify; +}; + +struct ami_diskarray { + u_int8_t ada_nld; + u_int8_t ada_pad[3]; + struct { + u_int8_t adl_spandepth; + u_int8_t adl_raidlvl; + u_int8_t adl_rdahead; + u_int8_t adl_stripesz; + u_int8_t adl_status; + u_int8_t adl_wrpolicy; + u_int8_t adl_directio; + u_int8_t adl_nstripes; + struct { + u_int32_t ads_start; + u_int32_t ads_length; /* blocks */ + struct { + u_int8_t add_channel; + u_int8_t add_target; + } ads_devs[AMI_MAX_DEVDEPTH]; + } adl_spans[AMI_MAX_SPANDEPTH]; + } ada_ldrv[AMI_MAX_LDRIVES]; + struct { + u_int8_t adp_type; /* SCSI device type */ + u_int8_t adp_ostatus; /* status during config */ + u_int8_t adp_tagdepth; /* level of tagging */ + u_int8_t adp_sneg; /* sync negotiation */ + u_int32_t adp_size; + } ada_pdrv[AMI_MAX_PDRIVES]; +}; + +struct ami_scsisense { + u_int8_t ase_end; + struct { + u_int8_t asd_channel; + u_int8_t asd_target; + u_int16_t asd_errcode; + u_int16_t asd_sense; + u_int16_t asd_addarea1; + u_int16_t asd_addarea2; + u_int16_t asd_cmdspec0; + u_int16_t asd_cmdspec1; + u_int16_t asd_asc_ascq; + } ase_dump[5]; +}; + +struct ami_escsisense { + u_int8_t ase_end; + struct { + u_int8_t asd_channel; + u_int8_t asd_target; + u_int16_t asd_errcode; + u_int16_t asd_sense; + u_int16_t asd_addarea1; + u_int16_t asd_addarea2; + u_int16_t asd_cmdspec0; + u_int16_t asd_cmdspec1; + u_int16_t asd_asc_ascq; + u_int16_t asd_extarea; + } ase_dump[5]; +}; + +struct ami_cachestats { + u_int32_t acs_total; + u_int32_t acs_hits; +}; + +struct ami_drivehistory { + struct { + u_int8_t adh_error; +#define AMI_ADHERR_TIMEOUT(e) ((e) & 15) +#define AMI_ADHERR_PARITY(e) (((e) >> 4) & 15) + u_int8_t adh_throttle; + } adh_err[3][16]; /* channels * drives */ + u_int8_t adh_failidx; + struct { + u_int8_t adh_tag; +#define AMI_ADHTAG_CH(t) ((t) & 7) +#define AMI_ADHTAG_TARG(t) (((t) >> 3) & 15) +#define AMI_ADHTAG_VALID(t) ((t) & 0x80) + u_int8_t reason; +#define AMI_ADHERR_MEDIA 1 +#define AMI_ADHERR_NMEDIA 2 +#define AMI_ADHERR_CMDTMO 3 +#define AMI_ADHERR_SELTMO 4 +#define AMI_ADHERR_HAFAIL 5 +#define AMI_ADHERR_REASSIGN 6 +#define AMI_ADHERR_CMDFAIL 7 +#define AMI_ADHERR_OTHER 8 + +#define AMI_FAILHISTORY 10 + } adh_fail[AMI_FAILHISTORY]; +}; + +#pragma pack() diff --git a/sys/dev/ic/amivar.h b/sys/dev/ic/amivar.h new file mode 100644 index 00000000000..27e9589a87a --- /dev/null +++ b/sys/dev/ic/amivar.h @@ -0,0 +1,121 @@ +/* $OpenBSD: amivar.h,v 1.1 2001/03/09 11:14:22 mickey Exp $ */ + +/* + * Copyright (c) 2000 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Michael Shalayeff. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +struct ami_softc; + +struct ami_ccb { + struct ami_softc *ccb_sc; + struct ami_iocmd *ccb_cmd; + paddr_t ccb_cmdpa; + struct ami_sgent *ccb_sglist; + paddr_t ccb_sglistpa; + struct scsi_xfer *ccb_xs; + TAILQ_ENTRY(ami_ccb) ccb_link; + enum { + AMI_CCB_FREE, AMI_CCB_READY, AMI_CCB_QUEUED, AMI_CCB_PREQUEUED + } ccb_state; + int ccb_len; + void *ccb_data; + bus_dmamap_t ccb_dmamap; +}; + +typedef TAILQ_HEAD(ami_queue_head, ami_ccb) ami_queue_head; + +struct ami_softc { + struct device sc_dev; + void *sc_ih; + struct scsi_link sc_link; + struct scsi_link sc_link_raw; + + u_int sc_flags; + + /* low-level interface */ + int (*sc_init) __P((struct ami_softc *sc)); + int (*sc_exec) __P((struct ami_softc *sc)); + int (*sc_done) __P((struct ami_softc *sc, struct ami_iocmd *)); + + bus_space_tag_t iot; + bus_space_handle_t ioh; + bus_dma_tag_t dmat; + + volatile struct ami_iocmd *sc_mbox; + paddr_t sc_mbox_pa; + struct ami_ccb sc_ccbs[AMI_MAXCMDS]; + ami_queue_head sc_free_ccb, sc_ccbq, sc_ccb2q, sc_ccbdone; + + void *sc_cmds; + bus_dmamap_t sc_cmdmap; + bus_dma_segment_t sc_cmdseg[1]; + + void *sc_sgents; + bus_dmamap_t sc_sgmap; + bus_dma_segment_t sc_sgseg[1]; + + int sc_timeout; + struct timeout sc_requeue_tmo; + + char sc_fwver[16]; + char sc_biosver[16]; + int sc_maxcmds; + int sc_memory; + int sc_targets; + int sc_channels; + int sc_maxunits; + int sc_nunits; + struct { + u_int8_t hd_present; + u_int8_t hd_is_logdrv; + u_int8_t hd_heads; + u_int8_t hd_secs; + u_int8_t hd_prop; + u_int8_t hd_stat; + u_int32_t hd_size; + } sc_hdr[AMI_BIG_MAX_LDRIVES]; +}; + +/* XXX These have to become spinlocks in case of SMP */ +#define AMI_LOCK_AMI(sc) splbio() +#define AMI_UNLOCK_AMI(sc, lock) splx(lock) +typedef int ami_lock_t; + +int ami_attach __P((struct ami_softc *sc)); +int ami_intr __P((void *)); + +int ami_quartz_init __P((struct ami_softc *sc)); +int ami_quartz_exec __P((struct ami_softc *sc)); +int ami_quartz_done __P((struct ami_softc *sc, struct ami_iocmd *)); + +int ami_schwartz_init __P((struct ami_softc *sc)); +int ami_schwartz_exec __P((struct ami_softc *sc)); +int ami_schwartz_done __P((struct ami_softc *sc, struct ami_iocmd *)); + diff --git a/sys/dev/pci/ami_pci.c b/sys/dev/pci/ami_pci.c new file mode 100644 index 00000000000..e11ac42d7ac --- /dev/null +++ b/sys/dev/pci/ami_pci.c @@ -0,0 +1,243 @@ +/* $OpenBSD: ami_pci.c,v 1.1 2001/03/09 11:14:22 mickey Exp $ */ + +/* + * Copyright (c) 2000 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Michael Shalayeff. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> + +#include <machine/bus.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/amireg.h> +#include <dev/ic/amivar.h> + +#define AMI_BAR 0x10 +#define AMI_SUBSYSID 0x2c +#define PCI_EBCR 0x40 +#define AMI_WAKEUP 0x64 + +/* "Quartz" i960 Config space */ +#define AMI_PCI_INIT 0x9c +#define AMI_INITSTAT(i) (((i) >> 8) & 0xff) +#define AMI_INITTARG(i) (((i) >> 16) & 0xff) +#define AMI_INITCHAN(i) (((i) >> 24) & 0xff) +#define AMI_PCI_SIG 0xa0 +#define AMI_SIGNATURE 0x11223344 +#define AMI_PCI_SGL 0xa4 +#define AMI_SGL_LHC 0x00000299 +#define AMI_SGL_HLC 0x00000199 + +int ami_pci_match __P((struct device *, void *, void *)); +void ami_pci_attach __P((struct device *, struct device *, void *)); + +struct cfattach ami_pci_ca = { + sizeof(struct ami_softc), ami_pci_match, ami_pci_attach +}; + +static const +struct ami_pci_device { + int vendor; + int product; + int flags; +#define AMI_CHECK_SIGN 0x001 +} ami_pci_devices[] = { + { PCI_VENDOR_AMI, PCI_PRODUCT_AMI_MEGARAID, 0 }, + { PCI_VENDOR_AMI, PCI_PRODUCT_AMI_MEGARAID428, 0 }, + { PCI_VENDOR_AMI, PCI_PRODUCT_AMI_MEGARAID434, 0 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_80960RP_ATU, AMI_CHECK_SIGN }, + { 0 } +}; + +static const +struct ami_pci_subsys { + pcireg_t id; + char name[12]; +} ami_pci_subsys[] = { + /* only those of a special name are listed here */ + { 0x09A0101E, "Dell 466v1" }, + { 0x11111111, "Dell 466v2" }, + { 0x11121111, "Dell 438" }, + { 0x11111028, "Dell 466v3" }, + { 0x10c6103c, "HP 438" }, + { 0x10c7103c, "HP T5/T6" }, + { 0x10cc103c, "HP T7" }, + { 0x10cd103c, "HP 466" }, + { 0 } +}; + +static const +struct ami_pci_vendor { + u_int16_t id; + char name[8]; +} ami_pci_vendors[] = { + { 0x101e, "AMI" }, + { 0x1028, "Dell" }, + { 0x103c, "HP" }, + { 0 } +}; + +int +ami_pci_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = aux; + const struct ami_pci_device *pami; + + for (pami = ami_pci_devices; pami->vendor; pami++) { + if (pami->vendor == PCI_VENDOR(pa->pa_id) && + pami->product == PCI_PRODUCT(pa->pa_id) && + (!pami->flags & AMI_CHECK_SIGN || + pci_conf_read(pa->pa_pc, pa->pa_tag, AMI_PCI_SIG) == + AMI_SIGNATURE)) + return (1); + } + return (0); +} + +void +ami_pci_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct ami_softc *sc = (struct ami_softc *)self; + struct pci_attach_args *pa = aux; + pci_intr_handle_t ih; + const char *intrstr, *model = NULL, *lhc; + const struct ami_pci_subsys *ssp; + bus_size_t size; + pcireg_t csr; +#if 0 + /* reset */ + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_EBCR, + pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_EBCR) | 0x20); + pci_conf_write(pa->pa_pc, pa->pa_tag, AMI_WAKEUP, 0); +#endif + if (!pci_mapreg_map(pa, AMI_BAR, PCI_MAPREG_TYPE_IO, 0, + &sc->iot, &sc->ioh, NULL, &size)) { + + sc->sc_init = ami_schwartz_init; + sc->sc_exec = ami_schwartz_exec; + sc->sc_done = ami_schwartz_done; + + } else if (!pci_mapreg_map(pa, AMI_BAR, PCI_MAPREG_TYPE_MEM, 0, + &sc->iot, &sc->ioh, NULL, &size)) { + + sc->sc_init = ami_quartz_init; + sc->sc_exec = ami_quartz_exec; + sc->sc_done = ami_quartz_done; + + } else { + printf(": can't map controller pci space\n"); + return; + } + sc->dmat = pa->pa_dmat; + + /* enable bus mastering (should not it be mi?) */ + csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + csr | PCI_COMMAND_MASTER_ENABLE); + + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf(": can't map interrupt\n"); + bus_space_unmap(sc->iot, sc->ioh, size); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, ami_intr, sc, + sc->sc_dev.dv_xname); + if (!sc->sc_ih) { + printf(": can't establish interrupt"); + if (intrstr) + printf(" at %s", intrstr); + printf("\n"); + bus_space_unmap(sc->iot, sc->ioh, size); + } + + printf(" %s", intrstr); + + csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + for (ssp = ami_pci_subsys; ssp->id; ssp++) + if (ssp->id == csr) { + model = ssp->name; + break; + } + + if (!model && PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMI) + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_AMI_MEGARAID428: + model = "AMI 428"; + break; + case PCI_PRODUCT_AMI_MEGARAID434: + model = "AMI 434"; + break; + } + + if (!model) { + const struct ami_pci_vendor *vp; + static char modelbuf[12]; + + for (vp = ami_pci_vendors; + vp->id && vp->id != (csr & 0xffff); vp++); + if (vp->id) + sprintf(modelbuf, "%s %x", vp->name, + (csr >> 16) & 0xffff); + else + sprintf(modelbuf, "unknown 0x%08x", csr); + model = modelbuf; + } + + switch (pci_conf_read(pa->pa_pc, pa->pa_tag, AMI_PCI_SGL)) { + case AMI_SGL_LHC: lhc = "64b/lhc"; break; + case AMI_SGL_HLC: lhc = "64b/hlc"; break; + default: lhc = "32b"; + } + + printf(": %s/%s\n%s", model, lhc, sc->sc_dev.dv_xname); + + if (ami_attach(sc)) { + pci_intr_disestablish(pa->pa_pc, sc->sc_ih); + sc->sc_ih = NULL; + bus_space_unmap(sc->iot, sc->ioh, size); + } +} |