summaryrefslogtreecommitdiffstats
path: root/sys/dev/amr
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/amr')
-rw-r--r--sys/dev/amr/amr.c1808
-rw-r--r--sys/dev/amr/amr_cam.c614
-rw-r--r--sys/dev/amr/amr_compat.h108
-rw-r--r--sys/dev/amr/amr_disk.c253
-rw-r--r--sys/dev/amr/amr_pci.c604
-rw-r--r--sys/dev/amr/amr_tables.h139
-rw-r--r--sys/dev/amr/amrio.h109
-rw-r--r--sys/dev/amr/amrreg.h606
-rw-r--r--sys/dev/amr/amrvar.h369
9 files changed, 4610 insertions, 0 deletions
diff --git a/sys/dev/amr/amr.c b/sys/dev/amr/amr.c
new file mode 100644
index 0000000..29e0a30
--- /dev/null
+++ b/sys/dev/amr/amr.c
@@ -0,0 +1,1808 @@
+/*-
+ * Copyright (c) 1999,2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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 <dev/amr/amr_compat.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/stat.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>
+#define AMR_DEFINE_TABLES
+#include <dev/amr/amr_tables.h>
+
+#define AMR_CDEV_MAJOR 132
+
+static d_open_t amr_open;
+static d_close_t amr_close;
+static d_ioctl_t amr_ioctl;
+
+static struct cdevsw amr_cdevsw = {
+ .d_open = amr_open,
+ .d_close = amr_close,
+ .d_ioctl = amr_ioctl,
+ .d_name = "amr",
+ .d_maj = AMR_CDEV_MAJOR,
+};
+
+/*
+ * Initialisation, bus interface.
+ */
+static void amr_startup(void *arg);
+
+/*
+ * 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 void amr_completeio(struct amr_command *ac);
+static int amr_support_ext_cdb(struct amr_softc *sc);
+
+/*
+ * Command buffer allocation.
+ */
+static void amr_alloccmd_cluster(struct amr_softc *sc);
+static void amr_freecmd_cluster(struct amr_command_cluster *acc);
+
+/*
+ * Command processing.
+ */
+static int amr_bio_command(struct amr_softc *sc, struct amr_command **acp);
+static int amr_wait_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 void amr_complete(void *context, int pending);
+
+/*
+ * Status monitoring
+ */
+static void amr_periodic(void *data);
+
+/*
+ * Interface-specific shims
+ */
+static int amr_quartz_submit_command(struct amr_softc *sc);
+static int amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
+static int amr_quartz_poll_command(struct amr_command *ac);
+
+static int amr_std_submit_command(struct amr_softc *sc);
+static int amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
+static int amr_std_poll_command(struct amr_command *ac);
+static void amr_std_attach_mailbox(struct amr_softc *sc);
+
+#ifdef AMR_BOARD_INIT
+static int amr_quartz_init(struct amr_softc *sc);
+static int amr_std_init(struct amr_softc *sc);
+#endif
+
+/*
+ * Debugging
+ */
+static void amr_describe_controller(struct amr_softc *sc);
+#ifdef AMR_DEBUG
+#if 0
+static void amr_printcommand(struct amr_command *ac);
+#endif
+#endif
+
+/********************************************************************************
+ ********************************************************************************
+ Inline Glue
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ ********************************************************************************
+ Public Interfaces
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Initialise the controller and softc.
+ */
+int
+amr_attach(struct amr_softc *sc)
+{
+
+ debug_called(1);
+
+ /*
+ * Initialise per-controller queues.
+ */
+ TAILQ_INIT(&sc->amr_completed);
+ TAILQ_INIT(&sc->amr_freecmds);
+ TAILQ_INIT(&sc->amr_cmd_clusters);
+ TAILQ_INIT(&sc->amr_ready);
+ bioq_init(&sc->amr_bioq);
+
+#if __FreeBSD_version >= 500005
+ /*
+ * Initialise command-completion task.
+ */
+ TASK_INIT(&sc->amr_task_complete, 0, amr_complete, sc);
+#endif
+
+ debug(2, "queue init done");
+
+ /*
+ * Configure for this controller type.
+ */
+ if (AMR_IS_QUARTZ(sc)) {
+ sc->amr_submit_command = amr_quartz_submit_command;
+ sc->amr_get_work = amr_quartz_get_work;
+ sc->amr_poll_command = amr_quartz_poll_command;
+ } else {
+ sc->amr_submit_command = amr_std_submit_command;
+ sc->amr_get_work = amr_std_get_work;
+ sc->amr_poll_command = amr_std_poll_command;
+ amr_std_attach_mailbox(sc);;
+ }
+
+#ifdef AMR_BOARD_INIT
+ if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc))))
+ return(ENXIO);
+#endif
+
+ /*
+ * Quiz controller for features and limits.
+ */
+ if (amr_query_controller(sc))
+ return(ENXIO);
+
+ debug(2, "controller query complete");
+
+ /*
+ * Attach our 'real' SCSI channels to CAM.
+ */
+ if (amr_cam_attach(sc))
+ return(ENXIO);
+ debug(2, "CAM attach done");
+
+ /*
+ * Create the control device.
+ */
+ sc->amr_dev_t = make_dev(&amr_cdevsw, device_get_unit(sc->amr_dev), UID_ROOT, GID_OPERATOR,
+ S_IRUSR | S_IWUSR, "amr%d", device_get_unit(sc->amr_dev));
+ sc->amr_dev_t->si_drv1 = sc;
+
+ /*
+ * Schedule ourselves to bring the controller up once interrupts are
+ * available.
+ */
+ bzero(&sc->amr_ich, sizeof(struct intr_config_hook));
+ sc->amr_ich.ich_func = amr_startup;
+ sc->amr_ich.ich_arg = sc;
+ if (config_intrhook_establish(&sc->amr_ich) != 0) {
+ device_printf(sc->amr_dev, "can't establish configuration hook\n");
+ return(ENOMEM);
+ }
+
+ /*
+ * Print a little information about the controller.
+ */
+ amr_describe_controller(sc);
+
+ debug(2, "attach complete");
+ return(0);
+}
+
+/********************************************************************************
+ * Locate disk resources and attach children to them.
+ */
+static void
+amr_startup(void *arg)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+ struct amr_logdrive *dr;
+ int i, error;
+
+ debug_called(1);
+
+ /* pull ourselves off the intrhook chain */
+ config_intrhook_disestablish(&sc->amr_ich);
+
+ /* get up-to-date drive information */
+ if (amr_query_controller(sc)) {
+ device_printf(sc->amr_dev, "can'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);
+ if (dr->al_disk == 0)
+ device_printf(sc->amr_dev, "device_add_child failed\n");
+ device_set_ivars(dr->al_disk, dr);
+ }
+ }
+
+ 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;
+
+ /*
+ * Start the timeout routine.
+ */
+/* sc->amr_timeout = timeout(amr_periodic, sc, hz);*/
+
+ return;
+}
+
+/*******************************************************************************
+ * Free resources associated with a controller instance
+ */
+void
+amr_free(struct amr_softc *sc)
+{
+ struct amr_command_cluster *acc;
+
+ /* detach from CAM */
+ amr_cam_detach(sc);
+
+ /* cancel status timeout */
+ untimeout(amr_periodic, sc, sc->amr_timeout);
+
+ /* throw away any command buffers */
+ while ((acc = TAILQ_FIRST(&sc->amr_cmd_clusters)) != NULL) {
+ TAILQ_REMOVE(&sc->amr_cmd_clusters, acc, acc_link);
+ amr_freecmd_cluster(acc);
+ }
+
+ /* destroy control device */
+ if( sc->amr_dev_t != (dev_t)NULL)
+ destroy_dev(sc->amr_dev_t);
+}
+
+/*******************************************************************************
+ * Receive a bio 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_bio(struct amr_softc *sc, struct bio *bio)
+{
+ debug_called(2);
+
+ amr_enqueue_bio(sc, bio);
+ amr_startio(sc);
+ return(0);
+}
+
+/********************************************************************************
+ * Accept an open operation on the control device.
+ */
+static int
+amr_open(dev_t dev, int flags, int fmt, d_thread_t *td)
+{
+ int unit = minor(dev);
+ struct amr_softc *sc = devclass_get_softc(devclass_find("amr"), unit);
+
+ debug_called(1);
+
+ sc->amr_state |= AMR_STATE_OPEN;
+ return(0);
+}
+
+/********************************************************************************
+ * Accept the last close on the control device.
+ */
+static int
+amr_close(dev_t dev, int flags, int fmt, d_thread_t *td)
+{
+ int unit = minor(dev);
+ struct amr_softc *sc = devclass_get_softc(devclass_find("amr"), unit);
+
+ debug_called(1);
+
+ sc->amr_state &= ~AMR_STATE_OPEN;
+ return (0);
+}
+
+/********************************************************************************
+ * Handle controller-specific control operations.
+ */
+static int
+amr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t *td)
+{
+ struct amr_softc *sc = (struct amr_softc *)dev->si_drv1;
+ int *arg = (int *)addr;
+ struct amr_user_ioctl *au = (struct amr_user_ioctl *)addr;
+ struct amr_command *ac;
+ struct amr_mailbox_ioctl *mbi;
+ struct amr_passthrough *ap;
+ void *dp;
+ int error;
+
+ debug_called(1);
+
+ error = 0;
+ dp = NULL;
+ ap = NULL;
+ ac = NULL;
+ switch(cmd) {
+
+ case AMR_IO_VERSION:
+ debug(1, "AMR_IO_VERSION");
+ *arg = AMR_IO_VERSION_NUMBER;
+ break;
+
+ case AMR_IO_COMMAND:
+ debug(1, "AMR_IO_COMMAND 0x%x", au->au_cmd[0]);
+ /* handle inbound data buffer */
+ if (au->au_length != 0) {
+ if ((dp = malloc(au->au_length, M_DEVBUF, M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ if ((error = copyin(au->au_buffer, dp, au->au_length)) != 0)
+ break;
+ debug(2, "copyin %ld bytes from %p -> %p", au->au_length, au->au_buffer, dp);
+ }
+
+ if ((ac = amr_alloccmd(sc)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ /* handle SCSI passthrough command */
+ if (au->au_cmd[0] == AMR_CMD_PASS) {
+ if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ /* copy cdb */
+ ap->ap_cdb_length = au->au_cmd[2];
+ bcopy(&au->au_cmd[3], &ap->ap_cdb[0], ap->ap_cdb_length);
+
+ /* build passthrough */
+ ap->ap_timeout = au->au_cmd[ap->ap_cdb_length + 3] & 0x07;
+ ap->ap_ars = (au->au_cmd[ap->ap_cdb_length + 3] & 0x08) ? 1 : 0;
+ ap->ap_islogical = (au->au_cmd[ap->ap_cdb_length + 3] & 0x80) ? 1 : 0;
+ ap->ap_logical_drive_no = au->au_cmd[ap->ap_cdb_length + 4];
+ ap->ap_channel = au->au_cmd[ap->ap_cdb_length + 5];
+ ap->ap_scsi_id = au->au_cmd[ap->ap_cdb_length + 6];
+ ap->ap_request_sense_length = 14;
+ ap->ap_data_transfer_length = au->au_length;
+ /* XXX what about the request-sense area? does the caller want it? */
+
+ /* build command */
+ ac->ac_data = ap;
+ ac->ac_length = sizeof(*ap);
+ ac->ac_flags |= AMR_CMD_DATAOUT;
+ ac->ac_ccb_data = dp;
+ ac->ac_ccb_length = au->au_length;
+ if (au->au_direction & AMR_IO_READ)
+ ac->ac_flags |= AMR_CMD_CCB_DATAIN;
+ if (au->au_direction & AMR_IO_WRITE)
+ ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
+
+ ac->ac_mailbox.mb_command = AMR_CMD_PASS;
+
+ } else {
+ /* direct command to controller */
+ mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
+
+ /* copy pertinent mailbox items */
+ mbi->mb_command = au->au_cmd[0];
+ mbi->mb_channel = au->au_cmd[1];
+ mbi->mb_param = au->au_cmd[2];
+ mbi->mb_pad[0] = au->au_cmd[3];
+ mbi->mb_drive = au->au_cmd[4];
+
+ /* build the command */
+ ac->ac_data = dp;
+ ac->ac_length = au->au_length;
+ if (au->au_direction & AMR_IO_READ)
+ ac->ac_flags |= AMR_CMD_DATAIN;
+ if (au->au_direction & AMR_IO_WRITE)
+ ac->ac_flags |= AMR_CMD_DATAOUT;
+ }
+
+ /* run the command */
+ if ((error = amr_wait_command(ac)) != 0)
+ break;
+
+ /* copy out data and set status */
+ if (au->au_length != 0)
+ error = copyout(dp, au->au_buffer, au->au_length);
+ debug(2, "copyout %ld bytes from %p -> %p", au->au_length, dp, au->au_buffer);
+ if (dp != NULL)
+ debug(2, "%16d", (int)dp);
+ au->au_status = ac->ac_status;
+ break;
+
+ default:
+ debug(1, "unknown ioctl 0x%lx", cmd);
+ error = ENOIOCTL;
+ break;
+ }
+
+ if (dp != NULL)
+ free(dp, M_DEVBUF);
+ if (ap != NULL)
+ free(ap, M_DEVBUF);
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ return(error);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Status Monitoring
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Perform a periodic check of the controller status
+ */
+static void
+amr_periodic(void *data)
+{
+ struct amr_softc *sc = (struct amr_softc *)data;
+
+ debug_called(2);
+
+ /* XXX perform periodic status checks here */
+
+ /* compensate for missed interrupts */
+ amr_done(sc);
+
+ /* reschedule */
+ sc->amr_timeout = timeout(amr_periodic, sc, hz);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Command Wrappers
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Interrogate the controller for the operational parameters we require.
+ */
+static int
+amr_query_controller(struct amr_softc *sc)
+{
+ struct amr_enquiry3 *aex;
+ struct amr_prodinfo *ap;
+ struct amr_enquiry *ae;
+ int ldrv;
+
+ /*
+ * If we haven't found the real limit yet, let us have a couple of commands in
+ * order to be able to probe.
+ */
+ if (sc->amr_maxio == 0)
+ sc->amr_maxio = 2;
+
+ /*
+ * Greater than 10 byte cdb support
+ */
+ sc->support_ext_cdb = amr_support_ext_cdb(sc);
+
+ if(sc->support_ext_cdb) {
+ debug(2,"supports extended CDBs.");
+ }
+
+ /*
+ * Try to issue an ENQUIRY3 command
+ */
+ if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
+ AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) {
+
+ /*
+ * Fetch current state of logical drives.
+ */
+ for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) {
+ sc->amr_drive[ldrv].al_size = aex->ae_drivesize[ldrv];
+ sc->amr_drive[ldrv].al_state = aex->ae_drivestate[ldrv];
+ sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[ldrv];
+ debug(2, " drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
+ sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
+ }
+ free(aex, M_DEVBUF);
+
+ /*
+ * Get product info for channel count.
+ */
+ if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) {
+ device_printf(sc->amr_dev, "can't obtain product data from controller\n");
+ return(1);
+ }
+ sc->amr_maxdrives = 40;
+ sc->amr_maxchan = ap->ap_nschan;
+ sc->amr_maxio = ap->ap_maxio;
+ sc->amr_type |= AMR_TYPE_40LD;
+ free(ap, M_DEVBUF);
+
+ } else {
+
+ /* failed, try the 8LD ENQUIRY commands */
+ if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) {
+ if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
+ device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
+ return(1);
+ }
+ ae->ae_signature = 0;
+ }
+
+ /*
+ * Fetch current state of logical drives.
+ */
+ for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) {
+ sc->amr_drive[ldrv].al_size = ae->ae_ldrv.al_size[ldrv];
+ sc->amr_drive[ldrv].al_state = ae->ae_ldrv.al_state[ldrv];
+ sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv];
+ debug(2, " drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
+ sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
+ }
+
+ sc->amr_maxdrives = 8;
+ sc->amr_maxchan = ae->ae_adapter.aa_channels;
+ sc->amr_maxio = ae->ae_adapter.aa_maxio;
+ free(ae, M_DEVBUF);
+ }
+
+ /*
+ * Mark remaining drives as unused.
+ */
+ for (; ldrv < AMR_MAXLD; ldrv++)
+ sc->amr_drive[ldrv].al_size = 0xffffffff;
+
+ /*
+ * Cap the maximum number of outstanding I/Os. AMI's Linux driver doesn't trust
+ * the controller's reported value, and lockups have been seen when we do.
+ */
+ sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD);
+
+ 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(1);
+
+ 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;
+ /* set command flags */
+ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
+
+ /* point the command at our data */
+ ac->ac_data = result;
+ ac->ac_length = bufsize;
+
+ /* 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;
+
+ /* can't assume that interrupts are going to work here, so play it safe */
+ if (sc->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.
+ */
+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;
+ /* set command flags */
+ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
+
+ /* build the command proper */
+ ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
+
+ /* we have to poll, as the system may be going down or otherwise damaged */
+ if (sc->amr_poll_command(ac))
+ goto out;
+ error = ac->ac_status;
+
+ out:
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ return(error);
+}
+
+/********************************************************************************
+ * Detect extented cdb >> greater than 10 byte cdb support
+ * returns '1' means this support exist
+ * returns '0' means this support doesn't exist
+ */
+static int
+amr_support_ext_cdb(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ u_int8_t *mbox;
+ int error;
+
+ /* get ourselves a command buffer */
+ error = 0;
+ if ((ac = amr_alloccmd(sc)) == NULL)
+ goto out;
+ /* set command flags */
+ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
+
+ /* build the command proper */
+ mbox = (u_int8_t *)&ac->ac_mailbox; /* XXX want a real structure for this? */
+ mbox[0] = 0xA4;
+ mbox[2] = 0x16;
+
+
+ /* we have to poll, as the system may be going down or otherwise damaged */
+ if (sc->amr_poll_command(ac))
+ goto out;
+ if( ac->ac_status == AMR_STATUS_SUCCESS ) {
+ error = 1;
+ }
+
+out:
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ return(error);
+}
+
+/********************************************************************************
+ * Try to find I/O work for the controller from one or more of the work queues.
+ *
+ * We make the assumption that if the controller is not ready to take a command
+ * at some given time, it will generate an interrupt at some later time when
+ * it is.
+ */
+void
+amr_startio(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+
+ /* spin until something prevents us from doing any work */
+ for (;;) {
+
+ /* try to get a ready command */
+ ac = amr_dequeue_ready(sc);
+
+ /* if that failed, build a command from a bio */
+ if (ac == NULL)
+ (void)amr_bio_command(sc, &ac);
+
+ /* if that failed, build a command from a ccb */
+ if (ac == NULL)
+ (void)amr_cam_command(sc, &ac);
+
+ /* if we don't have anything to do, give up */
+ if (ac == NULL)
+ break;
+
+ /* try to give the command to the controller; if this fails save it for later and give up */
+ if (amr_start(ac)) {
+ debug(2, "controller busy, command deferred");
+ amr_requeue_ready(ac); /* XXX schedule retry very soon? */
+ break;
+ }
+ }
+}
+
+/********************************************************************************
+ * Handle completion of an I/O command.
+ */
+static void
+amr_completeio(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+
+ if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */
+ ac->ac_bio->bio_error = EIO;
+ ac->ac_bio->bio_flags |= BIO_ERROR;
+
+ device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status);
+/* amr_printcommand(ac);*/
+ }
+ amrd_intr(ac->ac_bio);
+ amr_releasecmd(ac);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Command Processing
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Convert a bio off the top of the bio queue into a command.
+ */
+static int
+amr_bio_command(struct amr_softc *sc, struct amr_command **acp)
+{
+ struct amr_command *ac;
+ struct amrd_softc *amrd;
+ struct bio *bio;
+ int error;
+ int blkcount;
+ int driveno;
+ int cmd;
+
+ ac = NULL;
+ error = 0;
+
+ /* get a bio to work on */
+ if ((bio = amr_dequeue_bio(sc)) == NULL)
+ goto out;
+
+ /* get a command */
+ if ((ac = amr_alloccmd(sc)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* connect the bio to the command */
+ ac->ac_complete = amr_completeio;
+ ac->ac_bio = bio;
+ ac->ac_data = bio->bio_data;
+ ac->ac_length = bio->bio_bcount;
+ if (BIO_IS_READ(bio)) {
+ ac->ac_flags |= AMR_CMD_DATAIN;
+ cmd = AMR_CMD_LREAD;
+ } else {
+ ac->ac_flags |= AMR_CMD_DATAOUT;
+ cmd = AMR_CMD_LWRITE;
+ }
+ amrd = (struct amrd_softc *)bio->bio_disk->d_drv1;
+ driveno = amrd->amrd_drive - sc->amr_drive;
+ blkcount = (bio->bio_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
+
+ ac->ac_mailbox.mb_command = cmd;
+ ac->ac_mailbox.mb_blkcount = blkcount;
+ ac->ac_mailbox.mb_lba = bio->bio_pblkno;
+ ac->ac_mailbox.mb_drive = driveno;
+ /* we fill in the s/g related data when the command is mapped */
+
+ if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
+ device_printf(sc->amr_dev, "I/O beyond end of unit (%lld,%d > %lu)\n",
+ (long long)bio->bio_pblkno, blkcount,
+ (u_long)sc->amr_drive[driveno].al_size);
+
+out:
+ if (error != 0) {
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ if (bio != NULL) /* this breaks ordering... */
+ amr_enqueue_bio(sc, bio);
+ }
+ *acp = ac;
+ return(error);
+}
+
+/********************************************************************************
+ * 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)
+{
+ int error, count;
+
+ debug_called(1);
+
+ ac->ac_complete = NULL;
+ ac->ac_flags |= AMR_CMD_SLEEP;
+ if ((error = amr_start(ac)) != 0)
+ return(error);
+
+ count = 0;
+ /* XXX better timeout? */
+ while ((ac->ac_flags & AMR_CMD_BUSY) && (count < 30)) {
+ tsleep(ac, PRIBIO | PCATCH, "amrwcmd", hz);
+ }
+ 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_std_poll_command(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int error, count;
+
+ debug_called(2);
+
+ ac->ac_complete = 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);
+ DELAY(1000);
+ } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000));
+ if (!(ac->ac_flags & AMR_CMD_BUSY)) {
+ error = 0;
+ } else {
+ /* XXX the slot is now marked permanently busy */
+ error = EIO;
+ device_printf(sc->amr_dev, "polled command timeout\n");
+ }
+ return(error);
+}
+
+/********************************************************************************
+ * 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_quartz_poll_command(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int s;
+ int error,count;
+
+ debug_called(2);
+
+ /* now we have a slot, we can map the command (unmapped in amr_complete) */
+ amr_mapcmd(ac);
+
+ s = splbio();
+
+ count=0;
+ while (sc->amr_busyslots){
+ tsleep(sc, PRIBIO | PCATCH, "amrpoll", hz);
+ if(count++>10) {
+ break;
+ }
+ }
+
+ if(sc->amr_busyslots) {
+ device_printf(sc->amr_dev, "adapter is busy\n");
+ splx(s);
+ amr_unmapcmd(ac);
+ ac->ac_status=0;
+ return(1);
+ }
+
+ bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
+
+ /* clear the poll/ack fields in the mailbox */
+ sc->amr_mailbox->mb_ident = 0xFE;
+ sc->amr_mailbox->mb_nstatus = 0xFF;
+ sc->amr_mailbox->mb_status = 0xFF;
+ sc->amr_mailbox->mb_poll = 0;
+ sc->amr_mailbox->mb_ack = 0;
+ sc->amr_mailbox->mb_busy = 1;
+
+ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
+
+ while(sc->amr_mailbox->mb_nstatus == 0xFF);
+ while(sc->amr_mailbox->mb_status == 0xFF);
+ ac->ac_status=sc->amr_mailbox->mb_status;
+ error = (ac->ac_status !=AMR_STATUS_SUCCESS) ? 1:0;
+ while(sc->amr_mailbox->mb_poll != 0x77);
+ sc->amr_mailbox->mb_poll = 0;
+ sc->amr_mailbox->mb_ack = 0x77;
+
+ /* acknowledge that we have the commands */
+ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
+ while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK);
+
+ splx(s);
+
+ /* unmap the command's data buffer */
+ amr_unmapcmd(ac);
+
+ return(error);
+}
+
+/********************************************************************************
+ * Get a free command slot for a command if it doesn't already have one.
+ *
+ * May be safely called multiple times for a given command.
+ */
+static int
+amr_getslot(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int s, slot, limit, error;
+
+ debug_called(3);
+
+ /* if the command already has a slot, don't try to give it another one */
+ if (ac->ac_slot != 0)
+ return(0);
+
+ /* enforce slot usage limit */
+ limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
+ if (sc->amr_busyslots > limit)
+ return(EBUSY);
+
+ /*
+ * Allocate a slot. XXX linear scan is slow
+ */
+ error = EBUSY;
+ s = splbio();
+ for (slot = 0; slot < sc->amr_maxio; slot++) {
+ if (sc->amr_busycmd[slot] == NULL) {
+ sc->amr_busycmd[slot] = ac;
+ sc->amr_busyslots++;
+ ac->ac_slot = slot;
+ error = 0;
+ break;
+ }
+ }
+ splx(s);
+
+ return(error);
+}
+
+/********************************************************************************
+ * Map/unmap (ac)'s data in the controller's addressable space as required.
+ *
+ * These functions may be safely called multiple times on a given command.
+ */
+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;
+ u_int8_t *sgc;
+
+ debug_called(3);
+
+ /* get base address of s/g table */
+ sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+
+ /* save data physical address */
+ ac->ac_dataphys = segs[0].ds_addr;
+
+ /* for AMR_CMD_CONFIG the s/g count goes elsewhere */
+ if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) {
+ sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param);
+ } else {
+ sgc = &ac->ac_mailbox.mb_nsgelem;
+ }
+
+ /* decide whether we need to populate the s/g table */
+ if (nsegments < 2) {
+ *sgc = 0;
+ ac->ac_mailbox.mb_nsgelem = 0;
+ ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
+ } else {
+ ac->ac_mailbox.mb_nsgelem = nsegments;
+ *sgc = nsegments;
+ ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
+ for (i = 0; i < nsegments; i++, sg++) {
+ sg->sg_addr = segs[i].ds_addr;
+ sg->sg_count = segs[i].ds_len;
+ }
+ }
+}
+
+static void
+amr_setup_ccbmap(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;
+ struct amr_passthrough *ap = (struct amr_passthrough *)ac->ac_data;
+ struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data;
+ int i;
+
+ /* get base address of s/g table */
+ sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+
+ /* decide whether we need to populate the s/g table */
+ if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) {
+ if (nsegments < 2) {
+ aep->ap_no_sg_elements = 0;
+ aep->ap_data_transfer_address = segs[0].ds_addr;
+ } else {
+ /* save s/g table information in passthrough */
+ aep->ap_no_sg_elements = nsegments;
+ aep->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
+ /* populate s/g table (overwrites previous call which mapped the passthrough) */
+ for (i = 0; i < nsegments; i++, sg++) {
+ sg->sg_addr = segs[i].ds_addr;
+ sg->sg_count = segs[i].ds_len;
+ debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
+ }
+ }
+ debug(3, "slot %d %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
+ aep->ap_no_sg_elements, aep->ap_data_transfer_address, ac->ac_dataphys);
+ } else {
+ if (nsegments < 2) {
+ ap->ap_no_sg_elements = 0;
+ ap->ap_data_transfer_address = segs[0].ds_addr;
+ } else {
+ /* save s/g table information in passthrough */
+ ap->ap_no_sg_elements = nsegments;
+ ap->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
+ /* populate s/g table (overwrites previous call which mapped the passthrough) */
+ for (i = 0; i < nsegments; i++, sg++) {
+ sg->sg_addr = segs[i].ds_addr;
+ sg->sg_count = segs[i].ds_len;
+ debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
+ }
+ }
+ debug(3, "slot %d %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
+ ap->ap_no_sg_elements, ap->ap_data_transfer_address, ac->ac_dataphys);
+ }
+}
+
+static void
+amr_mapcmd(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+
+ debug_called(3);
+
+ /* if the command involves data at all, and hasn't been mapped */
+ if (!(ac->ac_flags & AMR_CMD_MAPPED)) {
+
+ if (ac->ac_data != NULL) {
+ /* map the data buffers 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);
+ }
+
+ if (ac->ac_ccb_data != NULL) {
+ bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, ac->ac_ccb_data, ac->ac_ccb_length,
+ amr_setup_ccbmap, ac, 0);
+ if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREREAD);
+ if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREWRITE);
+ }
+ ac->ac_flags |= AMR_CMD_MAPPED;
+ }
+}
+
+static void
+amr_unmapcmd(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+
+ debug_called(3);
+
+ /* if the command involved data at all and was mapped */
+ if (ac->ac_flags & AMR_CMD_MAPPED) {
+
+ 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);
+ }
+
+ if (ac->ac_ccb_data != NULL) {
+ if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTREAD);
+ if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
+ bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap);
+ }
+ ac->ac_flags &= ~AMR_CMD_MAPPED;
+ }
+}
+
+/********************************************************************************
+ * Take a command and give it to the controller, returns 0 if successful, or
+ * EBUSY if the command should be retried later.
+ */
+static int
+amr_start(struct amr_command *ac)
+{
+ struct amr_softc *sc = ac->ac_sc;
+ int done, s, i;
+
+ debug_called(3);
+
+ /* mark command as busy so that polling consumer can tell */
+ ac->ac_flags |= AMR_CMD_BUSY;
+
+ /* get a command slot (freed in amr_done) */
+ if (amr_getslot(ac))
+ return(EBUSY);
+
+ /* now we have a slot, we can map the command (unmapped in amr_complete) */
+ amr_mapcmd(ac);
+
+ /* mark the new mailbox we are going to copy in as busy */
+ ac->ac_mailbox.mb_busy = 1;
+
+ /* clear the poll/ack fields in the mailbox */
+ sc->amr_mailbox->mb_poll = 0;
+ sc->amr_mailbox->mb_ack = 0;
+
+ /*
+ * 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;
+
+ /*
+ * Spin waiting for the mailbox, give up after ~1 second. We expect the
+ * controller to be able to handle our I/O.
+ *
+ * XXX perhaps we should wait for less time, and count on the deferred command
+ * handling to deal with retries?
+ */
+ debug(4, "wait for mailbox");
+ for (i = 10000, done = 0; (i > 0) && !done; i--) {
+ s = splbio();
+
+ /* is the mailbox free? */
+ if (sc->amr_mailbox->mb_busy == 0) {
+ debug(4, "got mailbox");
+ sc->amr_mailbox64->mb64_segment = 0;
+ bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
+ done = 1;
+
+ /* not free, spin waiting */
+ } else {
+ debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy);
+ /* this is somewhat ugly */
+ DELAY(100);
+ }
+ splx(s); /* drop spl to allow completion interrupts */
+ }
+
+ /*
+ * Now give the command to the controller
+ */
+ if (done) {
+ if (sc->amr_submit_command(sc)) {
+ /* the controller wasn't ready to take the command, forget that we tried to post it */
+ sc->amr_mailbox->mb_busy = 0;
+ return(EBUSY);
+ }
+ debug(3, "posted command");
+ return(0);
+ }
+
+ /*
+ * The controller wouldn't take the command. Return the command as busy
+ * so that it is retried later.
+ */
+ return(EBUSY);
+}
+
+/********************************************************************************
+ * Extract one or more completed commands from the controller (sc)
+ *
+ * Returns nonzero if any commands on the work queue were marked as completed.
+ */
+int
+amr_done(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ struct amr_mailbox mbox;
+ int i, idx, result;
+
+ debug_called(3);
+
+ /* See if there's anything for us to do */
+ result = 0;
+
+ /* loop collecting completed commands */
+ for (;;) {
+ /* poll for a completed command's identifier and status */
+ if (sc->amr_get_work(sc, &mbox)) {
+ result = 1;
+
+ /* iterate over completed commands in this result */
+ 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_busyslots--;
+
+ /* save status for later use */
+ ac->ac_status = mbox.mb_status;
+ amr_enqueue_completed(ac);
+ debug(3, "completed command with status %x", mbox.mb_status);
+ } else {
+ device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
+ }
+ }
+ } else {
+ break; /* no work */
+ }
+ }
+
+ /* if we've completed any commands, try posting some more */
+ if (result)
+ amr_startio(sc);
+
+ /* handle completion and timeouts */
+#if __FreeBSD_version >= 500005
+ if (sc->amr_state & AMR_STATE_INTEN)
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->amr_task_complete);
+ else
+#endif
+ amr_complete(sc, 0);
+
+ return(result);
+}
+
+/********************************************************************************
+ * Do completion processing on done commands on (sc)
+ */
+static void
+amr_complete(void *context, int pending)
+{
+ struct amr_softc *sc = (struct amr_softc *)context;
+ struct amr_command *ac;
+
+ debug_called(3);
+
+ /* pull completed commands off the queue */
+ for (;;) {
+ ac = amr_dequeue_completed(sc);
+ if (ac == NULL)
+ break;
+
+ /* unmap the command's data buffer */
+ amr_unmapcmd(ac);
+
+ /* unbusy the command */
+ ac->ac_flags &= ~AMR_CMD_BUSY;
+
+ /*
+ * Is there a completion handler?
+ */
+ if (ac->ac_complete != NULL) {
+ ac->ac_complete(ac);
+
+ /*
+ * Is someone sleeping on this one?
+ */
+ } else if (ac->ac_flags & AMR_CMD_SLEEP) {
+ wakeup(ac);
+ }
+
+ if(!sc->amr_busyslots) {
+ wakeup(sc);
+ }
+ }
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Command Buffer Management
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Get a new command buffer.
+ *
+ * This may return NULL in low-memory cases.
+ *
+ * If possible, we recycle a command buffer that's been used before.
+ */
+struct amr_command *
+amr_alloccmd(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+
+ debug_called(3);
+
+ ac = amr_dequeue_free(sc);
+ if (ac == NULL) {
+ amr_alloccmd_cluster(sc);
+ ac = amr_dequeue_free(sc);
+ }
+ if (ac == NULL)
+ return(NULL);
+
+ /* clear out significant fields */
+ ac->ac_slot = 0;
+ ac->ac_status = 0;
+ bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
+ ac->ac_flags = 0;
+ ac->ac_bio = NULL;
+ ac->ac_data = NULL;
+ ac->ac_ccb_data = NULL;
+ ac->ac_complete = NULL;
+ return(ac);
+}
+
+/********************************************************************************
+ * Release a command buffer for recycling.
+ */
+void
+amr_releasecmd(struct amr_command *ac)
+{
+ debug_called(3);
+
+ amr_enqueue_free(ac);
+}
+
+/********************************************************************************
+ * Allocate a new command cluster and initialise it.
+ */
+static void
+amr_alloccmd_cluster(struct amr_softc *sc)
+{
+ struct amr_command_cluster *acc;
+ struct amr_command *ac;
+ int s, i;
+
+ acc = malloc(AMR_CMD_CLUSTERSIZE, M_DEVBUF, M_NOWAIT);
+ if (acc != NULL) {
+ s = splbio();
+ TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
+ splx(s);
+ for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
+ ac = &acc->acc_command[i];
+ bzero(ac, sizeof(*ac));
+ ac->ac_sc = sc;
+ if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) &&
+ !bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap))
+ amr_releasecmd(ac);
+ }
+ }
+}
+
+/********************************************************************************
+ * Free a command cluster
+ */
+static void
+amr_freecmd_cluster(struct amr_command_cluster *acc)
+{
+ struct amr_softc *sc = acc->acc_command[0].ac_sc;
+ int i;
+
+ for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++)
+ bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
+ free(acc, M_DEVBUF);
+}
+
+/********************************************************************************
+ ********************************************************************************
+ Interface-specific Shims
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Tell the controller that the mailbox contains a valid command
+ */
+static int
+amr_quartz_submit_command(struct amr_softc *sc)
+{
+ debug_called(3);
+
+ if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
+ return(EBUSY);
+ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
+ return(0);
+}
+
+static int
+amr_std_submit_command(struct amr_softc *sc)
+{
+ debug_called(3);
+
+ if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
+ return(EBUSY);
+ AMR_SPOST_COMMAND(sc);
+ return(0);
+}
+
+/********************************************************************************
+ * 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(3);
+
+ worked = 0;
+ s = splbio();
+
+ /* work waiting for us? */
+ if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
+
+ /* save mailbox, which contains a list of completed commands */
+ bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
+
+ /* acknowledge interrupt */
+ AMR_QPUT_ODB(sc, AMR_QODB_READY);
+
+ /* acknowledge that we have the commands */
+ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
+
+#ifndef AMR_QUARTZ_GOFASTER
+ /*
+ * This waits for the controller to notice that we've taken the
+ * command from it. It's very inefficient, and we shouldn't do it,
+ * but if we remove this code, we stop completing commands under
+ * load.
+ *
+ * Peter J says we shouldn't do this. The documentation says we
+ * should. Who is right?
+ */
+ while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
+ ; /* XXX aiee! what if it dies? */
+#endif
+
+ 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(3);
+
+ 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 */
+ bcopy((void *)(uintptr_t)(volatile void *)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_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);
+}
+
+#ifdef AMR_BOARD_INIT
+/********************************************************************************
+ * Initialise the controller
+ */
+static int
+amr_quartz_init(struct amr_softc *sc)
+{
+ int status, ostatus;
+
+ device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc));
+
+ AMR_QRESET(sc);
+
+ ostatus = 0xff;
+ while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) {
+ if (status != ostatus) {
+ device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status));
+ ostatus = status;
+ }
+ switch (status) {
+ case AMR_QINIT_NOMEM:
+ return(ENOMEM);
+
+ case AMR_QINIT_SCAN:
+ /* XXX we could print channel/target here */
+ break;
+ }
+ }
+ return(0);
+}
+
+static int
+amr_std_init(struct amr_softc *sc)
+{
+ int status, ostatus;
+
+ device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc));
+
+ AMR_SRESET(sc);
+
+ ostatus = 0xff;
+ while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) {
+ if (status != ostatus) {
+ device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status));
+ ostatus = status;
+ }
+ switch (status) {
+ case AMR_SINIT_NOMEM:
+ return(ENOMEM);
+
+ case AMR_SINIT_INPROG:
+ /* XXX we could print channel/target here? */
+ break;
+ }
+ }
+ return(0);
+}
+#endif
+
+/********************************************************************************
+ ********************************************************************************
+ Debugging
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Identify the controller and print some information about it.
+ */
+static void
+amr_describe_controller(struct amr_softc *sc)
+{
+ struct amr_prodinfo *ap;
+ struct amr_enquiry *ae;
+ char *prod;
+
+ /*
+ * Try to get 40LD product info, which tells us what the card is labelled as.
+ */
+ if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) {
+ device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
+ ap->ap_product, ap->ap_firmware, ap->ap_bios,
+ ap->ap_memsize);
+
+ free(ap, M_DEVBUF);
+ return;
+ }
+
+ /*
+ * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
+ */
+ if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) {
+ prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
+
+ } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) {
+
+ /*
+ * Try to work it out based on the PCI signatures.
+ */
+ switch (pci_get_device(sc->amr_dev)) {
+ case 0x9010:
+ prod = "Series 428";
+ break;
+ case 0x9060:
+ prod = "Series 434";
+ break;
+ default:
+ prod = "unknown controller";
+ break;
+ }
+ } else {
+ prod = "unsupported controller";
+ }
+
+ /*
+ * HP NetRaid controllers have a special encoding of the firmware and
+ * BIOS versions. The AMI version seems to have it as strings whereas
+ * the HP version does it with a leading uppercase character and two
+ * binary numbers.
+ */
+
+ if(ae->ae_adapter.aa_firmware[2] >= 'A' &&
+ ae->ae_adapter.aa_firmware[2] <= 'Z' &&
+ ae->ae_adapter.aa_firmware[1] < ' ' &&
+ ae->ae_adapter.aa_firmware[0] < ' ' &&
+ ae->ae_adapter.aa_bios[2] >= 'A' &&
+ ae->ae_adapter.aa_bios[2] <= 'Z' &&
+ ae->ae_adapter.aa_bios[1] < ' ' &&
+ ae->ae_adapter.aa_bios[0] < ' ') {
+
+ /* this looks like we have an HP NetRaid version of the MegaRaid */
+
+ if(ae->ae_signature == AMR_SIG_438) {
+ /* the AMI 438 is a NetRaid 3si in HP-land */
+ prod = "HP NetRaid 3si";
+ }
+
+ device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n",
+ prod, ae->ae_adapter.aa_firmware[2],
+ ae->ae_adapter.aa_firmware[1],
+ ae->ae_adapter.aa_firmware[0],
+ ae->ae_adapter.aa_bios[2],
+ ae->ae_adapter.aa_bios[1],
+ ae->ae_adapter.aa_bios[0],
+ ae->ae_adapter.aa_memorysize);
+ } else {
+ device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n",
+ prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
+ ae->ae_adapter.aa_memorysize);
+ }
+ free(ae, M_DEVBUF);
+}
+
+#ifdef AMR_DEBUG
+/********************************************************************************
+ * Print the command (ac) in human-readable format
+ */
+#if 0
+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 %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
+ device_printf(sc->amr_dev, "sg physaddr %08x nsg %d\n",
+ ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
+ device_printf(sc->amr_dev, "ccb %p bio %p\n", ac->ac_ccb_data, ac->ac_bio);
+
+ /* 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);
+}
+#endif
+#endif
diff --git a/sys/dev/amr/amr_cam.c b/sys/dev/amr/amr_cam.c
new file mode 100644
index 0000000..400a87e
--- /dev/null
+++ b/sys/dev/amr/amr_cam.c
@@ -0,0 +1,614 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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 <dev/amr/amr_compat.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+
+#include <dev/amr/amrreg.h>
+#include <dev/amr/amrvar.h>
+
+static void amr_cam_action(struct cam_sim *sim, union ccb *ccb);
+static void amr_cam_poll(struct cam_sim *sim);
+static void amr_cam_complete(struct amr_command *ac);
+static void amr_cam_complete_extcdb(struct amr_command *ac);
+
+
+/********************************************************************************
+ * Enqueue/dequeue functions
+ */
+static __inline void
+amr_enqueue_ccb(struct amr_softc *sc, union ccb *ccb)
+{
+ int s;
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
+ splx(s);
+}
+
+static __inline void
+amr_requeue_ccb(struct amr_softc *sc, union ccb *ccb)
+{
+ int s;
+
+ s = splbio();
+ TAILQ_INSERT_HEAD(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
+ splx(s);
+}
+
+static __inline union ccb *
+amr_dequeue_ccb(struct amr_softc *sc)
+{
+ union ccb *ccb;
+ int s;
+
+ s = splbio();
+ if ((ccb = (union ccb *)TAILQ_FIRST(&sc->amr_cam_ccbq)) != NULL)
+ TAILQ_REMOVE(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
+ splx(s);
+ return(ccb);
+}
+
+/********************************************************************************
+ * Attach our 'real' SCSI channels to CAM
+ */
+int
+amr_cam_attach(struct amr_softc *sc)
+{
+ struct cam_devq *devq;
+ int chn;
+
+ /* initialise the ccb queue */
+ TAILQ_INIT(&sc->amr_cam_ccbq);
+
+ /*
+ * Allocate a devq for all our channels combined. This should
+ * allow for the maximum number of SCSI commands we will accept
+ * at one time.
+ */
+ if ((devq = cam_simq_alloc(AMR_MAX_SCSI_CMDS)) == NULL)
+ return(ENOMEM);
+
+ /*
+ * Iterate over our channels, registering them with CAM
+ */
+ for (chn = 0; chn < sc->amr_maxchan; chn++) {
+
+ /* allocate a sim */
+ if ((sc->amr_cam_sim[chn] = cam_sim_alloc(amr_cam_action,
+ amr_cam_poll,
+ "amr",
+ sc,
+ device_get_unit(sc->amr_dev),
+ 1,
+ AMR_MAX_SCSI_CMDS,
+ devq)) == NULL) {
+ cam_simq_free(devq);
+ device_printf(sc->amr_dev, "CAM SIM attach failed\n");
+ return(ENOMEM);
+ }
+
+ /* register the bus ID so we can get it later */
+ if (xpt_bus_register(sc->amr_cam_sim[chn], chn)) {
+ device_printf(sc->amr_dev, "CAM XPT bus registration failed\n");
+ return(ENXIO);
+ }
+ }
+ /*
+ * XXX we should scan the config and work out which devices are actually
+ * protected.
+ */
+ return(0);
+}
+
+/********************************************************************************
+ * Disconnect ourselves from CAM
+ */
+void
+amr_cam_detach(struct amr_softc *sc)
+{
+ int chn, first;
+
+ for (chn = 0, first = 1; chn < sc->amr_maxchan; chn++) {
+
+ /*
+ * If a sim was allocated for this channel, free it
+ */
+ if (sc->amr_cam_sim[chn] != NULL) {
+ xpt_bus_deregister(cam_sim_path(sc->amr_cam_sim[chn]));
+ cam_sim_free(sc->amr_cam_sim[chn], first ? TRUE : FALSE);
+ first = 0;
+ }
+ }
+}
+
+/********************************************************************************
+ ********************************************************************************
+ CAM passthrough interface
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Handle a request for action from CAM
+ */
+static void
+amr_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct amr_softc *sc = cam_sim_softc(sim);
+
+ switch(ccb->ccb_h.func_code) {
+
+ /*
+ * Perform SCSI I/O to a physical device.
+ */
+ case XPT_SCSI_IO:
+ {
+ struct ccb_hdr *ccbh = &ccb->ccb_h;
+ struct ccb_scsiio *csio = &ccb->csio;
+
+ /* Validate the CCB */
+ ccbh->status = CAM_REQ_INPROG;
+
+ /* check the CDB length */
+ if (csio->cdb_len > AMR_MAX_EXTCDB_LEN)
+ ccbh->status = CAM_REQ_CMP_ERR;
+
+ if ((csio->cdb_len > AMR_MAX_CDB_LEN) && (sc->support_ext_cdb == 0 ))
+ ccbh->status = CAM_REQ_CMP_ERR;
+
+ /* check that the CDB pointer is not to a physical address */
+ if ((ccbh->flags & CAM_CDB_POINTER) && (ccbh->flags & CAM_CDB_PHYS))
+ ccbh->status = CAM_REQ_CMP_ERR;
+
+ /* if there is data transfer, it must be to/from a virtual address */
+ if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+ if (ccbh->flags & CAM_DATA_PHYS) /* we can't map it */
+ ccbh->status = CAM_REQ_CMP_ERR;
+ if (ccbh->flags & CAM_SCATTER_VALID) /* we want to do the s/g setup */
+ ccbh->status = CAM_REQ_CMP_ERR;
+ }
+
+ /*
+ * If the command is to a LUN other than 0, fail it.
+ * This is probably incorrect, but during testing the firmware did not
+ * seem to respect the LUN field, and thus devices appear echoed.
+ */
+ if (csio->ccb_h.target_lun != 0)
+ ccbh->status = CAM_REQ_CMP_ERR;
+
+ /* if we're happy with the request, queue it for attention */
+ if (ccbh->status == CAM_REQ_INPROG) {
+
+ /* save the channel number in the ccb */
+ csio->ccb_h.sim_priv.entries[0].field = cam_sim_bus(sim);
+
+ amr_enqueue_ccb(sc, ccb);
+ amr_startio(sc);
+ return;
+ }
+ break;
+ }
+
+ case XPT_CALC_GEOMETRY:
+ {
+ cam_calc_geometry(&ccb->ccg, /*extended*/1);
+ break;
+ }
+
+ /*
+ * Return path stats. Some of these should probably be
+ * amended.
+ */
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi = & ccb->cpi;
+
+ debug(3, "XPT_PATH_INQ");
+ cpi->version_num = 1; /* XXX??? */
+ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NOBUSRESET;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = AMR_MAX_TARGETS;
+ cpi->max_lun = 0 /* AMR_MAX_LUNS*/;
+ cpi->initiator_id = 7; /* XXX variable? */
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "LSI", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 132 * 1024; /* XXX get from controller? */
+ cpi->ccb_h.status = CAM_REQ_CMP;
+
+ break;
+ }
+
+ case XPT_RESET_BUS:
+ {
+ struct ccb_pathinq *cpi = & ccb->cpi;
+
+ debug(1, "XPT_RESET_BUS");
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+
+ case XPT_RESET_DEV:
+ {
+ debug(1, "XPT_RESET_DEV");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts;
+
+ debug(3, "XPT_GET_TRAN_SETTINGS");
+
+ cts = &(ccb->cts);
+
+ if ((cts->flags & CCB_TRANS_USER_SETTINGS) == 0) {
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ break;
+ }
+
+ cts->flags = CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB;
+ cts->bus_width = MSG_EXT_WDTR_BUS_32_BIT;
+ cts->sync_period = 6; /* 40MHz how wide is this bus? */
+ cts->sync_offset = 31; /* How to extract this from board? */
+
+ cts->valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID
+ | CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_DISC_VALID
+ | CCB_TRANS_TQ_VALID;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+
+ case XPT_SET_TRAN_SETTINGS:
+ debug(3, "XPT_SET_TRAN_SETTINGS");
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ break;
+
+
+ /*
+ * Reject anything else as unsupported.
+ */
+ default:
+ /* we can't do this */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+}
+
+/********************************************************************************
+ * Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI command.
+ */
+int
+amr_cam_command(struct amr_softc *sc, struct amr_command **acp)
+{
+ struct amr_command *ac;
+ struct amr_passthrough *ap;
+ struct amr_ext_passthrough *aep;
+ struct ccb_scsiio *csio;
+ int bus, target, error;
+
+ error = 0;
+ ac = NULL;
+ ap = NULL;
+ aep = NULL;
+
+ /* check to see if there is a ccb for us to work with */
+ if ((csio = (struct ccb_scsiio *)amr_dequeue_ccb(sc)) == NULL)
+ goto out;
+
+ /* get bus/target, XXX validate against protected devices? */
+ bus = csio->ccb_h.sim_priv.entries[0].field;
+ target = csio->ccb_h.target_id;
+
+ /*
+ * Build a passthrough command.
+ */
+
+ /* construct passthrough */
+ if (sc->support_ext_cdb ) {
+ if ((aep = malloc(sizeof(*aep), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ aep->ap_timeout = 2;
+ aep->ap_ars = 1;
+ aep->ap_request_sense_length = 14;
+ aep->ap_islogical = 0;
+ aep->ap_channel = bus;
+ aep->ap_scsi_id = target;
+ aep->ap_logical_drive_no = csio->ccb_h.target_lun;
+ aep->ap_cdb_length = csio->cdb_len;
+ aep->ap_data_transfer_length = csio->dxfer_len;
+ if (csio->ccb_h.flags & CAM_CDB_POINTER) {
+ bcopy(csio->cdb_io.cdb_ptr, aep->ap_cdb, csio->cdb_len);
+ } else {
+ bcopy(csio->cdb_io.cdb_bytes, aep->ap_cdb, csio->cdb_len);
+ }
+ /* we leave the data s/g list and s/g count to the map routine later */
+
+ debug(2, " COMMAND %x/%d+%d to %d:%d:%d", aep->ap_cdb[0], aep->ap_cdb_length, csio->dxfer_len,
+ aep->ap_channel, aep->ap_scsi_id, aep->ap_logical_drive_no);
+
+ } else {
+ if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ ap->ap_timeout = 0;
+ ap->ap_ars = 1;
+ ap->ap_request_sense_length = 14;
+ ap->ap_islogical = 0;
+ ap->ap_channel = bus;
+ ap->ap_scsi_id = target;
+ ap->ap_logical_drive_no = csio->ccb_h.target_lun;
+ ap->ap_cdb_length = csio->cdb_len;
+ ap->ap_data_transfer_length = csio->dxfer_len;
+ if (csio->ccb_h.flags & CAM_CDB_POINTER) {
+ bcopy(csio->cdb_io.cdb_ptr, ap->ap_cdb, csio->cdb_len);
+ } else {
+ bcopy(csio->cdb_io.cdb_bytes, ap->ap_cdb, csio->cdb_len);
+ }
+ /* we leave the data s/g list and s/g count to the map routine later */
+
+ debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap->ap_cdb[0], ap->ap_cdb_length, csio->dxfer_len,
+ ap->ap_channel, ap->ap_scsi_id, ap->ap_logical_drive_no);
+ }
+
+ /* construct command */
+ if ((ac = amr_alloccmd(sc)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ ac->ac_flags |= AMR_CMD_DATAOUT;
+
+ ac->ac_ccb_data = csio->data_ptr;
+ ac->ac_ccb_length = csio->dxfer_len;
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
+ ac->ac_flags |= AMR_CMD_CCB_DATAIN;
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
+ ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
+
+ ac->ac_private = csio;
+ if ( sc->support_ext_cdb ) {
+ ac->ac_data = aep;
+ ac->ac_length = sizeof(*aep);
+ ac->ac_complete = amr_cam_complete_extcdb;
+ ac->ac_mailbox.mb_command = AMR_CMD_EXTPASS;
+ } else {
+ ac->ac_data = ap;
+ ac->ac_length = sizeof(*ap);
+ ac->ac_complete = amr_cam_complete;
+ ac->ac_mailbox.mb_command = AMR_CMD_PASS;
+ }
+
+out:
+ if (error != 0) {
+ if (ac != NULL)
+ amr_releasecmd(ac);
+ if (ap != NULL)
+ free(ap, M_DEVBUF);
+ if (aep != NULL)
+ free(aep, M_DEVBUF);
+ if (csio != NULL) /* put it back and try again later */
+ amr_requeue_ccb(sc, (union ccb *)csio);
+ }
+ *acp = ac;
+ return(error);
+}
+
+/********************************************************************************
+ * Check for interrupt status
+ */
+static void
+amr_cam_poll(struct cam_sim *sim)
+{
+ amr_done(cam_sim_softc(sim));
+}
+
+ /********************************************************************************
+ * Handle completion of a command submitted via CAM.
+ */
+static void
+amr_cam_complete(struct amr_command *ac)
+{
+ struct amr_passthrough *ap = (struct amr_passthrough *)ac->ac_data;
+ struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private;
+ struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr;
+
+ /* XXX note that we're ignoring ac->ac_status - good idea? */
+
+ debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, ap->ap_scsi_status);
+
+ /*
+ * Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
+ *
+ * If the configuration provides a mechanism to mark a disk a "not managed", we
+ * could add handling for that to allow disks to be selectively visible.
+ */
+
+ if ((ap->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) {
+ bzero(csio->data_ptr, csio->dxfer_len);
+ if (ap->ap_scsi_status == 0xf0) {
+ csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ } else {
+ csio->ccb_h.status = CAM_DEV_NOT_THERE;
+ }
+ } else {
+
+ /* handle passthrough SCSI status */
+ switch(ap->ap_scsi_status) {
+ case 0: /* completed OK */
+ csio->ccb_h.status = CAM_REQ_CMP;
+ break;
+
+ case 0x02:
+ csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+ bcopy(ap->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN);
+ csio->sense_len = AMR_MAX_REQ_SENSE_LEN;
+ csio->ccb_h.status |= CAM_AUTOSNS_VALID;
+ break;
+
+ case 0x08:
+ csio->ccb_h.status = CAM_SCSI_BUSY;
+ break;
+
+ case 0xf0:
+ case 0xf4:
+ default:
+ csio->ccb_h.status = CAM_REQ_CMP_ERR;
+ break;
+ }
+ }
+ free(ap, M_DEVBUF);
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
+ debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " ");
+ xpt_done((union ccb *)csio);
+ amr_releasecmd(ac);
+}
+
+/********************************************************************************
+ * Handle completion of a command submitted via CAM.
+ * Completion for extended cdb
+ */
+static void
+amr_cam_complete_extcdb(struct amr_command *ac)
+{
+ struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data;
+ struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private;
+ struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr;
+
+ /* XXX note that we're ignoring ac->ac_status - good idea? */
+
+ debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, aep->ap_scsi_status);
+
+ /*
+ * Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
+ *
+ * If the configuration provides a mechanism to mark a disk a "not managed", we
+ * could add handling for that to allow disks to be selectively visible.
+ */
+
+ if ((aep->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) {
+ bzero(csio->data_ptr, csio->dxfer_len);
+ if (aep->ap_scsi_status == 0xf0) {
+ csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ } else {
+ csio->ccb_h.status = CAM_DEV_NOT_THERE;
+ }
+ } else {
+
+ /* handle passthrough SCSI status */
+ switch(aep->ap_scsi_status) {
+ case 0: /* completed OK */
+ csio->ccb_h.status = CAM_REQ_CMP;
+ break;
+
+ case 0x02:
+ csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+ bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN);
+ csio->sense_len = AMR_MAX_REQ_SENSE_LEN;
+ csio->ccb_h.status |= CAM_AUTOSNS_VALID;
+ break;
+
+ case 0x08:
+ csio->ccb_h.status = CAM_SCSI_BUSY;
+ break;
+
+ case 0xf0:
+ case 0xf4:
+ default:
+ csio->ccb_h.status = CAM_REQ_CMP_ERR;
+ break;
+ }
+ }
+ free(aep, M_DEVBUF);
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
+ debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " ");
+ xpt_done((union ccb *)csio);
+ amr_releasecmd(ac);
+}
diff --git a/sys/dev/amr/amr_compat.h b/sys/dev/amr/amr_compat.h
new file mode 100644
index 0000000..33a09f0
--- /dev/null
+++ b/sys/dev/amr/amr_compat.h
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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$
+ */
+/*
+ * Backwards compatibility support.
+ */
+
+#if __FreeBSD_version < 500003 /* old buf style */
+# include <sys/buf.h>
+# include <machine/clock.h>
+# define INTR_ENTROPY 0
+
+# define FREEBSD_4
+# define bio buf
+# define bioq_init(x) bufq_init(x)
+# define bioq_insert_tail(x, y) bufq_insert_tail(x, y)
+# define bioq_remove(x, y) bufq_remove(x, y)
+# define bioq_first(x) bufq_first(x)
+# define bio_queue_head buf_queue_head
+# define bio_bcount b_bcount
+# define bio_blkno b_blkno
+# define bio_caller1 b_caller1
+# define bio_data b_data
+# define bio_dev b_dev
+# define bio_driver1 b_driver1
+# define bio_driver2 b_driver2
+# define bio_error b_error
+# define bio_flags b_flags
+# define bio_pblkno b_pblkno
+# define bio_resid b_resid
+# define BIO_ERROR B_ERROR
+# define devstat_end_transaction_bio(x, y) devstat_end_transaction_buf(x, y)
+# define BIO_IS_READ(x) ((x)->b_flags & B_READ)
+# define AMR_BIO_FINISH(x) devstat_end_transaction_bio(&sc->amrd_stats, x);\
+ biodone(x)
+
+#else
+# include <sys/bio.h>
+# define BIO_IS_READ(x) ((x)->bio_cmd == BIO_READ)
+# define AMR_BIO_FINISH(x) biodone(x)
+#endif
+
+/************************************************************************
+ * Compatibility with older versions of FreeBSD
+ */
+#if __FreeBSD_version < 440001
+typedef struct proc d_thread_t;
+#define M_ZERO 0x0008 /* bzero the allocation */
+#endif
+
+
+#ifndef __packed
+#define __packed __attribute__ ((packed))
+#endif
diff --git a/sys/dev/amr/amr_disk.c b/sys/dev/amr/amr_disk.c
new file mode 100644
index 0000000..e7a9bbe
--- /dev/null
+++ b/sys/dev/amr/amr_disk.c
@@ -0,0 +1,253 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * Copyright (c) 1999, 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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/kernel.h>
+
+#include <dev/amr/amr_compat.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <dev/amr/amrio.h>
+#include <dev/amr/amrreg.h>
+#include <dev/amr/amrvar.h>
+#include <dev/amr/amr_tables.h>
+
+/* prototypes */
+static int amrd_probe(device_t dev);
+static int amrd_attach(device_t dev);
+static int amrd_detach(device_t dev);
+
+static disk_open_t amrd_open;
+static disk_strategy_t amrd_strategy;
+
+static devclass_t amrd_devclass;
+#ifdef FREEBSD_4
+static int disks_registered = 0;
+#endif
+
+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 int
+amrd_open(struct disk *dp)
+{
+ struct amrd_softc *sc = (struct amrd_softc *)dp->d_drv1;
+#if __FreeBSD_version < 500000 /* old buf style */
+ struct disklabel *label;
+#endif
+
+ debug_called(1);
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* controller not active? */
+ if (sc->amrd_controller->amr_state & AMR_STATE_SHUTDOWN)
+ return(ENXIO);
+
+#if __FreeBSD_version < 500000 /* old buf style */
+ 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;
+#else
+ sc->amrd_disk.d_sectorsize = AMR_BLKSIZE;
+ sc->amrd_disk.d_mediasize = (off_t)sc->amrd_drive->al_size * AMR_BLKSIZE;
+ sc->amrd_disk.d_fwsectors = sc->amrd_drive->al_sectors;
+ sc->amrd_disk.d_fwheads = sc->amrd_drive->al_heads;
+#endif
+
+ return (0);
+}
+
+/*
+ * 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 bio *bio)
+{
+ struct amrd_softc *sc = (struct amrd_softc *)bio->bio_disk->d_drv1;
+
+ /* bogus disk? */
+ if (sc == NULL) {
+ bio->bio_error = EINVAL;
+ goto bad;
+ }
+
+ amr_submit_bio(sc->amrd_controller, bio);
+ return;
+
+ bad:
+ bio->bio_flags |= BIO_ERROR;
+
+ /*
+ * Correctly set the buf to indicate a completed transfer
+ */
+ bio->bio_resid = bio->bio_bcount;
+ biodone(bio);
+ return;
+}
+
+void
+amrd_intr(void *data)
+{
+ struct bio *bio = (struct bio *)data;
+
+ debug_called(2);
+
+ if (bio->bio_flags & BIO_ERROR) {
+ bio->bio_error = EIO;
+ debug(1, "i/o error\n");
+ } else {
+ bio->bio_resid = 0;
+ }
+
+ AMR_BIO_FINISH(bio);
+}
+
+static int
+amrd_probe(device_t dev)
+{
+
+ debug_called(1);
+
+ device_set_desc(dev, "LSILogic 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(1);
+
+ 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);
+ sc->amrd_dev = dev;
+
+ device_printf(dev, "%uMB (%u sectors) RAID %d (%s)\n",
+ sc->amrd_drive->al_size / ((1024 * 1024) / AMR_BLKSIZE),
+ sc->amrd_drive->al_size, sc->amrd_drive->al_properties & AMR_DRV_RAID_MASK,
+ amr_describe_code(amr_table_drvstate, AMR_DRV_CURSTATE(sc->amrd_drive->al_state)));
+
+ sc->amrd_disk.d_drv1 = sc;
+ sc->amrd_disk.d_maxsize = (AMR_NSEG - 1) * PAGE_SIZE;
+ sc->amrd_disk.d_open = amrd_open;
+ sc->amrd_disk.d_strategy = amrd_strategy;
+ sc->amrd_disk.d_name = "amrd";
+ disk_create(sc->amrd_unit, &sc->amrd_disk, 0, NULL, NULL);
+#ifdef FREEBSD_4
+ disks_registered++;
+#endif
+
+ return (0);
+}
+
+static int
+amrd_detach(device_t dev)
+{
+ struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
+
+ debug_called(1);
+
+ if (sc->amrd_disk.d_flags & DISKFLAG_OPEN)
+ return(EBUSY);
+
+#ifdef FREEBSD_4
+ if (--disks_registered == 0)
+ cdevsw_remove(&amrddisk_cdevsw);
+#else
+ disk_destroy(&sc->amrd_disk);
+#endif
+ return(0);
+}
+
diff --git a/sys/dev/amr/amr_pci.c b/sys/dev/amr/amr_pci.c
new file mode 100644
index 0000000..eacb1bd
--- /dev/null
+++ b/sys/dev/amr/amr_pci.c
@@ -0,0 +1,604 @@
+/*-
+ * Copyright (c) 1999,2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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/kernel.h>
+
+#include <dev/amr/amr_compat.h>
+#include <sys/bus.h>
+#include <sys/conf.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>
+
+static int amr_pci_probe(device_t dev);
+static int amr_pci_attach(device_t dev);
+static int amr_pci_detach(device_t dev);
+static int amr_pci_shutdown(device_t dev);
+static int amr_pci_suspend(device_t dev);
+static int amr_pci_resume(device_t dev);
+static void amr_pci_intr(void *arg);
+static void amr_pci_free(struct amr_softc *sc);
+static void amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+static int amr_sglist_map(struct amr_softc *sc);
+static void amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+static int amr_setup_mbox(struct amr_softc *sc);
+
+static device_method_t amr_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, amr_pci_probe),
+ DEVMETHOD(device_attach, amr_pci_attach),
+ DEVMETHOD(device_detach, amr_pci_detach),
+ DEVMETHOD(device_shutdown, amr_pci_shutdown),
+ DEVMETHOD(device_suspend, amr_pci_suspend),
+ DEVMETHOD(device_resume, amr_pci_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)
+};
+
+static devclass_t amr_devclass;
+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 for signature */
+ {0x101e, 0x1960, 0},
+ {0x1000, 0x1960, PROBE_SIGNATURE},
+ {0x1000, 0x0407, 0},
+ {0x1028, 0x000e, PROBE_SIGNATURE}, /* perc4/di i960 */
+ {0x1028, 0x000f, 0}, /* perc4/di Verde*/
+ {0, 0, 0}
+};
+
+static int
+amr_pci_probe(device_t dev)
+{
+ int i, sig;
+
+ debug_called(1);
+
+ 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) {
+ sig = pci_read_config(dev, AMR_CFG_SIG, 2);
+ if ((sig != AMR_SIGNATURE_1) && (sig != AMR_SIGNATURE_2))
+ continue;
+ }
+ device_set_desc(dev, "LSILogic MegaRAID");
+ return(-10); /* allow room to be overridden */
+ }
+ }
+ return(ENXIO);
+}
+
+static int
+amr_pci_attach(device_t dev)
+{
+ struct amr_softc *sc;
+ int rid, rtype, error;
+ u_int32_t command;
+
+ debug_called(1);
+
+ /*
+ * Initialise softc.
+ */
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(*sc));
+ sc->amr_dev = dev;
+
+ /* assume failure is 'not configured' */
+ error = ENXIO;
+
+ /*
+ * Determine board type.
+ */
+ command = pci_read_config(dev, PCIR_COMMAND, 1);
+ if ((pci_get_device(dev) == 0x1960) || (pci_get_device(dev) == 0x0407) ||
+ (pci_get_device(dev) == 0x000e) || (pci_get_device(dev) == 0x000f)) {
+ /*
+ * 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");
+ goto out;
+ }
+ sc->amr_type |= AMR_TYPE_QUARTZ;
+
+ } else {
+ /*
+ * 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");
+ goto out;
+ }
+ }
+
+ /* force the busmaster enable bit on */
+ if (!(command & PCIM_CMD_BUSMASTEREN)) {
+ device_printf(dev, "busmaster bit not set, enabling\n");
+ command |= PCIM_CMD_BUSMASTEREN;
+ pci_write_config(dev, PCIR_COMMAND, command, 2);
+ }
+
+ /*
+ * Allocate the PCI register window.
+ */
+ rid = PCIR_MAPS;
+ rtype = AMR_IS_QUARTZ(sc) ? 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, "can't allocate register window\n");
+ goto out;
+ }
+ sc->amr_btag = rman_get_bustag(sc->amr_reg);
+ sc->amr_bhandle = rman_get_bushandle(sc->amr_reg);
+
+ /*
+ * 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, "can't allocate interrupt\n");
+ goto out;
+ }
+ if (bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO | INTR_ENTROPY, amr_pci_intr, sc, &sc->amr_intr)) {
+ device_printf(sc->amr_dev, "can't set up interrupt\n");
+ goto out;
+ }
+
+ debug(2, "interrupt attached");
+
+ /* assume failure is 'out of memory' */
+ error = ENOMEM;
+
+ /*
+ * Allocate the parent bus DMA tag appropriate for PCI.
+ */
+ if (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)) {
+ device_printf(dev, "can't allocate parent DMA tag\n");
+ goto out;
+ }
+
+ /*
+ * Create DMA tag for mapping buffers into controller-addressable space.
+ */
+ if (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)) {
+ device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
+ goto out;
+ }
+
+ debug(2, "dma tag done");
+
+ /*
+ * Allocate and set up mailbox in a bus-visible fashion.
+ */
+ if ((error = amr_setup_mbox(sc)) != 0)
+ goto out;
+
+ debug(2, "mailbox setup");
+
+ /*
+ * Build the scatter/gather buffers.
+ */
+ if (amr_sglist_map(sc))
+ goto out;
+
+ debug(2, "s/g list mapped");
+
+ /*
+ * Do bus-independant initialisation, bring controller online.
+ */
+ error = amr_attach(sc);
+
+out:
+ if (error)
+ amr_pci_free(sc);
+ return(error);
+}
+
+/********************************************************************************
+ * Disconnect from the controller completely, in preparation for unload.
+ */
+static int
+amr_pci_detach(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+ int error;
+
+ debug_called(1);
+
+ if (sc->amr_state & AMR_STATE_OPEN)
+ return(EBUSY);
+
+ if ((error = amr_pci_shutdown(dev)))
+ return(error);
+
+ amr_pci_free(sc);
+
+ 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 bioq on the controller is empty, as we won't
+ * allow shutdown if any device is open.
+ */
+static int
+amr_pci_shutdown(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+ int i,error,s;
+
+ debug_called(1);
+
+ /* mark ourselves as in-shutdown */
+ sc->amr_state |= AMR_STATE_SHUTDOWN;
+
+
+ /* flush controller */
+ device_printf(sc->amr_dev, "flushing cache...");
+ printf("%s\n", amr_flush(sc) ? "failed" : "done");
+
+ s = splbio();
+ error = 0;
+
+ /* 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 shutdown_out;
+ sc->amr_drive[i].al_disk = 0;
+ }
+ }
+
+ /* XXX disable interrupts? */
+
+shutdown_out:
+ splx(s);
+ return(error);
+}
+
+/********************************************************************************
+ * Bring the controller to a quiescent state, ready for system suspend.
+ */
+static int
+amr_pci_suspend(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+
+ debug_called(1);
+
+ sc->amr_state |= AMR_STATE_SUSPEND;
+
+ /* flush controller */
+ device_printf(sc->amr_dev, "flushing cache...");
+ printf("%s\n", amr_flush(sc) ? "failed" : "done");
+
+ /* XXX disable interrupts? */
+
+ return(0);
+}
+
+/********************************************************************************
+ * Bring the controller back to a state ready for operation.
+ */
+static int
+amr_pci_resume(device_t dev)
+{
+ struct amr_softc *sc = device_get_softc(dev);
+
+ debug_called(1);
+
+ sc->amr_state &= ~AMR_STATE_SUSPEND;
+
+ /* XXX enable interrupts? */
+
+ return(0);
+}
+
+/*******************************************************************************
+ * Take an interrupt, or be poked by other code to look for interrupt-worthy
+ * status.
+ */
+static void
+amr_pci_intr(void *arg)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+
+ debug_called(2);
+
+ /* collect finished commands, queue anything waiting */
+ amr_done(sc);
+}
+
+/********************************************************************************
+ * Free all of the resources associated with (sc)
+ *
+ * Should not be called if the controller is active.
+ */
+static void
+amr_pci_free(struct amr_softc *sc)
+{
+ u_int8_t *p;
+
+ debug_called(1);
+
+ amr_free(sc);
+
+ /* 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 *)(uintptr_t)(volatile void *)sc->amr_mailbox;
+ bus_dmamem_free(sc->amr_mailbox_dmat, p - 16, sc->amr_mailbox_dmamap);
+ }
+ if (sc->amr_mailbox_dmat)
+ bus_dma_tag_destroy(sc->amr_mailbox_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,
+ AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT,
+ PCIR_MAPS, sc->amr_reg);
+}
+
+/********************************************************************************
+ * Allocate and map the scatter/gather table in bus space.
+ */
+static void
+amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+
+ debug_called(1);
+
+ /* 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(1);
+
+ /*
+ * Create a single tag describing a region large enough to hold all of
+ * the s/g lists we will need.
+ *
+ * Note that we could probably use AMR_LIMITCMD here, but that may become tunable.
+ */
+ segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD;
+ 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.
+ *
+ * XXX HACK ALERT: at least some controllers don't like the s/g memory being
+ * allocated below 0x2000. We leak some memory if we get some
+ * below this mark and allocate again. We should be able to
+ * avoid this with the tag setup, but that does't seem to work.
+ */
+retry:
+ 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_sglist_map_helper, sc, 0);
+ if (sc->amr_sgbusaddr < 0x2000) {
+ debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr);
+ goto retry;
+ }
+ 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_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct amr_softc *sc = (struct amr_softc *)arg;
+
+ debug_called(1);
+
+ /* 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(1);
+
+ /*
+ * 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_setup_mbox_helper, 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);
+
+ return(0);
+}
diff --git a/sys/dev/amr/amr_tables.h b/sys/dev/amr/amr_tables.h
new file mode 100644
index 0000000..b1a4e44
--- /dev/null
+++ b/sys/dev/amr/amr_tables.h
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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$
+ */
+
+/*
+ * Lookup table for code-to-text translations.
+ */
+struct amr_code_lookup {
+ char *string;
+ u_int32_t code;
+};
+
+extern char *amr_describe_code(struct amr_code_lookup *table, u_int32_t code);
+
+#ifndef AMR_DEFINE_TABLES
+extern struct amr_code_lookup amr_table_qinit[];
+extern struct amr_code_lookup amr_table_sinit[];
+extern struct amr_code_lookup amr_table_drvstate[];
+
+#else /* AMR_DEFINE_TABLES */
+
+/********************************************************************************
+ * Look up a text description of a numeric code and return a pointer to same.
+ */
+char *
+amr_describe_code(struct amr_code_lookup *table, u_int32_t code)
+{
+ int i;
+
+ for (i = 0; table[i].string != NULL; i++)
+ if (table[i].code == code)
+ return(table[i].string);
+ return(table[i+1].string);
+}
+
+struct amr_code_lookup amr_table_qinit[] = {
+ {"init scanning drives", AMR_QINIT_SCAN},
+ {"init scanning initialising", AMR_QINIT_SCANINIT},
+ {"init firmware initing", AMR_QINIT_FIRMWARE},
+ {"init in progress", AMR_QINIT_INPROG},
+ {"init spinning drives", AMR_QINIT_SPINUP},
+ {"insufficient memory", AMR_QINIT_NOMEM},
+ {"init flushing cache", AMR_QINIT_CACHEFLUSH},
+ {"init successfully done", AMR_QINIT_DONE},
+ {NULL, 0},
+ {"unknown init code", 0}
+};
+
+struct amr_code_lookup amr_table_sinit[] = {
+ {"init abnormal terminated", AMR_SINIT_ABEND},
+ {"insufficient memory", AMR_SINIT_NOMEM},
+ {"firmware flushing cache", AMR_SINIT_CACHEFLUSH},
+ {"init in progress", AMR_SINIT_INPROG},
+ {"firmware spinning drives", AMR_SINIT_SPINUP},
+ {"init successfully done", AMR_SINIT_DONE},
+ {NULL, 0},
+ {"unknown init code", 0}
+};
+
+struct amr_code_lookup amr_table_drvstate[] = {
+ {"offline", AMR_DRV_OFFLINE},
+ {"degraded", AMR_DRV_DEGRADED},
+ {"optimal", AMR_DRV_OPTIMAL},
+ {"online", AMR_DRV_ONLINE},
+ {"failed", AMR_DRV_FAILED},
+ {"rebuild", AMR_DRV_REBUILD},
+ {"hot spare", AMR_DRV_HOTSPARE},
+ {NULL, 0},
+ {"unknown", 0}
+};
+
+struct amr_code_lookup amr_table_adaptertype[] = {
+ {"Series 431", AMR_SIG_431},
+ {"Series 438", AMR_SIG_438},
+ {"Series 762", AMR_SIG_762},
+ {"Integrated HP NetRAID (T5)", AMR_SIG_T5},
+ {"Series 466", AMR_SIG_466},
+ {"Series 467", AMR_SIG_467},
+ {"Integrated HP NetRAID (T7)", AMR_SIG_T7},
+ {"Series 490", AMR_SIG_490},
+ {NULL, 0},
+ {"unknown adapter", 0}
+};
+
+#endif
diff --git a/sys/dev/amr/amrio.h b/sys/dev/amr/amrio.h
new file mode 100644
index 0000000..48ec4e8
--- /dev/null
+++ b/sys/dev/amr/amrio.h
@@ -0,0 +1,109 @@
+/*-
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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$
+ */
+
+/*
+ * ioctl interface
+ */
+
+#include <sys/ioccom.h>
+
+/*
+ * Fetch the driver's interface version.
+ */
+#define AMR_IO_VERSION_NUMBER 0x01
+#define AMR_IO_VERSION _IOR('A', 0x200, int)
+
+/*
+ * Pass a command from userspace through to the adapter.
+ *
+ * Note that in order to be code-compatible with the Linux
+ * interface where possible, the formatting of the au_cmd field is
+ * somewhat Interesting.
+ *
+ * For normal commands, the layout is (fields from struct amr_mailbox_ioctl):
+ *
+ * 0 mb_command
+ * 1 mb_channel
+ * 2 mb_param
+ * 3 mb_pad[0]
+ * 4 mb_drive
+ *
+ * For SCSI passthrough commands, the layout is:
+ *
+ * 0 AMR_CMD_PASS (0x3)
+ * 1 reserved, 0
+ * 2 cdb length
+ * 3 cdb data
+ * 3+cdb_len passthrough control byte (timeout, ars, islogical)
+ * 4+cdb_len reserved, 0
+ * 5+cdb_len channel
+ * 6+cdb_len target
+ */
+
+struct amr_user_ioctl {
+ unsigned char au_cmd[32]; /* command text from userspace */
+ void *au_buffer; /* data buffer in userspace */
+ unsigned long au_length; /* data buffer size (0 == no data) */
+ int au_direction; /* data transfer direction */
+#define AMR_IO_NODATA 0
+#define AMR_IO_READ 1
+#define AMR_IO_WRITE 2
+ int au_status; /* command status returned by adapter */
+};
+
+#define AMR_IO_COMMAND _IOWR('A', 0x201, struct amr_user_ioctl)
+
diff --git a/sys/dev/amr/amrreg.h b/sys/dev/amr/amrreg.h
new file mode 100644
index 0000000..0785038
--- /dev/null
+++ b/sys/dev/amr/amrreg.h
@@ -0,0 +1,606 @@
+/*-
+ * Copyright (c) 1999,2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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 parameters
+ ********************************************************************************
+ ********************************************************************************/
+
+/*
+ * 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.
+ *
+ * The AMI documentation says that the limit is 26. Unfortunately, there's no way to
+ * cleanly fit more than 16 entries in without a page boundary. But is this a concern,
+ * since we allocate the s/g maps contiguously anyway?
+ */
+/*
+ * emoore - Oct 21, 2002
+ * firmware doesn't have sglist boundary restrictions.
+ * The sgelem can be set to 26
+ */
+#define AMR_NSEG 26
+
+#define AMR_MAXCMD 255 /* ident = 0 not allowed */
+#define AMR_LIMITCMD 120 /* maximum count of outstanding commands */
+#define AMR_MAXLD 40
+
+#define AMR_MAX_CHANNELS 4
+#define AMR_MAX_TARGETS 15
+#define AMR_MAX_LUNS 7
+#define AMR_MAX_SCSI_CMDS (15 * AMR_MAX_CHANNELS) /* one for every target? */
+
+#define AMR_MAX_CDB_LEN 0x0a
+#define AMR_MAX_EXTCDB_LEN 0x10
+#define AMR_MAX_REQ_SENSE_LEN 0x20
+
+#define AMR_BLKSIZE 512 /* constant for all controllers */
+
+/*
+ * Perform at-startup board initialisation.
+ * At this point in time, this code doesn't work correctly, so leave it disabled.
+ */
+/*#define AMR_BOARD_INIT*/
+
+/********************************************************************************
+ ********************************************************************************
+ Interface Magic Numbers
+ ********************************************************************************
+ ********************************************************************************/
+
+/*
+ * Mailbox commands
+ */
+#define AMR_CMD_LREAD 0x01
+#define AMR_CMD_LWRITE 0x02
+#define AMR_CMD_PASS 0x03
+#define AMR_CMD_EXT_ENQUIRY 0x04
+#define AMR_CMD_ENQUIRY 0x05
+#define AMR_CMD_FLUSH 0x0a
+#define AMR_CMD_EXT_ENQUIRY2 0x0c
+#define AMR_CONFIG_PRODINFO 0x0e
+#define AMR_CMD_GET_MACHINEID 0x36
+#define AMR_CMD_GET_INITIATOR 0x7d /* returns one byte */
+#define AMR_CMD_CONFIG 0xa1
+#define AMR_CONFIG_PRODUCT_INFO 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
+#define AMR_CMD_EXTPASS 0xe3
+
+/*
+ * Command results
+ */
+#define AMR_STATUS_SUCCESS 0x00
+#define AMR_STATUS_ABORTED 0x02
+#define AMR_STATUS_FAILED 0x80
+
+/*
+ * Physical/logical drive states
+ */
+#define AMR_DRV_CURSTATE(x) ((x) & 0x0f)
+#define AMR_DRV_PREVSTATE(x) (((x) >> 4) & 0x0f)
+#define AMR_DRV_OFFLINE 0x00
+#define AMR_DRV_DEGRADED 0x01
+#define AMR_DRV_OPTIMAL 0x02
+#define AMR_DRV_ONLINE 0x03
+#define AMR_DRV_FAILED 0x04
+#define AMR_DRV_REBUILD 0x05
+#define AMR_DRV_HOTSPARE 0x06
+
+/*
+ * Logical drive properties
+ */
+#define AMR_DRV_RAID_MASK 0x0f /* RAID level 0, 1, 3, 5, etc. */
+#define AMR_DRV_WRITEBACK 0x10 /* write-back enabled */
+#define AMR_DRV_READHEAD 0x20 /* readhead policy enabled */
+#define AMR_DRV_ADAPTIVE 0x40 /* adaptive I/O policy enabled */
+
+/*
+ * Battery status
+ */
+#define AMR_BATT_MODULE_MISSING 0x01
+#define AMR_BATT_LOW_VOLTAGE 0x02
+#define AMR_BATT_TEMP_HIGH 0x04
+#define AMR_BATT_PACK_MISSING 0x08
+#define AMR_BATT_CHARGE_MASK 0x30
+#define AMR_BATT_CHARGE_DONE 0x00
+#define AMR_BATT_CHARGE_INPROG 0x10
+#define AMR_BATT_CHARGE_FAIL 0x20
+#define AMR_BATT_CYCLES_EXCEEDED 0x40
+
+
+/********************************************************************************
+ ********************************************************************************
+ 8LD Firmware Interface
+ ********************************************************************************
+ ********************************************************************************/
+
+/*
+ * Array constraints
+ */
+#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)
+
+/*
+ * Adapter Info structure
+ */
+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 aa_boardtype;
+ u_int8_t aa_scsisensealert;
+ u_int8_t aa_writeconfigcount;
+ u_int8_t aa_driveinsertioncount;
+ u_int8_t aa_inserteddrive;
+ u_int8_t aa_batterystatus;
+ u_int8_t res1;
+} __packed;
+
+/*
+ * Logical Drive info structure
+ */
+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];
+} __packed;
+
+/*
+ * Physical Drive info structure
+ */
+struct amr_physdrive_info
+{
+ u_int8_t ap_state[AMR_8LD_MAXPHYSDRIVES]; /* low nibble current state, high nibble previous state */
+ u_int8_t ap_predictivefailure;
+} __packed;
+
+/*
+ * Enquiry response structure for AMR_CMD_ENQUIRY, AMR_CMD_EXT_ENQUIRY and
+ * AMR_CMD_EXT_ENQUIRY2.
+ * ENQUIRY EXT_ENQUIRY EXT_ENQUIRY2
+ */
+struct amr_enquiry
+{
+ struct amr_adapter_info ae_adapter; /* X X X */
+ struct amr_logdrive_info ae_ldrv; /* X X X */
+ struct amr_physdrive_info ae_pdrv; /* X X X */
+ u_int8_t ae_formatting[AMR_8LD_MAXDRIVES];/* X X */
+ u_int8_t res1[AMR_8LD_MAXDRIVES]; /* X X */
+ u_int32_t ae_extlen; /* X */
+ u_int16_t ae_subsystem; /* X */
+ u_int16_t ae_subvendor; /* X */
+ u_int32_t ae_signature; /* X */
+#define AMR_SIG_431 0xfffe0001
+#define AMR_SIG_438 0xfffd0002
+#define AMR_SIG_762 0xfffc0003
+#define AMR_SIG_T5 0xfffb0004
+#define AMR_SIG_466 0xfffa0005
+#define AMR_SIG_467 0xfff90006
+#define AMR_SIG_T7 0xfff80007
+#define AMR_SIG_490 0xfff70008
+ u_int8_t res2[844]; /* X */
+} __packed;
+
+
+/********************************************************************************
+ ********************************************************************************
+ 40LD Firmware Interface
+ ********************************************************************************
+ ********************************************************************************/
+
+/*
+ * Array constraints
+ */
+#define AMR_40LD_MAXDRIVES 40
+#define AMR_40LD_MAXCHAN 16
+#define AMR_40LD_MAXTARG 16
+#define AMR_40LD_MAXPHYSDRIVES 256
+
+/*
+ * Product Info structure
+ */
+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 */
+} __packed;
+
+/*
+ * Notify structure
+ */
+struct amr_notify
+{
+ u_int32_t an_globalcounter; /* change counter */
+
+ u_int8_t an_paramcounter; /* parameter change counter */
+ u_int8_t an_paramid;
+#define AMR_PARAM_REBUILD_RATE 0x01 /* value = new rebuild rate */
+#define AMR_PARAM_FLUSH_INTERVAL 0x02 /* value = new flush interval */
+#define AMR_PARAM_SENSE_ALERT 0x03 /* value = last physical drive with check condition set */
+#define AMR_PARAM_DRIVE_INSERTED 0x04 /* value = last physical drive inserted */
+#define AMR_PARAM_BATTERY_STATUS 0x05 /* value = battery status */
+ u_int16_t an_paramval;
+
+ u_int8_t an_writeconfigcounter; /* write config occurred */
+ u_int8_t res1[3];
+
+ u_int8_t an_ldrvopcounter; /* logical drive operation started/completed */
+ u_int8_t an_ldrvopid;
+ u_int8_t an_ldrvopcmd;
+#define AMR_LDRVOP_CHECK 0x01
+#define AMR_LDRVOP_INIT 0x02
+#define AMR_LDRVOP_REBUILD 0x03
+ u_int8_t an_ldrvopstatus;
+#define AMR_LDRVOP_SUCCESS 0x00
+#define AMR_LDRVOP_FAILED 0x01
+#define AMR_LDRVOP_ABORTED 0x02
+#define AMR_LDRVOP_CORRECTED 0x03
+#define AMR_LDRVOP_STARTED 0x04
+
+ u_int8_t an_ldrvstatecounter; /* logical drive state change occurred */
+ u_int8_t an_ldrvstateid;
+ u_int8_t an_ldrvstatenew;
+ u_int8_t an_ldrvstateold;
+
+ u_int8_t an_pdrvstatecounter; /* physical drive state change occurred */
+ u_int8_t an_pdrvstateid;
+ u_int8_t an_pdrvstatenew;
+ u_int8_t an_pdrvstateold;
+
+ u_int8_t an_pdrvfmtcounter;
+ u_int8_t an_pdrvfmtid;
+ u_int8_t an_pdrvfmtval;
+#define AMR_FORMAT_START 0x01
+#define AMR_FORMAT_COMPLETE 0x02
+ u_int8_t res2;
+
+ u_int8_t an_targxfercounter; /* scsi xfer rate change */
+ u_int8_t an_targxferid;
+ u_int8_t an_targxferval;
+ u_int8_t res3;
+
+ u_int8_t an_fcloopidcounter; /* FC/AL loop ID changed */
+ u_int8_t an_fcloopidpdrvid;
+ u_int8_t an_fcloopid0;
+ u_int8_t an_fcloopid1;
+
+ u_int8_t an_fcloopstatecounter; /* FC/AL loop status changed */
+ u_int8_t an_fcloopstate0;
+ u_int8_t an_fcloopstate1;
+ u_int8_t res4;
+} __packed;
+
+/*
+ * Enquiry3 structure
+ */
+struct amr_enquiry3
+{
+ u_int32_t ae_datasize; /* valid data size in this structure */
+ union { /* event notify structure */
+ struct amr_notify n;
+ u_int8_t pad[0x80];
+ } ae_notify;
+ u_int8_t ae_rebuildrate; /* current rebuild rate in % */
+ u_int8_t ae_cacheflush; /* flush interval in seconds */
+ u_int8_t ae_sensealert;
+ u_int8_t ae_driveinsertcount; /* count of inserted drives */
+ u_int8_t ae_batterystatus;
+ u_int8_t ae_numldrives;
+ u_int8_t ae_reconstate[AMR_40LD_MAXDRIVES / 8]; /* reconstruction state */
+ u_int16_t ae_opstatus[AMR_40LD_MAXDRIVES / 8]; /* operation status per drive */
+ u_int32_t ae_drivesize[AMR_40LD_MAXDRIVES]; /* logical drive size */
+ u_int8_t ae_driveprop[AMR_40LD_MAXDRIVES]; /* logical drive properties */
+ u_int8_t ae_drivestate[AMR_40LD_MAXDRIVES]; /* physical drive state */
+ u_int16_t ae_driveformat[AMR_40LD_MAXPHYSDRIVES];
+ u_int8_t ae_targxfer[80]; /* physical drive transfer rates */
+
+ u_int8_t res1[263]; /* pad to 1024 bytes */
+} __packed;
+
+
+/********************************************************************************
+ ********************************************************************************
+ Mailbox and Command Structures
+ ********************************************************************************
+ ********************************************************************************/
+
+#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];
+} __packed;
+
+struct amr_mailbox64
+{
+ u_int32_t mb64_segment; /* for 64-bit controllers */
+ struct amr_mailbox mb;
+} __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 mb_pad[4];
+ 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_completed[46];
+ u_int8_t mb_poll;
+ u_int8_t mb_ack;
+ u_int8_t res4[16];
+} __packed;
+
+struct amr_sgentry
+{
+ u_int32_t sg_addr;
+ u_int32_t sg_count;
+} __packed;
+
+struct amr_passthrough
+{
+ u_int8_t ap_timeout:3;
+ u_int8_t ap_ars:1;
+ u_int8_t ap_dummy:3;
+ u_int8_t ap_islogical:1;
+ u_int8_t ap_logical_drive_no;
+ u_int8_t ap_channel;
+ u_int8_t ap_scsi_id;
+ u_int8_t ap_queue_tag;
+ u_int8_t ap_queue_action;
+ u_int8_t ap_cdb[AMR_MAX_CDB_LEN];
+ u_int8_t ap_cdb_length;
+ u_int8_t ap_request_sense_length;
+ u_int8_t ap_request_sense_area[AMR_MAX_REQ_SENSE_LEN];
+ u_int8_t ap_no_sg_elements;
+ u_int8_t ap_scsi_status;
+ u_int32_t ap_data_transfer_address;
+ u_int32_t ap_data_transfer_length;
+} __packed;
+
+struct amr_ext_passthrough
+{
+ u_int8_t ap_timeout:3;
+ u_int8_t ap_ars:1;
+ u_int8_t ap_rsvd1:1;
+ u_int8_t ap_cd_rom:1;
+ u_int8_t ap_rsvd2:1;
+ u_int8_t ap_islogical:1;
+ u_int8_t ap_logical_drive_no;
+ u_int8_t ap_channel;
+ u_int8_t ap_scsi_id;
+ u_int8_t ap_queue_tag;
+ u_int8_t ap_queue_action;
+ u_int8_t ap_cdb_length;
+ u_int8_t ap_rsvd3;
+ u_int8_t ap_cdb[AMR_MAX_EXTCDB_LEN];
+ u_int8_t ap_no_sg_elements;
+ u_int8_t ap_scsi_status;
+ u_int8_t ap_request_sense_length;
+ u_int8_t ap_request_sense_area[AMR_MAX_REQ_SENSE_LEN];
+ u_int8_t ap_rsvd4;
+ u_int32_t ap_data_transfer_address;
+ u_int32_t ap_data_transfer_length;
+} __packed;
+
+#ifdef _KERNEL
+/********************************************************************************
+ ********************************************************************************
+ "Quartz" i960 PCI bridge interface
+ ********************************************************************************
+ ********************************************************************************/
+
+#define AMR_CFG_SIG 0xa0 /* PCI config register for signature */
+#define AMR_SIGNATURE_1 0xCCCC /* i960 signature (older adapters) */
+#define AMR_SIGNATURE_2 0x3344 /* i960 signature (newer adapters) */
+
+/*
+ * 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 */
+
+/*
+ * Initialisation status
+ */
+#define AMR_QINIT_SCAN 0x01 /* init scanning drives */
+#define AMR_QINIT_SCANINIT 0x02 /* init scanning initialising */
+#define AMR_QINIT_FIRMWARE 0x03 /* init firmware initing */
+#define AMR_QINIT_INPROG 0xdc /* init in progress */
+#define AMR_QINIT_SPINUP 0x2c /* init spinning drives */
+#define AMR_QINIT_NOMEM 0xac /* insufficient memory */
+#define AMR_QINIT_CACHEFLUSH 0xbc /* init flushing cache */
+#define AMR_QINIT_DONE 0x9c /* init successfully done */
+
+/*
+ * I/O primitives
+ */
+#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)
+
+#ifdef AMR_BOARD_INIT
+#define AMR_QRESET(sc) \
+ do { \
+ pci_write_config((sc)->amr_dev, 0x40, pci_read_config((sc)->amr_dev, 0x40, 1) | 0x20, 1); \
+ pci_write_config((sc)->amr_dev, 0x64, 0x1122, 1); \
+ } while (0)
+#define AMR_QGET_INITSTATUS(sc) pci_read_config((sc)->amr_dev, 0x9c, 1)
+#define AMR_QGET_INITCHAN(sc) pci_read_config((sc)->amr_dev, 0x9f, 1)
+#define AMR_QGET_INITTARG(sc) pci_read_config((sc)->amr_dev, 0x9e, 1)
+#endif
+
+/********************************************************************************
+ ********************************************************************************
+ "Standard" old-style ASIC bridge interface
+ ********************************************************************************
+ ********************************************************************************/
+
+/*
+ * 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 */
+
+/*
+ * 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 */
+
+/*
+ * Initialisation status
+ */
+#define AMR_SINIT_ABEND 0xee /* init abnormal terminated */
+#define AMR_SINIT_NOMEM 0xca /* insufficient memory */
+#define AMR_SINIT_CACHEFLUSH 0xbb /* firmware flushing cache */
+#define AMR_SINIT_INPROG 0x11 /* init in progress */
+#define AMR_SINIT_SPINUP 0x22 /* firmware spinning drives */
+#define AMR_SINIT_DONE 0x99 /* init successfully done */
+
+/*
+ * I/O primitives
+ */
+#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)
+
+#ifdef AMR_BOARD_INIT
+#define AMR_SRESET(sc) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, 0, 0x80)
+#define AMR_SGET_INITSTATUS(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE)
+#define AMR_SGET_FAILDRIVE(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE + 1)
+#define AMR_SGET_INITCHAN(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE + 2)
+#define AMR_SGET_INITTARG(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE + 3)
+#endif
+
+#endif /* _KERNEL */
diff --git a/sys/dev/amr/amrvar.h b/sys/dev/amr/amrvar.h
new file mode 100644
index 0000000..a28fa47
--- /dev/null
+++ b/sys/dev/amr/amrvar.h
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 1999,2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * Copyright (c) 2002 Eric Moore
+ * Copyright (c) 2002 LSI Logic Corporation
+ * 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. The party using or redistributing the source code and binary forms
+ * agrees to the disclaimer below and the terms and conditions set forth
+ * herein.
+ *
+ * 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$
+ */
+
+#if __FreeBSD_version >= 500005
+# include <sys/taskqueue.h>
+# include <geom/geom_disk.h>
+#endif
+
+#ifdef AMR_DEBUG
+# define debug(level, fmt, args...) do {if (level <= AMR_DEBUG) printf("%s: " fmt "\n", __func__ , ##args);} while(0)
+# define debug_called(level) do {if (level <= AMR_DEBUG) printf("%s: called\n", __func__);} while(0)
+#else
+# define debug(level, fmt, args...)
+# define debug_called(level)
+#endif
+#define xdebug(fmt, args...) printf("%s: " fmt "\n", __func__ , ##args)
+
+/*
+ * 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;
+};
+
+/*
+ * Due to the difficulty of using the zone allocator to create a new
+ * zone from within a module, we use our own clustering to reduce
+ * memory wastage due to allocating lots of these small structures.
+ *
+ * 16k gives us a little under 200 command structures, which should
+ * normally be plenty. We will grab more if we need them.
+ */
+
+#define AMR_CMD_CLUSTERSIZE (16 * 1024)
+
+/*
+ * 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; /* command completion status */
+ struct amr_mailbox ac_mailbox;
+ int ac_flags;
+#define AMR_CMD_DATAIN (1<<0)
+#define AMR_CMD_DATAOUT (1<<1)
+#define AMR_CMD_CCB_DATAIN (1<<2)
+#define AMR_CMD_CCB_DATAOUT (1<<3)
+#define AMR_CMD_PRIORITY (1<<4)
+#define AMR_CMD_MAPPED (1<<5)
+#define AMR_CMD_SLEEP (1<<6)
+#define AMR_CMD_BUSY (1<<7)
+
+ struct bio *ac_bio;
+
+ void *ac_data;
+ size_t ac_length;
+ bus_dmamap_t ac_dmamap;
+ u_int32_t ac_dataphys;
+
+ void *ac_ccb_data;
+ size_t ac_ccb_length;
+ bus_dmamap_t ac_ccb_dmamap;
+ u_int32_t ac_ccb_dataphys;
+
+ void (* ac_complete)(struct amr_command *ac);
+ void *ac_private;
+};
+
+struct amr_command_cluster
+{
+ TAILQ_ENTRY(amr_command_cluster) acc_link;
+ struct amr_command acc_command[0];
+};
+
+#define AMR_CMD_CLUSTERCOUNT ((AMR_CMD_CLUSTERSIZE - sizeof(struct amr_command_cluster)) / \
+ sizeof(struct amr_command))
+
+/*
+ * Per-controller-instance data
+ */
+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 */
+ volatile struct amr_mailbox *amr_mailbox;
+ volatile 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 */
+ int amr_maxchan; /* count of SCSI channels */
+
+ /* connected logical drives */
+ struct amr_logdrive amr_drive[AMR_MAXLD];
+
+ /* controller state */
+ 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 bio_queue_head amr_bioq; /* pending I/O with no commands */
+ TAILQ_HEAD(,amr_command) amr_ready; /* commands ready to be submitted */
+ struct amr_command *amr_busycmd[AMR_MAXCMD];
+ int amr_busyslots;
+ TAILQ_HEAD(,amr_command) amr_completed;
+ TAILQ_HEAD(,amr_command) amr_freecmds;
+ TAILQ_HEAD(,amr_command_cluster) amr_cmd_clusters;
+
+ /* CAM attachments for passthrough */
+ struct cam_sim *amr_cam_sim[AMR_MAX_CHANNELS];
+ TAILQ_HEAD(, ccb_hdr) amr_cam_ccbq;
+
+ /* control device */
+ dev_t amr_dev_t;
+
+ /* controller type-specific support */
+ int amr_type;
+#define AMR_TYPE_QUARTZ (1<<0)
+#define AMR_IS_QUARTZ(sc) ((sc)->amr_type & AMR_TYPE_QUARTZ)
+#define AMR_TYPE_40LD (1<<1)
+#define AMR_IS_40LD(sc) ((sc)->amr_type & AMR_TYPE_40LD)
+ int (* amr_submit_command)(struct amr_softc *sc);
+ int (* amr_get_work)(struct amr_softc *sc, struct amr_mailbox *mbsave);
+ int (*amr_poll_command)(struct amr_command *ac);
+ int support_ext_cdb; /* greater than 10 byte cdb support */
+
+ /* misc glue */
+ struct intr_config_hook amr_ich; /* wait-for-interrupts probe hook */
+ struct callout_handle amr_timeout; /* periodic status check */
+#if __FreeBSD_version >= 500005
+ struct task amr_task_complete; /* deferred-completion task */
+#endif
+};
+
+/*
+ * Interface between bus connections and driver core.
+ */
+extern int amr_attach(struct amr_softc *sc);
+extern void amr_free(struct amr_softc *sc);
+extern int amr_flush(struct amr_softc *sc);
+extern int amr_done(struct amr_softc *sc);
+extern void amr_startio(struct amr_softc *sc);
+
+/*
+ * Command buffer allocation.
+ */
+extern struct amr_command *amr_alloccmd(struct amr_softc *sc);
+extern void amr_releasecmd(struct amr_command *ac);
+
+/*
+ * CAM interface
+ */
+extern int amr_cam_attach(struct amr_softc *sc);
+extern void amr_cam_detach(struct amr_softc *sc);
+extern int amr_cam_command(struct amr_softc *sc, struct amr_command **acp);
+
+/*
+ * 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;
+ int amrd_unit;
+};
+
+/*
+ * Interface between driver core and disk driver (should be using a bus?)
+ */
+extern int amr_submit_bio(struct amr_softc *sc, struct bio *bio);
+extern void amrd_intr(void *data);
+
+/********************************************************************************
+ * Enqueue/dequeue functions
+ */
+static __inline void
+amr_enqueue_bio(struct amr_softc *sc, struct bio *bio)
+{
+ int s;
+
+ s = splbio();
+ bioq_insert_tail(&sc->amr_bioq, bio);
+ splx(s);
+}
+
+static __inline struct bio *
+amr_dequeue_bio(struct amr_softc *sc)
+{
+ struct bio *bio;
+ int s;
+
+ s = splbio();
+ if ((bio = bioq_first(&sc->amr_bioq)) != NULL)
+ bioq_remove(&sc->amr_bioq, bio);
+ splx(s);
+ return(bio);
+}
+
+static __inline void
+amr_enqueue_ready(struct amr_command *ac)
+{
+ int s;
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&ac->ac_sc->amr_ready, ac, ac_link);
+ splx(s);
+}
+
+static __inline void
+amr_requeue_ready(struct amr_command *ac)
+{
+ int s;
+
+ s = splbio();
+ TAILQ_INSERT_HEAD(&ac->ac_sc->amr_ready, ac, ac_link);
+ splx(s);
+}
+
+static __inline struct amr_command *
+amr_dequeue_ready(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ int s;
+
+ s = splbio();
+ if ((ac = TAILQ_FIRST(&sc->amr_ready)) != NULL)
+ TAILQ_REMOVE(&sc->amr_ready, ac, ac_link);
+ splx(s);
+ return(ac);
+}
+
+static __inline void
+amr_enqueue_completed(struct amr_command *ac)
+{
+ int s;
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&ac->ac_sc->amr_completed, ac, ac_link);
+ splx(s);
+}
+
+static __inline struct amr_command *
+amr_dequeue_completed(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ int s;
+
+ s = splbio();
+ if ((ac = TAILQ_FIRST(&sc->amr_completed)) != NULL)
+ TAILQ_REMOVE(&sc->amr_completed, ac, ac_link);
+ splx(s);
+ return(ac);
+}
+
+static __inline void
+amr_enqueue_free(struct amr_command *ac)
+{
+ int s;
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&ac->ac_sc->amr_freecmds, ac, ac_link);
+ splx(s);
+}
+
+static __inline struct amr_command *
+amr_dequeue_free(struct amr_softc *sc)
+{
+ struct amr_command *ac;
+ int s;
+
+ s = splbio();
+ if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL)
+ TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
+ splx(s);
+ return(ac);
+}
OpenPOWER on IntegriCloud