aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMike Smith <msmith@FreeBSD.org>1999-10-07 02:23:12 +0000
committerMike Smith <msmith@FreeBSD.org>1999-10-07 02:23:12 +0000
commitd5a51e03e79c61befdb9f749cab2c79b7eac407e (patch)
tree9e7993670832ba736069cfee75286aeead9f189a /sys/dev
parent1ac4b82b11c460d0ef82bf760b71a8b4d34be28a (diff)
downloadsrc-d5a51e03e79c61befdb9f749cab2c79b7eac407e.tar.gz
src-d5a51e03e79c61befdb9f749cab2c79b7eac407e.zip
This is a driver for the AMI MegaRAID family of controllers. It all of
the AMI PCI controllers using the 8LD firmware interface (40LD firmware will be supported as soon as I have hardware to test with). These controllers are rebadged by Dell as the PERC, as well as by HP and possibly other vendors.
Notes
Notes: svn path=/head/; revision=51974
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/amr/amr.c1498
-rw-r--r--sys/dev/amr/amr_disk.c299
-rw-r--r--sys/dev/amr/amr_pci.c208
-rw-r--r--sys/dev/amr/amrio.h28
-rw-r--r--sys/dev/amr/amrreg.h199
-rw-r--r--sys/dev/amr/amrvar.h216
6 files changed, 2448 insertions, 0 deletions
diff --git a/sys/dev/amr/amr.c b/sys/dev/amr/amr.c
new file mode 100644
index 000000000000..257479b8b903
--- /dev/null
+++ b/sys/dev/amr/amr.c
@@ -0,0 +1,1498 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Driver for the AMI MegaRaid family of controllers
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/devicestat.h>
+#include <sys/disk.h>
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+
+#include <dev/amr/amrio.h>
+#include <dev/amr/amrreg.h>
+#include <dev/amr/amrvar.h>
+
+#if 0
+#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
+#else
+#define debug(fmt, args...)
+#endif
+
+#define AMR_CDEV_MAJOR 132
+
+static struct cdevsw amr_cdevsw = {
+ /* open */ amr_open,
+ /* close */ amr_close,
+ /* read */ noread,
+ /* write */ nowrite,
+ /* ioctl */ amr_ioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "amr",
+ /* maj */ AMR_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ 254 /* XXX magic no-bdev */
+};
+
+static int cdev_registered = 0;
+devclass_t amr_devclass;
+
+/*
+ * Command wrappers
+ */
+static int amr_query_controller(struct amr_softc *sc);
+static void *amr_enquiry(struct amr_softc *sc, size_t bufsize,
+ u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual);
+static int amr_flush(struct amr_softc *sc);
+static void amr_startio(struct amr_softc *sc);
+static void amr_completeio(struct amr_command *ac);
+
+/*
+ * Command processing.
+ */
+static int amr_wait_command(struct amr_command *ac);
+static int amr_poll_command(struct amr_command *ac);
+static int amr_getslot(struct amr_command *ac);
+static void amr_mapcmd(struct amr_command *ac);
+static void amr_unmapcmd(struct amr_command *ac);
+static int amr_start(struct amr_command *ac);
+static int amr_done(struct amr_softc *sc);
+static void amr_complete(struct amr_softc *sc);
+
+/*
+ * Command buffer allocation.
+ */
+static struct amr_command *amr_alloccmd(struct amr_softc *sc);
+static void amr_releasecmd(struct amr_command *ac);
+static void amr_freecmd(struct amr_command *ac);
+
+/*
+ * Interface-specific shims
+ */
+static void amr_quartz_submit_command(struct amr_softc *sc);
+static int amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
+static void amr_quartz_attach_mailbox(struct amr_softc *sc);
+
+static void amr_std_submit_command(struct amr_softc *sc);
+static int amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
+static void amr_std_attach_mailbox(struct amr_softc *sc);
+
+/*
+ * Debugging
+ */
+static void amr_printcommand(struct amr_command *ac);
+
+/********************************************************************************
+ ********************************************************************************
+ Public Interfaces
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Free all of the resources associated with (sc)
+ *
+ * Should not be called if the controller is active.
+ */
+void
+amr_free(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ u_int8_t *p;
+
+ debug("called");
+
+
+ /* throw away any command buffers */
+ while ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL) {
+ TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
+ amr_freecmd(ac);
+ }
+
+ /* destroy data-transfer DMA tag */
+ if (sc->amr_buffer_dmat)
+ bus_dma_tag_destroy(sc->amr_buffer_dmat);
+
+ /* free and destroy DMA memory and tag for s/g lists */
+ if (sc->amr_sgtable)
+ bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
+ if (sc->amr_sg_dmat)
+ bus_dma_tag_destroy(sc->amr_sg_dmat);
+
+ /* free and destroy DMA memory and tag for mailbox */
+ if (sc->amr_mailbox) {
+ p = (u_int8_t *)sc->amr_mailbox;
+ bus_dmamem_free(sc->amr_sg_dmat, p - 16, sc->amr_sg_dmamap);
+ }
+ if (sc->amr_sg_dmat)
+ bus_dma_tag_destroy(sc->amr_sg_dmat);
+
+ /* disconnect the interrupt handler */
+ if (sc->amr_intr)
+ bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr);
+ if (sc->amr_irq != NULL)
+ bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq);
+
+ /* destroy the parent DMA tag */
+ if (sc->amr_parent_dmat)
+ bus_dma_tag_destroy(sc->amr_parent_dmat);
+
+ /* release the register window mapping */
+ if (sc->amr_reg != NULL)
+ bus_release_resource(sc->amr_dev,
+ (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT,
+ AMR_CFG_BASE, sc->amr_reg);
+}
+
+/********************************************************************************
+ * Allocate and map the scatter/gather table in bus space.
+ */
+static void
+amr_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+
+ debug("called");
+
+ /* save base of s/g table's address in bus space */
+ sc->amr_sgbusaddr = segs->ds_addr;
+}
+
+static int
+amr_sglist_map(struct amr_softc *sc)
+{
+ size_t segsize;
+ int error;
+
+ debug("called");
+
+ /* destroy any existing mappings */
+ if (sc->amr_sgtable)
+ bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
+ if (sc->amr_sg_dmat)
+ bus_dma_tag_destroy(sc->amr_sg_dmat);
+
+ /*
+ * Create a single tag describing a region large enough to hold all of
+ * the s/g lists we will need.
+ */
+ segsize = sizeof(struct amr_sgentry) * AMR_NSEG * sc->amr_maxio;
+ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ segsize, 1, /* maxsize, nsegments */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
+ 0, /* flags */
+ &sc->amr_sg_dmat);
+ if (error != 0) {
+ device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n");
+ return(ENOMEM);
+ }
+
+ /*
+ * Allocate enough s/g maps for all commands and permanently map them into
+ * controller-visible space.
+ *
+ * XXX this assumes we can get enough space for all the s/g maps in one
+ * contiguous slab. We may need to switch to a more complex arrangement where
+ * we allocate in smaller chunks and keep a lookup table from slot to bus address.
+ */
+ error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap);
+ if (error) {
+ device_printf(sc->amr_dev, "can't allocate s/g table\n");
+ return(ENOMEM);
+ }
+ bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_dma_map_sg, sc, 0);
+ return(0);
+}
+
+/********************************************************************************
+ * Allocate and set up mailbox areas for the controller (sc)
+ *
+ * The basic mailbox structure should be 16-byte aligned. This means that the
+ * mailbox64 structure has 4 bytes hanging off the bottom.
+ */
+static void
+amr_map_mailbox(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+
+ debug("called");
+
+ /* save phsyical base of the basic mailbox structure */
+ sc->amr_mailboxphys = segs->ds_addr + 16;
+}
+
+static int
+amr_setup_mbox(struct amr_softc *sc)
+{
+ int error;
+ u_int8_t *p;
+
+ debug("called");
+
+ /*
+ * Create a single tag describing a region large enough to hold the entire
+ * mailbox.
+ */
+ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */
+ 16, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
+ 0, /* flags */
+ &sc->amr_mailbox_dmat);
+ if (error != 0) {
+ device_printf(sc->amr_dev, "can't allocate mailbox tag\n");
+ return(ENOMEM);
+ }
+
+ /*
+ * Allocate the mailbox structure and permanently map it into
+ * controller-visible space.
+ */
+ error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT,
+ &sc->amr_mailbox_dmamap);
+ if (error) {
+ device_printf(sc->amr_dev, "can't allocate mailbox memory\n");
+ return(ENOMEM);
+ }
+ bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p,
+ sizeof(struct amr_mailbox64), amr_map_mailbox, sc, 0);
+ /*
+ * Conventional mailbox is inside the mailbox64 region.
+ */
+ bzero(p, sizeof(struct amr_mailbox64));
+ sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12);
+ sc->amr_mailbox = (struct amr_mailbox *)(p + 16);
+
+ if (sc->amr_type == AMR_TYPE_STD) {
+ /* XXX we have to tell the controller where we put it */
+ }
+ return(0);
+}
+
+
+/********************************************************************************
+ * Initialise the controller and softc.
+ */
+int
+amr_attach(struct amr_softc *sc)
+{
+ int rid, error;
+
+ /*
+ * Initialise per-controller queues.
+ */
+ TAILQ_INIT(&sc->amr_donecmds);
+ TAILQ_INIT(&sc->amr_freecmds);
+ bufq_init(&sc->amr_bufq);
+
+ /*
+ * Configure for this controller type.
+ */
+ if (sc->amr_type == AMR_TYPE_QUARTZ) {
+ sc->amr_submit_command = amr_quartz_submit_command;
+ sc->amr_get_work = amr_quartz_get_work;
+ sc->amr_attach_mailbox = amr_quartz_attach_mailbox;
+ } else {
+ sc->amr_submit_command = amr_std_submit_command;
+ sc->amr_get_work = amr_std_get_work;
+ sc->amr_attach_mailbox = amr_std_attach_mailbox;
+ }
+
+ /*
+ * Allocate and connect our interrupt.
+ */
+ rid = 0;
+ sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
+ if (sc->amr_irq == NULL) {
+ device_printf(sc->amr_dev, "couldn't allocate interrupt\n");
+ amr_free(sc);
+ return(ENXIO);
+ }
+ error = bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO, amr_intr, sc, &sc->amr_intr);
+ if (error) {
+ device_printf(sc->amr_dev, "couldn't set up interrupt\n");
+ amr_free(sc);
+ return(ENXIO);
+ }
+
+ /*
+ * Create DMA tag for mapping buffers into controller-addressable space.
+ */
+ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
+ 0, /* flags */
+ &sc->amr_buffer_dmat);
+ if (error != 0) {
+ device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
+ return(ENOMEM);
+ }
+
+ /*
+ * Allocate and set up mailbox in a bus-visible fashion, attach to controller.
+ */
+ if ((error = amr_setup_mbox(sc)) != 0)
+ return(error);
+ sc->amr_attach_mailbox(sc);
+
+ /*
+ * Build a temporary set of scatter/gather buffers.
+ */
+ sc->amr_maxio = 2;
+ if (amr_sglist_map(sc))
+ return(ENXIO);
+
+ /*
+ * Quiz controller for features and limits.
+ */
+ if (amr_query_controller(sc))
+ return(ENXIO);
+
+ /*
+ * Rebuild the scatter/gather buffers now we know how many we need.
+ */
+ if (amr_sglist_map(sc))
+ return(ENXIO);
+
+ return(0);
+}
+
+/********************************************************************************
+ * Locate disk resources and attach children to them.
+ */
+void
+amr_startup(struct amr_softc *sc)
+{
+ struct amr_logdrive *dr;
+ int i, error;
+
+ debug("called");
+
+ /* get up-to-date drive information */
+ if (amr_query_controller(sc)) {
+ device_printf(sc->amr_dev, "couldn't scan controller for drives\n");
+ return;
+ }
+
+ /* iterate over available drives */
+ for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
+ /* are we already attached to this drive? */
+ if (dr->al_disk == 0) {
+ /* generate geometry information */
+ if (dr->al_size > 0x200000) { /* extended translation? */
+ dr->al_heads = 255;
+ dr->al_sectors = 63;
+ } else {
+ dr->al_heads = 64;
+ dr->al_sectors = 32;
+ }
+ dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
+
+ dr->al_disk = device_add_child(sc->amr_dev, NULL, -1, dr);
+ if (dr->al_disk == 0)
+ device_printf(sc->amr_dev, "device_add_child failed\n");
+ }
+ }
+
+ if ((error = bus_generic_attach(sc->amr_dev)) != 0)
+ device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
+
+ /* mark controller back up */
+ sc->amr_state &= ~AMR_STATE_SHUTDOWN;
+
+ /* interrupts will be enabled before we do anything more */
+ sc->amr_state |= AMR_STATE_INTEN;
+}
+
+/********************************************************************************
+ * Disconnect from the controller completely, in preparation for unload.
+ */
+int
+amr_detach(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+ int error;
+
+ debug("called");
+
+ if (sc->amr_state & AMR_STATE_OPEN)
+ return(EBUSY);
+
+ if ((error = amr_shutdown(dev)))
+ return(error);
+
+ amr_free(sc);
+
+ /*
+ * Deregister the control device on last detach.
+ */
+ if (--cdev_registered == 0)
+ cdevsw_remove(&amr_cdevsw);
+
+ return(0);
+}
+
+/********************************************************************************
+ * Bring the controller down to a dormant state and detach all child devices.
+ *
+ * This function is called before detach, system shutdown, or before performing
+ * an operation which may add or delete system disks. (Call amr_startup to
+ * resume normal operation.)
+ *
+ * Note that we can assume that the bufq on the controller is empty, as we won't
+ * allow shutdown if any device is open.
+ */
+int
+amr_shutdown(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+ struct amrd_softc *ad;
+ int i, s, error;
+
+ debug("called");
+
+ s = splbio();
+ error = 0;
+
+
+ /* assume we're going to shut down */
+ sc->amr_state |= AMR_STATE_SHUTDOWN;
+ for (i = 0; i < AMR_MAXLD; i++) {
+ if (sc->amr_drive[i].al_disk != 0) {
+ ad = device_get_softc(sc->amr_drive[i].al_disk);
+ if (ad->amrd_flags & AMRD_OPEN) { /* drive is mounted, abort shutdown */
+ sc->amr_state &= ~AMR_STATE_SHUTDOWN;
+ device_printf(sc->amr_drive[i].al_disk, "still open, can't shutdown\n");
+ error = EBUSY;
+ goto out;
+ }
+ }
+ }
+
+ /* flush controller */
+ device_printf(sc->amr_dev, "flushing cache...");
+ if (amr_flush(sc)) {
+ printf("failed\n");
+ } else {
+ printf("done\n");
+ }
+
+ /* delete all our child devices */
+ for (i = 0; i < AMR_MAXLD; i++) {
+ if (sc->amr_drive[i].al_disk != 0) {
+ if ((error = device_delete_child(sc->amr_dev, sc->amr_drive[i].al_disk)) != 0)
+ goto out;
+ sc->amr_drive[i].al_disk = 0;
+ }
+ }
+ bus_generic_detach(sc->amr_dev);
+
+ out:
+ splx(s);
+ return(error);
+}
+
+/********************************************************************************
+ * Bring the controller to a quiescent state, ready for system suspend.
+ */
+int
+amr_suspend(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+
+ debug("called");
+
+ sc->amr_state |= AMR_STATE_SUSPEND;
+
+ /* flush controller */
+ device_printf(sc->amr_dev, "flushing cache...");
+ printf("%s\n", amr_flush(sc) ? "failed" : "done");
+
+ return(0);
+}
+
+/********************************************************************************
+ * Bring the controller back to a state ready for operation.
+ */
+int
+amr_resume(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+
+ debug("called");
+
+ sc->amr_state &= ~AMR_STATE_SUSPEND;
+
+ return(0);
+}
+
+/*******************************************************************************
+ * Take an interrupt, or be poked by other code to look for interrupt-worthy
+ * status.
+ */
+void
+amr_intr(void *arg)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+ int worked;
+
+ debug("called on %p", sc);
+
+ /* spin collecting finished commands, process them if we find anything */
+ worked = 0;
+ while (amr_done(sc))
+ worked = 1;
+ if (worked)
+ amr_complete(sc);
+};
+
+/*******************************************************************************
+ * Receive a buf structure from a child device and queue it on a particular
+ * disk resource, then poke the disk resource to start as much work as it can.
+ */
+int
+amr_submit_buf(struct amr_softc *sc, struct buf *bp)
+{
+ debug("called");
+
+ bufq_insert_tail(&sc->amr_bufq, bp);
+ sc->amr_waitbufs++;
+ amr_startio(sc);
+ return(0);
+}
+
+/********************************************************************************
+ * Accept an open operation on the control device.
+ */
+int
+amr_open(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int unit = minor(dev);
+ struct amr_softc *sc = devclass_get_softc(amr_devclass, unit);
+
+ sc->amr_state |= AMR_STATE_OPEN;
+ return(0);
+}
+
+/********************************************************************************
+ * Accept the last close on the control device.
+ */
+int
+amr_close(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int unit = minor(dev);
+ struct amr_softc *sc = devclass_get_softc(amr_devclass, unit);
+
+ sc->amr_state &= ~AMR_STATE_OPEN;
+ return (0);
+}
+
+/********************************************************************************
+ * Handle controller-specific control operations.
+ */
+int
+amr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+
+ switch(cmd) {
+ default:
+ return(ENOTTY);
+ }
+}
+
+/********************************************************************************
+ * Handle operations requested by a drive connected to this controller.
+ */
+int
+amr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd,
+ caddr_t addr, int32_t flag, struct proc *p)
+{
+ return(ENOTTY);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Command Wrappers
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Interrogate the controller for the operational parameters we require.
+ */
+static int
+amr_query_controller(struct amr_softc *sc)
+{
+ void *buf;
+ int i;
+
+ /* try to issue an ENQUIRY3 command */
+ if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
+ AMR_CONFIG_ENQ3_SOLICITED_FULL)) == NULL) {
+
+ struct amr_enquiry *ae;
+
+ /* failed, try the old ENQUIRY command */
+ if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
+ device_printf(sc->amr_dev, "could not obtain configuration data from controller\n");
+ return(1);
+ }
+ /* first-time enquiry? */
+ if (sc->amr_maxdrives == 0) {
+ device_printf(sc->amr_dev, "firmware %.4s bios %.4s %dMB memory, chipset %x\n",
+ ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
+ ae->ae_adapter.aa_memorysize, ae->ae_adapter.aa_chipsetvalue);
+ }
+ sc->amr_maxdrives = 8;
+ sc->amr_maxio = ae->ae_adapter.aa_maxio;
+ for (i = 0; i < ae->ae_ldrv.al_numdrives; i++) {
+ sc->amr_drive[i].al_size = ae->ae_ldrv.al_size[i];
+ sc->amr_drive[i].al_state = ae->ae_ldrv.al_state[i];
+ sc->amr_drive[i].al_properties = ae->ae_ldrv.al_properties[i];
+ debug(" drive %d: %d state %x properties %x\n", i, sc->amr_drive[i].al_size,
+ sc->amr_drive[i].al_state, sc->amr_drive[i].al_properties);
+ }
+ for (; i < AMR_MAXLD; i++)
+ sc->amr_drive[i].al_size = 0xffffffff;
+ free(ae, M_DEVBUF);
+ } else {
+ free(buf, M_DEVBUF);
+ sc->amr_maxdrives = 40;
+
+ /* get static product info */
+ if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODINFO, 0)) == NULL) {
+ device_printf(sc->amr_dev, "controller supports 40ld but CONFIG_PRODINFO failed\n");
+ return(1);
+ }
+ free(buf, M_DEVBUF);
+ device_printf(sc->amr_dev, "40LD firmware unsupported; send controller to msmith@freebsd.org\n");
+ return(1);
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Run a generic enquiry-style command.
+ */
+static void *
+amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
+{
+ struct amr_command *ac;
+ void *result;
+ u_int8_t *mbox;
+ int error;
+
+ debug("called");
+
+ error = 1;
+ result = NULL;
+
+ /* get ourselves a command buffer */
+ if ((ac = amr_alloccmd(sc)) == NULL)
+ goto out;
+ /* allocate the response structure */
+ if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
+ goto out;
+ /* get a command slot */
+ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
+ if (amr_getslot(ac))
+ goto out;
+
+ /* map the command so the controller can see it */
+ ac->ac_data = result;
+ ac->ac_length = bufsize;
+ amr_mapcmd(ac);
+
+ /* build the command proper */
+ mbox = (u_int8_t *)&ac->ac_mailbox; /* XXX want a real structure for this? */
+ mbox[0] = cmd;
+ mbox[2] = cmdsub;
+ mbox[3] = cmdqual;
+ ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
+
+ /* run the command in polled/wait mode as suits the current mode */
+ if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac))
+ goto out;
+ error = ac->ac_status;
+
+ out:
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ if ((error != 0) && (result != NULL)) {
+ free(result, M_DEVBUF);
+ result = NULL;
+ }
+ return(result);
+}
+
+/********************************************************************************
+ * Flush the controller's internal cache, return status.
+ */
+static int
+amr_flush(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ int error;
+
+ /* get ourselves a command buffer */
+ error = 1;
+ if ((ac = amr_alloccmd(sc)) == NULL)
+ goto out;
+ /* get a command slot */
+ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
+ if (amr_getslot(ac))
+ goto out;
+
+ /* build the command proper */
+ ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
+
+ /* run the command in polled/wait mode as suits the current mode */
+ if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac))
+ goto out;
+ error = ac->ac_status;
+
+ out:
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ return(error);
+}
+
+/********************************************************************************
+ * Pull as much work off the softc's work queue as possible and give it to the
+ * controller. Leave a couple of slots free for emergencies.
+ *
+ * Must be called at splbio or in an equivalent fashion that prevents
+ * reentry or activity on the bufq.
+ */
+static void
+amr_startio(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ struct amrd_softc *amrd;
+ struct buf *bp;
+ int blkcount;
+ int driveno;
+ int cmd;
+
+ /* spin until something prevents us from doing any work */
+ for (;;) {
+
+ /* see if there's work to be done */
+ if ((bp = bufq_first(&sc->amr_bufq)) == NULL)
+ break;
+ /* get a command */
+ if ((ac = amr_alloccmd(sc)) == NULL)
+ break;
+ /* get a slot for the command */
+ if (amr_getslot(ac) != 0) {
+ amr_releasecmd(ac);
+ break;
+ }
+ /* get the buf containing our work */
+ bufq_remove(&sc->amr_bufq, bp);
+ sc->amr_waitbufs--;
+
+ /* connect the buf to the command */
+ ac->ac_complete = amr_completeio;
+ ac->ac_private = bp;
+ ac->ac_data = bp->b_data;
+ ac->ac_length = bp->b_bcount;
+ if (bp->b_flags & B_READ) {
+ ac->ac_flags |= AMR_CMD_DATAIN;
+ cmd = AMR_CMD_LREAD;
+ } else {
+ ac->ac_flags |= AMR_CMD_DATAOUT;
+ cmd = AMR_CMD_LWRITE;
+ }
+
+ /* map the command so the controller can work with it */
+ amr_mapcmd(ac);
+
+ /* build a suitable I/O command (assumes 512-byte rounded transfers) */
+ amrd = (struct amrd_softc *)bp->b_driver1;
+ driveno = amrd->amrd_drive - &sc->amr_drive[0];
+ blkcount = bp->b_bcount / AMR_BLKSIZE;
+
+ if ((bp->b_blkno + blkcount) > sc->amr_drive[driveno].al_size)
+ device_printf(sc->amr_dev, "I/O beyond end of unit (%u,%d > %u)\n",
+ bp->b_blkno, blkcount, sc->amr_drive[driveno].al_size);
+
+ /*
+ * Build the I/O command.
+ */
+ ac->ac_mailbox.mb_command = cmd;
+ ac->ac_mailbox.mb_blkcount = blkcount;
+ ac->ac_mailbox.mb_lba = bp->b_blkno;
+ ac->ac_mailbox.mb_physaddr = ac->ac_sgphys;
+ ac->ac_mailbox.mb_drive = driveno;
+ ac->ac_mailbox.mb_nsgelem = ac->ac_nsgent;
+
+ /* try to give command to controller */
+ if (amr_start(ac) != 0) {
+ /* fail the command */
+ ac->ac_status = AMR_STATUS_WEDGED;
+ amr_completeio(ac);
+ }
+ }
+}
+
+/********************************************************************************
+ * Handle completion of an I/O command.
+ */
+static void
+amr_completeio(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ struct buf *bp = (struct buf *)ac->ac_private;
+
+ if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+
+ switch(ac->ac_status) {
+ /* XXX need more information on I/O error reasons */
+ default:
+ device_printf(sc->amr_dev, "I/O error - %x\n", ac->ac_status);
+ amr_printcommand(ac);
+ break;
+ }
+ }
+ amr_releasecmd(ac);
+ amrd_intr(bp);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Command Processing
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Take a command, submit it to the controller and sleep until it completes
+ * or fails. Interrupts must be enabled, returns nonzero on error.
+ */
+static int
+amr_wait_command(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int error, count;
+
+ debug("called");
+
+ ac->ac_complete = NULL;
+ ac->ac_private = ac;
+ if ((error = amr_start(ac)) != 0)
+ return(error);
+
+ count = 0;
+ /* XXX better timeout? */
+ while ((ac->ac_status == AMR_STATUS_BUSY) && (count < 30)) {
+ tsleep(ac->ac_private, PRIBIO | PCATCH, "amrwcmd", hz);
+ }
+
+ if (ac->ac_status != 0) {
+ device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status);
+ return(EIO);
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Take a command, submit it to the controller and busy-wait for it to return.
+ * Returns nonzero on error. Can be safely called with interrupts enabled.
+ */
+static int
+amr_poll_command(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int error, count, s;
+
+ debug("called");
+
+ ac->ac_complete = NULL;
+ ac->ac_private = NULL;
+ if ((error = amr_start(ac)) != 0)
+ return(error);
+
+ count = 0;
+ do {
+ /*
+ * Poll for completion, although the interrupt handler may beat us to it.
+ * Note that the timeout here is somewhat arbitrary.
+ */
+ amr_done(sc);
+ } while ((ac->ac_status == AMR_STATUS_BUSY) && (count++ < 100000));
+ s = splbio();
+ if (ac->ac_status != AMR_STATUS_BUSY) {
+ TAILQ_REMOVE(&sc->amr_donecmds, ac, ac_link);
+ sc->amr_donecmdcount--;
+ error = 0;
+ } else {
+ /* take the command out of the busy list, mark slot as bogus */
+ sc->amr_busycmd[ac->ac_slot] = (struct amr_command *)sc;
+ error = EIO;
+ device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status);
+ }
+ splx(s);
+ return(error);
+}
+
+/********************************************************************************
+ * Get a free command slot.
+ */
+static int
+amr_getslot(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int s, slot, limit;
+
+ debug("called");
+
+ /* enforce slot usage limit */
+ limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
+ if (sc->amr_busycmdcount > limit)
+ return(EBUSY);
+
+ /*
+ * Allocate a slot
+ */
+ s = splbio();
+ for (slot = 0; slot < sc->amr_maxio; slot++) {
+ if (sc->amr_busycmd[slot] == NULL)
+ break;
+ }
+ if (slot < sc->amr_maxio) {
+ sc->amr_busycmdcount++;
+ sc->amr_busycmd[slot] = ac;
+ }
+ splx(s);
+
+ /* out of slots? */
+ if (slot >= sc->amr_maxio)
+ return(EBUSY);
+
+ ac->ac_slot = slot;
+ return(0);
+}
+
+/********************************************************************************
+ * Map/unmap (ac)'s data in the controller's addressable space.
+ */
+static void
+amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
+{
+ struct amr_command *ac = (struct amr_command *)arg;
+ struct amr_softc *sc = ac->ac_sc;
+ struct amr_sgentry *sg;
+ int i;
+
+ debug("called");
+
+ /* get base address of s/g table */
+ sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+
+ /* save s/g table information in command */
+ ac->ac_nsgent = nsegments;
+ ac->ac_sgphys = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
+ ac->ac_dataphys = segs[0].ds_addr;
+
+ /* populate s/g table */
+ for (i = 0; i < nsegments; i++, sg++) {
+ sg->sg_addr = segs[i].ds_addr;
+ sg->sg_count = segs[i].ds_len;
+ }
+}
+
+static void
+amr_mapcmd(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+
+ debug("called");
+
+ /* if the command involves data at all */
+ if (ac->ac_data != NULL) {
+
+ /* map the data buffer into bus space and build the s/g list */
+ bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
+ amr_setup_dmamap, ac, 0);
+ if (ac->ac_flags & AMR_CMD_DATAIN)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
+ if (ac->ac_flags & AMR_CMD_DATAOUT)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
+ }
+}
+
+static void
+amr_unmapcmd(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+
+ debug("called");
+
+ /* if the command involved data at all */
+ if (ac->ac_data != NULL) {
+
+ if (ac->ac_flags & AMR_CMD_DATAIN)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
+ if (ac->ac_flags & AMR_CMD_DATAOUT)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
+
+ bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
+ }
+}
+
+/********************************************************************************
+ * Take a command and give it to the controller. Take care of any completed
+ * commands we encouter while waiting.
+ */
+static int
+amr_start(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int worked, done, s, i;
+
+ debug("called");
+
+ /*
+ * Save the slot number so that we can locate this command when complete.
+ * Note that ident = 0 seems to be special, so we don't use it.
+ */
+ ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
+
+ /* set the busy flag when we copy the mailbox in */
+ ac->ac_mailbox.mb_busy = 1;
+
+ /* set impossible status so that a woken sleeper can tell the command is busy */
+ ac->ac_status = AMR_STATUS_BUSY;
+
+ /* spin waiting for the mailbox */
+ debug("wait for mailbox");
+ for (i = 10000, done = 0, worked = 0; (i > 0) && !done; i--) {
+ s = splbio();
+
+ /* is the mailbox free? */
+ if (sc->amr_mailbox->mb_busy == 0) {
+ debug("got mailbox");
+ sc->amr_mailbox64->mb64_segment = 0;
+ bcopy(&ac->ac_mailbox, sc->amr_mailbox, AMR_MBOX_CMDSIZE);
+ sc->amr_submit_command(sc);
+ done = 1;
+
+ /* not free, try to clean up while we wait */
+ } else {
+ debug("busy flag %x\n", sc->amr_mailbox->mb_busy);
+ worked = amr_done(sc);
+ }
+ splx(s);
+ }
+
+ /* do completion processing if we picked anything up */
+ if (worked)
+ amr_complete(sc);
+
+ /* command is enqueued? */
+ if (done) {
+ ac->ac_stamp = time_second;
+ debug("posted command");
+ return(0);
+ }
+
+ /*
+ * The controller wouldn't take the command. Revoke the slot
+ * that the command was given and return with a bad status.
+ */
+ sc->amr_busycmd[ac->ac_slot] = NULL;
+ device_printf(sc->amr_dev, "controller wedged (not taking commands)\n");
+ ac->ac_status = AMR_STATUS_WEDGED;
+ return(EIO);
+}
+
+/********************************************************************************
+ * Extract one or more completed commands from the controller (sc)
+ *
+ * Returns nonzero if work was moved to the done queue.
+ */
+static int
+amr_done(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ struct amr_mailbox mbox;
+ int i, idx, s, result;
+
+ debug("called");
+
+ /* See if there's anything for us to do */
+ result = 0;
+ if (sc->amr_get_work(sc, &mbox)) {
+ /* iterate over completed commands */
+ for (i = 0; i < mbox.mb_nstatus; i++) {
+ /* get pointer to busy command */
+ idx = mbox.mb_completed[i] - 1;
+ ac = sc->amr_busycmd[idx];
+
+ /* really a busy command? */
+ if (ac != NULL) {
+
+ /* pull the command from the busy index */
+ sc->amr_busycmd[idx] = NULL;
+ sc->amr_busycmdcount--;
+
+ /* aborted command? */
+ if (ac == (struct amr_command *)sc) {
+ device_printf(sc->amr_dev, "aborted command completed (%d)\n", idx);
+ ac = NULL;
+
+ /* normally completed command, move it to the done queue */
+ } else {
+ sc->amr_donecmdcount++;
+ s = splbio();
+ TAILQ_INSERT_TAIL(&sc->amr_donecmds, ac, ac_link);
+ splx(s);
+ /* save completion status */
+ ac->ac_status = mbox.mb_status;
+ debug("completed command with status %x", mbox.mb_status);
+
+ /* unmap data buffer */
+ amr_unmapcmd(ac);
+ }
+ result = 1;
+ }
+ }
+ }
+ return(result);
+}
+
+/********************************************************************************
+ * Do completion processing on done commands on (sc)
+ */
+static void
+amr_complete(struct amr_softc *sc)
+{
+ struct amr_command *ac, *nc;
+ int s, count;
+
+ debug("called");
+
+ count = 0;
+
+ s = splbio();
+ ac = TAILQ_FIRST(&sc->amr_donecmds);
+ while (ac != NULL) {
+ nc = TAILQ_NEXT(ac, ac_link);
+
+ /*
+ * Is there a completion handler?
+ */
+ if (ac->ac_complete != NULL) {
+
+ /* remove and give to completion handler */
+ TAILQ_REMOVE(&sc->amr_donecmds, ac, ac_link);
+ sc->amr_donecmdcount--;
+ ac->ac_complete(ac);
+
+ /*
+ * Is someone sleeping on this one?
+ */
+ } else if (ac->ac_private != NULL) {
+
+ /* remove and wake up */
+ TAILQ_REMOVE(&sc->amr_donecmds, ac, ac_link);
+ sc->amr_donecmdcount--;
+ wakeup_one(ac->ac_private);
+
+ /*
+ * Leave it for a polling caller.
+ */
+ } else {
+ }
+ ac = nc;
+ }
+ splx(s);
+
+ /* queue more work if we can */
+ amr_startio(sc);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Command Buffer Management
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Get a new command buffer.
+ *
+ * This may return NULL in low-memory cases.
+ *
+ * Note that using malloc() is expensive (the command buffer is << 1 page) but
+ * necessary if we are to be a loadable module before the zone allocator is fixed.
+ *
+ * If possible, we recycle a command buffer that's been used before.
+ *
+ * XXX Note that command buffers are not cleaned out - it is the caller's
+ * responsibility to ensure that all required fields are filled in before
+ * using a buffer.
+ */
+static struct amr_command *
+amr_alloccmd(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ int error;
+ int s;
+
+ debug("called");
+
+ s = splbio();
+ if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL)
+ TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
+ splx(s);
+
+ /* allocate a new command buffer? */
+ if (ac == NULL) {
+ ac = (struct amr_command *)malloc(sizeof(*ac), M_DEVBUF, M_NOWAIT);
+ if (ac != NULL) {
+ bzero(ac, sizeof(*ac));
+ ac->ac_sc = sc;
+ error = bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap);
+ if (error) {
+ free(ac, M_DEVBUF);
+ return(NULL);
+ }
+ }
+ }
+ bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
+ return(ac);
+}
+
+/********************************************************************************
+ * Release a command buffer for recycling.
+ *
+ * XXX It might be a good idea to limit the number of commands we save for reuse
+ * if it's shown that this list bloats out massively.
+ */
+static void
+amr_releasecmd(struct amr_command *ac)
+{
+ int s;
+
+ debug("called");
+
+ s = splbio();
+ TAILQ_INSERT_HEAD(&ac->ac_sc->amr_freecmds, ac, ac_link);
+ splx(s);
+}
+
+/********************************************************************************
+ * Permanently discard a command buffer.
+ */
+static void
+amr_freecmd(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+
+ debug("called");
+
+ bus_dmamap_destroy(sc->amr_buffer_dmat, ac->ac_dmamap);
+ free(ac, M_DEVBUF);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Interface-specific Shims
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Tell the controller that the mailbox contains a valid command
+ */
+static void
+amr_quartz_submit_command(struct amr_softc *sc)
+{
+ debug("called");
+
+ sc->amr_mailbox->mb_poll = 0;
+ sc->amr_mailbox->mb_ack = 0;
+ /* XXX write barrier? */
+ while(AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
+ ; /* XXX aiee! what if it dies? */
+ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
+}
+
+static void
+amr_std_submit_command(struct amr_softc *sc)
+{
+ debug("called");
+
+ /* XXX write barrier? */
+ while (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
+ ; /* XXX aiee! what if it dies? */
+ AMR_SPOST_COMMAND(sc);
+}
+
+/********************************************************************************
+ * Claim any work that the controller has completed; acknowledge completion,
+ * save details of the completion in (mbsave)
+ */
+static int
+amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
+{
+ int s, worked;
+ u_int32_t outd;
+
+ debug("called");
+
+ worked = 0;
+ s = splbio();
+
+ /* work waiting for us? */
+ if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
+ AMR_QPUT_ODB(sc, AMR_QODB_READY);
+
+ /* save mailbox, which contains a list of completed commands */
+ /* XXX read barrier? */
+ bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave));
+
+ /* acknowledge that we have the commands */
+ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
+ while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
+ ; /* XXX aiee! what if it dies? */
+ worked = 1; /* got some work */
+ }
+
+ splx(s);
+ return(worked);
+}
+
+static int
+amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
+{
+ int s, worked;
+ u_int8_t istat;
+
+ debug("called");
+
+ worked = 0;
+ s = splbio();
+
+ /* check for valid interrupt status */
+ istat = AMR_SGET_ISTAT(sc);
+ if ((istat & AMR_SINTR_VALID) != 0) {
+ AMR_SPUT_ISTAT(sc, istat); /* ack interrupt status */
+
+ /* save mailbox, which contains a list of completed commands */
+ /* XXX read barrier? */
+ bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave));
+
+ AMR_SACK_INTERRUPT(sc); /* acknowledge we have the mailbox */
+ worked = 1;
+ }
+
+ splx(s);
+ return(worked);
+}
+
+/********************************************************************************
+ * Notify the controller of the mailbox location.
+ */
+static void
+amr_quartz_attach_mailbox(struct amr_softc *sc)
+{
+ /* Quartz is given the mailbox location when a command is submitted */
+}
+
+static void
+amr_std_attach_mailbox(struct amr_softc *sc)
+{
+
+ /* program the mailbox physical address */
+ AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys & 0xff);
+ AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >> 8) & 0xff);
+ AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
+ AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
+ AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
+
+ /* clear any outstanding interrupt and enable interrupts proper */
+ AMR_SACK_INTERRUPT(sc);
+ AMR_SENABLE_INTR(sc);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Debugging
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Print the command (ac) in human-readable format
+ */
+static void
+amr_printcommand(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ struct amr_sgentry *sg;
+ int i;
+
+ device_printf(sc->amr_dev, "cmd %x ident %d drive %d\n",
+ ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
+ device_printf(sc->amr_dev, "blkcount %d lba %d\n",
+ ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
+ device_printf(sc->amr_dev, "virtaddr %p length %d\n", ac->ac_data, ac->ac_length);
+ device_printf(sc->amr_dev, "physaddr %08x nsg %d\n",
+ ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
+
+ /* get base address of s/g table */
+ sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+ for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
+ device_printf(sc->amr_dev, " %x/%d\n", sg->sg_addr, sg->sg_count);
+}
diff --git a/sys/dev/amr/amr_disk.c b/sys/dev/amr/amr_disk.c
new file mode 100644
index 000000000000..958e5ad81b0c
--- /dev/null
+++ b/sys/dev/amr/amr_disk.c
@@ -0,0 +1,299 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Disk driver for AMI MegaRaid controllers
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/devicestat.h>
+#include <sys/disk.h>
+
+#include <machine/bus.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+
+#include <dev/amr/amrio.h>
+#include <dev/amr/amrreg.h>
+#include <dev/amr/amrvar.h>
+
+#if 0
+#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
+#else
+#define debug(fmt, args...)
+#endif
+
+/* prototypes */
+static int amrd_probe(device_t dev);
+static int amrd_attach(device_t dev);
+static int amrd_detach(device_t dev);
+
+static d_open_t amrd_open;
+static d_close_t amrd_close;
+static d_strategy_t amrd_strategy;
+static d_ioctl_t amrd_ioctl;
+
+#define AMRD_BDEV_MAJOR 35
+#define AMRD_CDEV_MAJOR 133
+
+static struct cdevsw amrd_cdevsw = {
+ /* open */ amrd_open,
+ /* close */ amrd_close,
+ /* read */ physread,
+ /* write */ physwrite,
+ /* ioctl */ amrd_ioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ amrd_strategy,
+ /* name */ "amrd",
+ /* maj */ AMRD_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_DISK,
+ /* bmaj */ AMRD_BDEV_MAJOR
+};
+
+static devclass_t amrd_devclass;
+static struct cdevsw amrddisk_cdevsw;
+static int disks_registered = 0;
+
+static device_method_t amrd_methods[] = {
+ DEVMETHOD(device_probe, amrd_probe),
+ DEVMETHOD(device_attach, amrd_attach),
+ DEVMETHOD(device_detach, amrd_detach),
+ { 0, 0 }
+};
+
+static driver_t amrd_driver = {
+ "amrd",
+ amrd_methods,
+ sizeof(struct amrd_softc)
+};
+
+DRIVER_MODULE(amrd, amr, amrd_driver, amrd_devclass, 0, 0);
+
+static __inline struct amrd_softc *
+amrd_getsoftc(dev_t dev)
+{
+ int unit;
+
+ unit = dkunit(dev);
+ return ((struct amrd_softc *)devclass_get_softc(amrd_devclass, unit));
+}
+
+static int
+amrd_open(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct amrd_softc *sc = amrd_getsoftc(dev);
+ struct disklabel *label;
+
+ debug("called");
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* controller not active? */
+ if (sc->amrd_controller->amr_state & AMR_STATE_SHUTDOWN)
+ return(ENXIO);
+
+ label = &sc->amrd_disk.d_label;
+ bzero(label, sizeof(*label));
+ label->d_type = DTYPE_SCSI;
+ label->d_secsize = AMR_BLKSIZE;
+ label->d_nsectors = sc->amrd_drive->al_sectors;
+ label->d_ntracks = sc->amrd_drive->al_heads;
+ label->d_ncylinders = sc->amrd_drive->al_cylinders;
+ label->d_secpercyl = sc->amrd_drive->al_sectors * sc->amrd_drive->al_heads;
+ label->d_secperunit = sc->amrd_drive->al_size;
+
+ sc->amrd_flags |= AMRD_OPEN;
+ return (0);
+}
+
+static int
+amrd_close(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct amrd_softc *sc = amrd_getsoftc(dev);
+
+ debug("called");
+
+ if (sc == NULL)
+ return (ENXIO);
+ sc->amrd_flags &= ~AMRD_OPEN;
+ return (0);
+}
+
+static int
+amrd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ struct amrd_softc *sc = amrd_getsoftc(dev);
+ int error;
+
+ debug("called");
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ if ((error = amr_submit_ioctl(sc->amrd_controller, sc->amrd_drive, cmd, addr, flag, p)) != ENOIOCTL) {
+ debug("amr_submit_ioctl returned %d\n", error);
+ return(error);
+ }
+ return (ENOTTY);
+}
+
+/*
+ * Read/write routine for a buffer. Finds the proper unit, range checks
+ * arguments, and schedules the transfer. Does not wait for the transfer
+ * to complete. Multi-page transfers are supported. All I/O requests must
+ * be a multiple of a sector in length.
+ */
+static void
+amrd_strategy(struct buf *bp)
+{
+ struct amrd_softc *sc = amrd_getsoftc(bp->b_dev);
+ int s;
+
+ debug("called");
+
+ /* bogus disk? */
+ if (sc == NULL) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+
+#if 0
+ /* XXX may only be temporarily offline - sleep? */
+ if (sc->amrd_drive->ld_state == AMR_SYSD_OFFLINE) {
+ bp->b_error = ENXIO;
+ goto bad;
+ }
+#endif
+
+ /* do-nothing operation */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /* pass reference to us */
+ bp->b_driver1 = sc;
+ s = splbio();
+ devstat_start_transaction(&sc->amrd_stats);
+ amr_submit_buf(sc->amrd_controller, bp);
+ splx(s);
+ return;
+
+ bad:
+ bp->b_flags |= B_ERROR;
+
+ done:
+ /*
+ * Correctly set the buf to indicate a completed transfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return;
+}
+
+void
+amrd_intr(void *data)
+{
+ struct buf *bp = (struct buf *)data;
+ struct amrd_softc *sc = (struct amrd_softc *)bp->b_driver1;
+
+ debug("called");
+
+ if (bp->b_flags & B_ERROR)
+ bp->b_error = EIO;
+ else
+ bp->b_resid = 0;
+
+ devstat_end_transaction_buf(&sc->amrd_stats, bp);
+ biodone(bp);
+}
+
+static int
+amrd_probe(device_t dev)
+{
+
+ debug("called");
+
+ device_set_desc(dev, "MegaRAID logical drive");
+ return (0);
+}
+
+static int
+amrd_attach(device_t dev)
+{
+ struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
+ device_t parent;
+
+ debug("called");
+
+ parent = device_get_parent(dev);
+ sc->amrd_controller = (struct amr_softc *)device_get_softc(parent);
+ sc->amrd_unit = device_get_unit(dev);
+ sc->amrd_drive = device_get_ivars(dev);
+
+ device_printf(dev, "%uMB (%u sectors), state 0x%x properties 0x%x\n",
+ sc->amrd_drive->al_size / ((1024 * 1024) / AMR_BLKSIZE),
+ sc->amrd_drive->al_size, sc->amrd_drive->al_state, sc->amrd_drive->al_properties);
+
+ devstat_add_entry(&sc->amrd_stats, "amrd", sc->amrd_unit, AMR_BLKSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER,
+ DEVSTAT_PRIORITY_DA);
+
+ disk_create(sc->amrd_unit, &sc->amrd_disk, 0, &amrd_cdevsw, &amrddisk_cdevsw);
+ disks_registered++;
+
+ return (0);
+}
+
+static int
+amrd_detach(device_t dev)
+{
+ struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
+
+ debug("called");
+
+ devstat_remove_entry(&sc->amrd_stats);
+
+ /* hack to handle lack of destroy_disk() */
+ if (--disks_registered == 0)
+ cdevsw_remove(&amrddisk_cdevsw);
+
+ return(0);
+}
+
diff --git a/sys/dev/amr/amr_pci.c b/sys/dev/amr/amr_pci.c
new file mode 100644
index 000000000000..7968a327681f
--- /dev/null
+++ b/sys/dev/amr/amr_pci.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/bus.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/devicestat.h>
+#include <sys/disk.h>
+
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include <dev/amr/amrio.h>
+#include <dev/amr/amrreg.h>
+#include <dev/amr/amrvar.h>
+
+#if 0
+#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
+#else
+#define debug(fmt, args...)
+#endif
+
+static int amr_pci_probe(device_t dev);
+static int amr_pci_attach(device_t dev);
+
+static device_method_t amr_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, amr_pci_probe),
+ DEVMETHOD(device_attach, amr_pci_attach),
+ DEVMETHOD(device_detach, amr_detach),
+ DEVMETHOD(device_shutdown, amr_shutdown),
+ DEVMETHOD(device_suspend, amr_suspend),
+ DEVMETHOD(device_resume, amr_resume),
+
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ { 0, 0 }
+};
+
+static driver_t amr_pci_driver = {
+ "amr",
+ amr_methods,
+ sizeof(struct amr_softc)
+};
+
+DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0);
+
+static struct
+{
+ int vendor;
+ int device;
+ int flag;
+#define PROBE_SIGNATURE (1<<0)
+} amr_device_ids[] = {
+ {0x101e, 0x9010, 0},
+ {0x101e, 0x9060, 0},
+ {0x8086, 0x1960, PROBE_SIGNATURE}, /* generic i960RD, check signature */
+ {0, 0, 0}
+};
+
+static int
+amr_pci_probe(device_t dev)
+{
+ int i;
+
+ debug("called");
+
+ for (i = 0; amr_device_ids[i].vendor != 0; i++) {
+ if ((pci_get_vendor(dev) == amr_device_ids[i].vendor) &&
+ (pci_get_device(dev) == amr_device_ids[i].device)) {
+
+ /* do we need to test for a signature? */
+ if ((amr_device_ids[i].flag & PROBE_SIGNATURE) &&
+ (pci_read_config(dev, AMR_CFG_SIG, 2) != AMR_SIGNATURE))
+ continue;
+ device_set_desc(dev, "AMI MegaRAID");
+ return(0);
+ }
+ }
+ return(ENXIO);
+}
+
+static int
+amr_pci_attach(device_t dev)
+{
+ struct amr_softc *sc;
+ int rid, rtype, error;
+ u_int32_t command;
+
+ debug("called");
+
+ /*
+ * Initialise softc.
+ */
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(*sc));
+ sc->amr_dev = dev;
+
+ /*
+ * Determine board type..
+ */
+ command = pci_read_config(dev, PCIR_COMMAND, 1);
+ if ((pci_get_vendor(dev) == 0x8086) && (pci_get_device(dev) == 0x1960)) {
+ sc->amr_type = AMR_TYPE_QUARTZ;
+
+ /*
+ * Make sure we are going to be able to talk to this board.
+ */
+ if ((command & PCIM_CMD_MEMEN) == 0) {
+ device_printf(dev, "memory window not available\n");
+ return(ENXIO);
+ }
+
+ } else {
+ sc->amr_type = AMR_TYPE_STD;
+
+ /*
+ * Make sure we are going to be able to talk to this board.
+ */
+ if ((command & PCIM_CMD_PORTEN) == 0) {
+ device_printf(dev, "I/O window not available\n");
+ return(ENXIO);
+ }
+ }
+
+ /*
+ * Allocate the PCI register window.
+ */
+ rid = AMR_CFG_BASE;
+ rtype = (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT;
+ sc->amr_reg = bus_alloc_resource(dev, rtype, &rid, 0, ~0, 1, RF_ACTIVE);
+ if (sc->amr_reg == NULL) {
+ device_printf(sc->amr_dev, "couldn't allocate register window\n");
+ amr_free(sc);
+ return(ENXIO);
+ }
+ sc->amr_btag = rman_get_bustag(sc->amr_reg);
+ sc->amr_bhandle = rman_get_bushandle(sc->amr_reg);
+
+ /*
+ * Allocate the parent bus DMA tag appropriate for PCI.
+ */
+ error = bus_dma_tag_create(NULL, /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ &sc->amr_parent_dmat);
+ if (error != 0) {
+ device_printf(dev, "can't allocate parent DMA tag\n");
+ amr_free(sc);
+ return(ENOMEM);
+ }
+
+ /*
+ * Do bus-independant initialisation.
+ */
+ error = amr_attach(sc);
+ if (error != 0) {
+ amr_free(sc);
+ return(error);
+ }
+
+ /*
+ * Start the controller.
+ */
+ amr_startup(sc);
+ return(0);
+}
diff --git a/sys/dev/amr/amrio.h b/sys/dev/amr/amrio.h
new file mode 100644
index 000000000000..c35a133e0abd
--- /dev/null
+++ b/sys/dev/amr/amrio.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
diff --git a/sys/dev/amr/amrreg.h b/sys/dev/amr/amrreg.h
new file mode 100644
index 000000000000..3ae84b915c22
--- /dev/null
+++ b/sys/dev/amr/amrreg.h
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Mailbox commands
+ */
+#define AMR_CMD_LREAD 0x01
+#define AMR_CMD_LWRITE 0x02
+#define AMR_CMD_ENQUIRY 0x05
+#define AMR_CMD_FLUSH 0x0a
+#define AMR_CMD_CONFIG 0xa1
+#define AMR_CONFIG_PRODINFO 0x0e
+#define AMR_CONFIG_ENQ3 0x0f
+#define AMR_CONFIG_ENQ3_SOLICITED_NOTIFY 0x01
+#define AMR_CONFIG_ENQ3_SOLICITED_FULL 0x02
+#define AMR_CONFIG_ENQ3_UNSOLICITED 0x03
+
+/*
+ * Command results
+ */
+#define AMR_STATUS_SUCCESS 0x00
+#define AMR_STATUS_ABORTED 0x02
+#define AMR_STATUS_FAILED 0x80
+
+/*
+ * Quartz doorbell registers
+ */
+#define AMR_QIDB 0x20
+#define AMR_QODB 0x2c
+#define AMR_QIDB_SUBMIT 0x00000001 /* mailbox ready for work */
+#define AMR_QIDB_ACK 0x00000002 /* mailbox done */
+#define AMR_QODB_READY 0x10001234 /* work ready to be processed */
+
+/*
+ * Standard I/O registers
+ */
+#define AMR_SCMD 0x10 /* command/ack register (write) */
+#define AMR_SMBOX_BUSY 0x10 /* mailbox status (read) */
+#define AMR_STOGGLE 0x11 /* interrupt enable bit here */
+#define AMR_SMBOX_0 0x14 /* mailbox physical address low byte */
+#define AMR_SMBOX_1 0x15
+#define AMR_SMBOX_2 0x16
+#define AMR_SMBOX_3 0x17 /* high byte */
+#define AMR_SMBOX_ENABLE 0x18 /* atomic mailbox address enable */
+#define AMR_SINTR 0x1a /* interrupt status */
+
+/*
+ * Standard I/O magic numbers
+ */
+#define AMR_SCMD_POST 0x10 /* -> SCMD to initiate action on mailbox */
+#define AMR_SCMD_ACKINTR 0x08 /* -> SCMD to ack mailbox retrieved */
+#define AMR_STOGL_IENABLE 0xc0 /* in STOGGLE */
+#define AMR_SINTR_VALID 0x40 /* in SINTR */
+#define AMR_SMBOX_BUSYFLAG 0x10 /* in SMBOX_BUSY */
+#define AMR_SMBOX_ADDR 0x00 /* -> SMBOX_ENABLE */
+
+/*
+ * Old Enquiry results
+ */
+#define AMR_8LD_MAXDRIVES 8
+#define AMR_8LD_MAXCHAN 5
+#define AMR_8LD_MAXTARG 15
+#define AMR_8LD_MAXPHYSDRIVES (AMR_8LD_MAXCHAN * AMR_8LD_MAXTARG)
+
+struct amr_adapter_info
+{
+ u_int8_t aa_maxio;
+ u_int8_t aa_rebuild_rate;
+ u_int8_t aa_maxtargchan;
+ u_int8_t aa_channels;
+ u_int8_t aa_firmware[4];
+ u_int16_t aa_flashage;
+ u_int8_t aa_chipsetvalue;
+ u_int8_t aa_memorysize;
+ u_int8_t aa_cacheflush;
+ u_int8_t aa_bios[4];
+ u_int8_t res1[7];
+} __attribute__ ((packed));
+
+struct amr_logdrive_info
+{
+ u_int8_t al_numdrives;
+ u_int8_t res1[3];
+ u_int32_t al_size[AMR_8LD_MAXDRIVES];
+ u_int8_t al_properties[AMR_8LD_MAXDRIVES];
+ u_int8_t al_state[AMR_8LD_MAXDRIVES];
+} __attribute__ ((packed));
+
+struct amr_physdrive_info
+{
+ u_int8_t ap_state[AMR_8LD_MAXPHYSDRIVES];
+ u_int8_t res1;
+} __attribute__ ((packed));
+
+struct amr_enquiry
+{
+ struct amr_adapter_info ae_adapter;
+ struct amr_logdrive_info ae_ldrv;
+ struct amr_physdrive_info ae_pdrv;
+} __attribute__ ((packed));
+
+struct amr_prodinfo
+{
+ u_int32_t ap_size; /* current size in bytes (not including resvd) */
+ u_int32_t ap_configsig; /* default is 0x00282008, indicating 0x28 maximum
+ * logical drives, 0x20 maximum stripes and 0x08
+ * maximum spans */
+ u_int8_t ap_firmware[16]; /* printable identifiers */
+ u_int8_t ap_bios[16];
+ u_int8_t ap_product[80];
+ u_int8_t ap_maxio; /* maximum number of concurrent commands supported */
+ u_int8_t ap_nschan; /* number of SCSI channels present */
+ u_int8_t ap_fcloops; /* number of fibre loops present */
+ u_int8_t ap_memtype; /* memory type */
+ u_int32_t ap_signature;
+ u_int16_t ap_memsize; /* onboard memory in MB */
+ u_int16_t ap_subsystem; /* subsystem identifier */
+ u_int16_t ap_subvendor; /* subsystem vendor ID */
+ u_int8_t ap_numnotifyctr; /* number of notify counters */
+} __attribute__((packed));
+
+#define AMR_MBOX_CMDSIZE 0x10 /* portion worth copying for controller */
+
+struct amr_mailbox
+{
+ u_int8_t mb_command;
+ u_int8_t mb_ident;
+ u_int16_t mb_blkcount;
+ u_int32_t mb_lba;
+ u_int32_t mb_physaddr;
+ u_int8_t mb_drive;
+ u_int8_t mb_nsgelem;
+ u_int8_t res1;
+ u_int8_t mb_busy;
+ u_int8_t mb_nstatus;
+ u_int8_t mb_status;
+ u_int8_t mb_completed[46];
+ u_int8_t mb_poll;
+ u_int8_t mb_ack;
+ u_int8_t res2[16];
+} __attribute__ ((packed));
+
+struct amr_mailbox64
+{
+ u_int32_t mb64_segment; /* for 64-bit controllers */
+ struct amr_mailbox mb;
+} __attribute__ ((packed));
+
+struct amr_mailbox_ioctl
+{
+ u_int8_t mb_command;
+ u_int8_t mb_ident;
+ u_int8_t mb_channel;
+ u_int8_t mb_param;
+ u_int8_t res1[4];
+ u_int32_t mb_physaddr;
+ u_int8_t mb_drive;
+ u_int8_t mb_nsgelem;
+ u_int8_t res2;
+ u_int8_t mb_busy;
+ u_int8_t mb_nstatus;
+ u_int8_t mb_completed[46];
+ u_int8_t mb_poll;
+ u_int8_t mb_ack;
+ u_int8_t res3[16];
+} __attribute__ ((packed));
+
+struct amr_sgentry
+{
+ u_int32_t sg_addr;
+ u_int32_t sg_count;
+} __attribute__ ((packed));
+
+
diff --git a/sys/dev/amr/amrvar.h b/sys/dev/amr/amrvar.h
new file mode 100644
index 000000000000..c35d5db2e910
--- /dev/null
+++ b/sys/dev/amr/amrvar.h
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * We could actually use all 17 segments, but using only 16 means that
+ * each scatter/gather map is 128 bytes in size, and thus we don't have to worry about
+ * maps crossing page boundaries.
+ */
+#define AMR_NSEG 16
+
+#define AMR_CFG_BASE 0x10
+#define AMR_CFG_SIG 0xa0
+#define AMR_SIGNATURE 0x3344
+
+#define AMR_MAXCMD 255 /* ident = 0 not allowed */
+#define AMR_MAXLD 40
+
+#define AMR_BLKSIZE 512
+
+struct amr_softc;
+
+/*
+ * Per-logical-drive datastructure
+ */
+struct amr_logdrive
+{
+ u_int32_t al_size;
+ int al_state;
+ int al_properties;
+
+ /* synthetic geometry */
+ int al_cylinders;
+ int al_heads;
+ int al_sectors;
+
+ /* driver */
+ device_t al_disk;
+};
+
+
+/*
+ * Per-command control structure.
+ */
+struct amr_command
+{
+ TAILQ_ENTRY(amr_command) ac_link;
+
+ struct amr_softc *ac_sc;
+ u_int8_t ac_slot;
+ int ac_status;
+#define AMR_STATUS_BUSY 0xffff
+#define AMR_STATUS_WEDGED 0xdead
+ struct amr_mailbox ac_mailbox;
+ u_int32_t ac_sgphys;
+ int ac_nsgent;
+ int ac_flags;
+#define AMR_CMD_DATAIN (1<<0)
+#define AMR_CMD_DATAOUT (1<<1)
+#define AMR_CMD_PRIORITY (1<<2)
+ time_t ac_stamp;
+
+ void *ac_data;
+ size_t ac_length;
+ bus_dmamap_t ac_dmamap;
+ u_int32_t ac_dataphys;
+
+ void (* ac_complete)(struct amr_command *ac);
+ void *ac_private;
+};
+
+struct amr_softc
+{
+ /* bus attachments */
+ device_t amr_dev;
+ struct resource *amr_reg; /* control registers */
+ bus_space_handle_t amr_bhandle;
+ bus_space_tag_t amr_btag;
+ bus_dma_tag_t amr_parent_dmat; /* parent DMA tag */
+ bus_dma_tag_t amr_buffer_dmat; /* data buffer DMA tag */
+ struct resource *amr_irq; /* interrupt */
+ void *amr_intr;
+
+ /* mailbox */
+ struct amr_mailbox *amr_mailbox;
+ struct amr_mailbox64 *amr_mailbox64;
+ u_int32_t amr_mailboxphys;
+ bus_dma_tag_t amr_mailbox_dmat;
+ bus_dmamap_t amr_mailbox_dmamap;
+
+ /* scatter/gather lists and their controller-visible mappings */
+ struct amr_sgentry *amr_sgtable; /* s/g lists */
+ u_int32_t amr_sgbusaddr; /* s/g table base address in bus space */
+ bus_dma_tag_t amr_sg_dmat; /* s/g buffer DMA tag */
+ bus_dmamap_t amr_sg_dmamap; /* map for s/g buffers */
+
+ /* controller limits and features */
+ int amr_maxio; /* maximum number of I/O transactions */
+ int amr_maxdrives; /* max number of logical drives */
+
+ /* connected logical drives */
+ struct amr_logdrive amr_drive[AMR_MAXLD];
+
+ /* controller status */
+ int amr_state;
+#define AMR_STATE_OPEN (1<<0)
+#define AMR_STATE_SUSPEND (1<<1)
+#define AMR_STATE_INTEN (1<<2)
+#define AMR_STATE_SHUTDOWN (1<<3)
+
+ /* per-controller queues */
+ struct buf_queue_head amr_bufq; /* pending I/O */
+ int amr_waitbufs;
+ struct amr_command *amr_busycmd[AMR_MAXCMD];
+ int amr_busycmdcount;
+ TAILQ_HEAD(,amr_command) amr_donecmds;
+ int amr_donecmdcount;
+ TAILQ_HEAD(,amr_command) amr_freecmds;
+
+ /* controller type-specific support */
+ int amr_type;
+#define AMR_TYPE_STD 0
+#define AMR_TYPE_QUARTZ 1
+ void (* amr_submit_command)(struct amr_softc *sc);
+ int (* amr_get_work)(struct amr_softc *sc, struct amr_mailbox *mbsave);
+ void (* amr_attach_mailbox)(struct amr_softc *sc);
+};
+
+/*
+ * I/O primitives
+ */
+/* Quartz */
+#define AMR_QPUT_IDB(sc, val) bus_space_write_4(sc->amr_btag, sc->amr_bhandle, AMR_QIDB, val)
+#define AMR_QGET_IDB(sc) bus_space_read_4 (sc->amr_btag, sc->amr_bhandle, AMR_QIDB)
+#define AMR_QPUT_ODB(sc, val) bus_space_write_4(sc->amr_btag, sc->amr_bhandle, AMR_QODB, val)
+#define AMR_QGET_ODB(sc) bus_space_read_4 (sc->amr_btag, sc->amr_bhandle, AMR_QODB)
+
+/* Standard */
+#define AMR_SPUT_ISTAT(sc, val) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_SINTR, val)
+#define AMR_SGET_ISTAT(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SINTR)
+#define AMR_SACK_INTERRUPT(sc) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_SCMD, AMR_SCMD_ACKINTR)
+#define AMR_SPOST_COMMAND(sc) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_SCMD, AMR_SCMD_POST)
+#define AMR_SGET_MBSTAT(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_BUSY)
+#define AMR_SENABLE_INTR(sc) \
+ bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE, \
+ bus_space_read_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE) | AMR_STOGL_IENABLE)
+#define AMR_SDISABLE_INTR(sc) \
+ bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE, \
+ bus_space_read_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE) & ~AMR_STOGL_IENABLE)
+#define AMR_SBYTE_SET(sc, reg, val) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, reg, val)
+
+/*
+ * Interface between bus connections and driver core.
+ */
+extern void amr_free(struct amr_softc *sc);
+extern int amr_attach(struct amr_softc *sc);
+extern void amr_startup(struct amr_softc *sc);
+extern void amr_intr(void *data);
+extern int amr_detach(device_t dev);
+extern int amr_shutdown(device_t dev);
+extern int amr_suspend(device_t dev);
+extern int amr_resume(device_t dev);
+extern d_open_t amr_open;
+extern d_close_t amr_close;
+extern d_ioctl_t amr_ioctl;
+
+extern devclass_t amr_devclass;
+
+/*
+ * MegaRAID logical disk driver
+ */
+struct amrd_softc
+{
+ device_t amrd_dev;
+ struct amr_softc *amrd_controller;
+ struct amr_logdrive *amrd_drive;
+ struct disk amrd_disk;
+ struct devstat amrd_stats;
+ struct disklabel amrd_label;
+ int amrd_unit;
+ int amrd_flags;
+#define AMRD_OPEN (1<<0) /* drive is open (can't shut down) */
+};
+
+/*
+ * Interface between driver core and disk driver (should be using a bus?)
+ */
+extern int amr_submit_buf(struct amr_softc *sc, struct buf *bp);
+extern int amr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd,
+ caddr_t addr, int32_t flag, struct proc *p);
+extern void amrd_intr(void *data);
+