summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ic/ami.c1053
-rw-r--r--sys/dev/ic/amireg.h540
-rw-r--r--sys/dev/ic/amivar.h121
-rw-r--r--sys/dev/pci/ami_pci.c243
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);
+ }
+}