Add tws(4), a driver for the LSI 3ware 9750 series SATA/SAS RAID controllers.
authorSascha Wildner <saw@online.de>
Sat, 8 Jan 2011 20:47:12 +0000 (21:47 +0100)
committerSascha Wildner <saw@online.de>
Sat, 8 Jan 2011 20:47:39 +0000 (21:47 +0100)
It should support the following controllers:

* LSI 3ware SAS 9750-4i
* LSI 3ware SAS 9750-4i4e
* LSI 3ware SAS 9750-8i
* LSI 3ware SAS 9750-8e
* LSI 3ware SAS 9750-16i4e
* LSI 3ware SAS 9750-24i4e

It was tested with the 9750-4i.

Many thanks to LSI Corporation. The driver is a port of their FreeBSD driver
that comes on the Installation CD (manual page added by me).

Thanks to vsrinivas and sephe for helping with finding and fixing a busdma
locking issue.

21 files changed:
share/man/man4/Makefile
share/man/man4/twa.4
share/man/man4/twe.4
share/man/man4/tws.4 [new file with mode: 0644]
sys/conf/files
sys/config/GENERIC
sys/config/GENERIC_SMP
sys/config/LINT
sys/config/X86_64_GENERIC
sys/config/X86_64_GENERIC_SMP
sys/dev/raid/Makefile
sys/dev/raid/tws/Makefile [new file with mode: 0644]
sys/dev/raid/tws/tws.c [new file with mode: 0644]
sys/dev/raid/tws/tws.h [new file with mode: 0644]
sys/dev/raid/tws/tws_cam.c [new file with mode: 0644]
sys/dev/raid/tws/tws_hdm.c [new file with mode: 0644]
sys/dev/raid/tws/tws_hdm.h [new file with mode: 0644]
sys/dev/raid/tws/tws_services.c [new file with mode: 0644]
sys/dev/raid/tws/tws_services.h [new file with mode: 0644]
sys/dev/raid/tws/tws_user.c [new file with mode: 0644]
sys/dev/raid/tws/tws_user.h [new file with mode: 0644]

index 052208d..b8b8f5e 100644 (file)
@@ -306,6 +306,7 @@ MAN=        aac.4 \
        tun.4 \
        twa.4 \
        twe.4 \
+       tws.4 \
        txp.4 \
        uark.4 \
        ubsa.4 \
index a8a5290..61dd0c7 100644 (file)
@@ -128,6 +128,10 @@ defined, it prints out a whole bunch of debug
 messages, the quantity of which varies depending on the value assigned to
 .Dv TWA_DEBUG
 (0 to 10).
+.Sh SEE ALSO
+.Xr da 4 ,
+.Xr twe 4 ,
+.Xr tws 4
 .Sh AUTHORS
 The
 .Nm
index 926f25c..e37163e 100644 (file)
@@ -253,6 +253,9 @@ No further I/O will be handled.
 The controller was successfully reset,
 and outstanding commands were restarted.
 .El
+.Sh SEE ALSO
+.Xr twa 4 ,
+.Xr tws 4
 .Sh AUTHORS
 .An -nosplit
 The
diff --git a/share/man/man4/tws.4 b/share/man/man4/tws.4
new file mode 100644 (file)
index 0000000..03d7792
--- /dev/null
@@ -0,0 +1,129 @@
+.\"
+.\" Copyright (c) 2010
+.\"    The DragonFly Project.  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. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+.\" COPYRIGHT HOLDERS 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.
+.\"
+.Dd December 20, 2010
+.Dt TWS 4
+.Os
+.Sh NAME
+.Nm tws
+.Nd 3ware 9750 series SATA/SAS RAID controllers driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device scbus"
+.Cd "device tws"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+tws_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for LSI's 3ware 9750 series
+SATA/SAS controllers.
+.Pp
+These controllers are available in 4, 8, 16 or 24-port configurations,
+and support the following RAID levels: 0, 1, 10, 5, 50, 6.
+The device nodes for the controllers are of the form
+.Pa /dev/tws Ns Ar X ,
+where
+.Ar X
+is the controller number.
+The driver is implemented as a SCSI SIM
+under CAM, and, as such, the logical units that it controls are accessible
+via the device nodes,
+.Pa /dev/da Ns Ar Y ,
+where
+.Ar Y
+is the logical unit number.
+.Sh HARDWARE
+The
+.Nm
+driver supports the following SATA RAID controllers:
+.Pp
+.Bl -bullet -compact
+.It
+LSI's 3ware SAS 9750-4i
+.It
+LSI's 3ware SAS 9750-4i4e
+.It
+LSI's 3ware SAS 9750-8i
+.It
+LSI's 3ware SAS 9750-8e
+.It
+LSI's 3ware SAS 9750-16i4e
+.It
+LSI's 3ware SAS 9750-24i4e
+.El
+.Sh SEE ALSO
+.Xr da 4 ,
+.Xr twa 4 ,
+.Xr twe 4
+.Sh COPYRIGHT
+.Bd -literal
+Copyright (c) 2010, LSI Corp.
+All rights reserved.
+Author : Manjunath Ranganathaiah
+Support: freebsdraid@lsi.com
+
+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. Neither the name of the <ORGANIZATION> nor the names of its
+   contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+COPYRIGHT HOLDER 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.
+.Ed
index 41dfcfc..3707744 100644 (file)
@@ -732,6 +732,11 @@ dev/raid/twa/tw_osl_cam.c          optional twa
 dev/raid/twa/tw_osl_freebsd.c          optional twa
 dev/raid/twe/twe.c                     optional twe
 dev/raid/twe/twe_freebsd.c             optional twe
+dev/raid/tws/tws.c                     optional tws
+dev/raid/tws/tws_cam.c                 optional tws
+dev/raid/tws/tws_hdm.c                 optional tws
+dev/raid/tws/tws_services.c            optional tws
+dev/raid/tws/tws_user.c                        optional tws
 dev/netif/tx/if_tx.c                   optional tx
 dev/netif/txp/if_txp.c         optional txp
 dev/raid/vinum/vinum.c         optional vinum
index 9d88a80..22933db 100644 (file)
@@ -162,6 +162,7 @@ device              hptmv           # Highpoint RocketRAID 182x
 device         iir             # Intel Integrated RAID
 device         mly             # Mylex AcceleRAID/eXtremeRAID
 device         twa             # 3ware 9000 series PATA/SATA RAID
+device         tws             # 3ware 9750 series SATA/SAS RAID
 
 # RAID controllers
 device         aac             # Adaptec FSA RAID, Dell PERC2/PERC3
index 371a30e..329b051 100644 (file)
@@ -163,6 +163,7 @@ device              hptmv           # Highpoint RocketRAID 182x
 device         iir             # Intel Integrated RAID
 device         mly             # Mylex AcceleRAID/eXtremeRAID
 device         twa             # 3ware 9000 series PATA/SATA RAID
+device         tws             # 3ware 9750 series SATA/SAS RAID
 
 # RAID controllers
 device         aac             # Adaptec FSA RAID, Dell PERC2/PERC3
index 323c777..3e8a786 100644 (file)
@@ -1283,6 +1283,7 @@ device            hptiop
 device         twe             # 3ware ATA RAID
 device         twa             # 3ware 9000 series PATA/SATA RAID
 options        TWA_DEBUG=10    # enable debug messages
+device         tws             # 3ware 9750 series SATA/SAS RAID
 
 #
 # Promise Supertrack SX6000
index e242026..1f2cf4c 100644 (file)
@@ -149,6 +149,7 @@ device              hptmv           # Highpoint RocketRAID 182x
 device         iir             # Intel Integrated RAID
 device         mly             # Mylex AcceleRAID/eXtremeRAID
 device         twa             # 3ware 9000 series PATA/SATA RAID
+device         tws             # 3ware 9750 series SATA/SAS RAID
 
 # RAID controllers
 device         aac             # Adaptec FSA RAID, Dell PERC2/PERC3
index b0cd69a..0ef4887 100644 (file)
@@ -149,6 +149,7 @@ device              hptmv           # Highpoint RocketRAID 182x
 device         iir             # Intel Integrated RAID
 device         mly             # Mylex AcceleRAID/eXtremeRAID
 device         twa             # 3ware 9000 series PATA/SATA RAID
+device         tws             # 3ware 9750 series SATA/SAS RAID
 
 # RAID controllers
 device         aac             # Adaptec FSA RAID, Dell PERC2/PERC3
index 8a343fc..3ce204e 100644 (file)
@@ -1,4 +1,4 @@
 SUBDIR=        aac amr arcmsr asr ciss hptiop hptmv \
-       iir ips mfi mlx mly pst twa twe vinum
+       iir ips mfi mlx mly pst twa twe tws vinum
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/raid/tws/Makefile b/sys/dev/raid/tws/Makefile
new file mode 100644 (file)
index 0000000..8781c53
--- /dev/null
@@ -0,0 +1,8 @@
+# Makefile for tws driver
+
+KMOD=   tws
+
+SRCS=   tws.c tws_services.c tws_cam.c tws_hdm.c tws_user.c
+SRCS+=  device_if.h bus_if.h pci_if.h opt_cam.h opt_scsi.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/raid/tws/tws.c b/sys/dev/raid/tws/tws.c
new file mode 100644 (file)
index 0000000..f30b49d
--- /dev/null
@@ -0,0 +1,920 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+#include <dev/raid/tws/tws.h>
+#include <dev/raid/tws/tws_services.h>
+#include <dev/raid/tws/tws_hdm.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+
+MALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver");
+int tws_queue_depth = TWS_MAX_REQS;
+int tws_enable_msi = 0;
+int tws_enable_msix = 0;
+
+
+
+/* externs */
+extern int tws_cam_attach(struct tws_softc *sc);
+extern void tws_cam_detach(struct tws_softc *sc);
+extern int tws_init_ctlr(struct tws_softc *sc);
+extern boolean tws_ctlr_ready(struct tws_softc *sc);
+extern void tws_turn_off_interrupts(struct tws_softc *sc);
+extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type );
+extern struct tws_request *tws_q_remove_request(struct tws_softc *sc,
+                                   struct tws_request *req, u_int8_t q_type );
+extern struct tws_request *tws_q_remove_head(struct tws_softc *sc,
+                                                       u_int8_t q_type );
+extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id);
+extern boolean tws_ctlr_reset(struct tws_softc *sc);
+extern void tws_intr(void *arg);
+extern int tws_use_32bit_sgls;
+
+
+struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type);
+int tws_init_connect(struct tws_softc *sc, u_int16_t mc);
+void tws_send_event(struct tws_softc *sc, u_int8_t event);
+uint8_t tws_get_state(struct tws_softc *sc);
+void tws_release_request(struct tws_request *req);
+
+
+
+/* Function prototypes */
+static d_open_t     tws_open;
+static d_close_t    tws_close;
+static d_read_t     tws_read;
+static d_write_t    tws_write;
+extern d_ioctl_t    tws_ioctl;
+
+static int tws_init(struct tws_softc *sc);
+static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
+                           int nseg, int error);
+
+static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size);
+static int tws_init_aen_q(struct tws_softc *sc);
+static int tws_init_trace_q(struct tws_softc *sc);
+static int tws_setup_irq(struct tws_softc *sc);
+static int tws_setup_intr(struct tws_softc *sc, int irqs);
+
+
+/* Character device entry points */
+
+static struct dev_ops tws_ops = {
+    { "tws", 0, 0 },
+    .d_open =   tws_open,
+    .d_close =  tws_close,
+    .d_read =   tws_read,
+    .d_write =  tws_write,
+    .d_ioctl =  tws_ioctl,
+};
+
+/*
+ * In the cdevsw routines, we find our softc by using the si_drv1 member
+ * of struct cdev.  We set this variable to point to our softc in our
+ * attach routine when we create the /dev entry.
+ */
+
+int
+tws_open(struct dev_open_args *ap)
+{
+    cdev_t dev = ap->a_head.a_dev;
+    struct tws_softc *sc = dev->si_drv1;
+
+    if ( sc )
+        TWS_TRACE_DEBUG(sc, "entry", dev, oflags);
+    return (0);
+}
+
+int
+tws_close(struct dev_close_args *ap)
+{
+    cdev_t dev = ap->a_head.a_dev;
+    struct tws_softc *sc = dev->si_drv1;
+
+    if ( sc )
+        TWS_TRACE_DEBUG(sc, "entry", dev, fflag);
+    return (0);
+}
+
+int
+tws_read(struct dev_read_args *ap)
+{
+    cdev_t dev = ap->a_head.a_dev;
+    struct tws_softc *sc = dev->si_drv1;
+
+    if ( sc )
+        TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
+    return (0);
+}
+
+int
+tws_write(struct dev_write_args *ap)
+{
+    cdev_t dev = ap->a_head.a_dev;
+    struct tws_softc *sc = dev->si_drv1;
+
+    if ( sc )
+        TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
+    return (0);
+}
+
+/* PCI Support Functions */
+
+/*
+ * Compare the device ID of this device against the IDs that this driver
+ * supports.  If there is a match, set the description and return success.
+ */
+static int
+tws_probe(device_t dev)
+{
+    static u_int8_t first_ctlr = 1;
+
+    if ((pci_get_vendor(dev) == TWS_VENDOR_ID) &&
+        (pci_get_device(dev) == TWS_DEVICE_ID)) {
+        device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller");
+        if (first_ctlr) {
+            kprintf("LSI 3ware device driver for SAS/SATA storage "
+                    "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING);
+            first_ctlr = 0;
+        }
+
+        return(0);
+    }
+    return (ENXIO);
+}
+
+/* Attach function is only called if the probe is successful. */
+
+static int
+tws_attach(device_t dev)
+{
+    struct tws_softc *sc = device_get_softc(dev);
+    u_int32_t cmd, bar;
+    int error=0,i;
+
+    /* no tracing yet */
+    /* Look up our softc and initialize its fields. */
+    sc->tws_dev = dev;
+    sc->device_id = pci_get_device(dev);
+    sc->subvendor_id = pci_get_subvendor(dev);
+    sc->subdevice_id = pci_get_subdevice(dev);
+
+    /* Intialize mutexes */
+    lockinit(&sc->q_lock, "tws_q_lock", 0, LK_CANRECURSE);
+    lockinit(&sc->sim_lock, "tws_sim_lock", 0, LK_CANRECURSE);
+    lockinit(&sc->gen_lock, "tws_gen_lock", 0, LK_CANRECURSE);
+    lockinit(&sc->io_lock, "tws_io_lock", 0, LK_CANRECURSE);
+
+    if ( tws_init_trace_q(sc) == FAILURE )
+        kprintf("trace init failure\n");
+    /* send init event */
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    tws_send_event(sc, TWS_INIT_START);
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+    TWS_TRACE(sc, "BIG endian", 0, 0);
+#endif
+    /* sysctl context setup */
+    sysctl_ctx_init(&sc->tws_clist);
+    sc->tws_oidp = SYSCTL_ADD_NODE(&sc->tws_clist,
+                                   SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
+                                   device_get_nameunit(dev),
+                                   CTLFLAG_RD, 0, "");
+    if ( sc->tws_oidp == NULL ) {
+        tws_log(sc, SYSCTL_TREE_NODE_ADD);
+        goto attach_fail_1;
+    }
+    SYSCTL_ADD_STRING(&sc->tws_clist, SYSCTL_CHILDREN(sc->tws_oidp),
+                      OID_AUTO, "driver_version", CTLFLAG_RD,
+                      TWS_DRIVER_VERSION_STRING, 0, "TWS driver version");
+
+    cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+    if ( (cmd & PCIM_CMD_PORTEN) == 0) {
+        tws_log(sc, PCI_COMMAND_READ);
+        goto attach_fail_1;
+    }
+    /* Force the busmaster enable bit on. */
+    cmd |= PCIM_CMD_BUSMASTEREN;
+    pci_write_config(dev, PCIR_COMMAND, cmd, 2);
+
+    bar = pci_read_config(dev, TWS_PCI_BAR0, 4);
+    TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0);
+    bar = pci_read_config(dev, TWS_PCI_BAR1, 4);
+    bar = bar & ~TWS_BIT2;
+    TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0);
+
+    /* MFA base address is BAR2 register used for
+     * push mode. Firmware will evatualy move to
+     * pull mode during witch this needs to change
+     */
+#ifndef TWS_PULL_MODE_ENABLE
+    sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4);
+    sc->mfa_base = sc->mfa_base & ~TWS_BIT2;
+    TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0);
+#endif
+
+    /* allocate MMIO register space */
+    sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */
+    if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+                                &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE))
+                                == NULL) {
+        tws_log(sc, ALLOC_MEMORY_RES);
+        goto attach_fail_1;
+    }
+    sc->bus_tag = rman_get_bustag(sc->reg_res);
+    sc->bus_handle = rman_get_bushandle(sc->reg_res);
+
+#ifndef TWS_PULL_MODE_ENABLE
+    /* Allocate bus space for inbound mfa */
+    sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */
+    if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+                          &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE))
+                                == NULL) {
+        tws_log(sc, ALLOC_MEMORY_RES);
+        goto attach_fail_2;
+    }
+    sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res);
+    sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res);
+#endif
+
+    /* Allocate and register our interrupt. */
+    sc->intr_type = TWS_INTx; /* default */
+
+    if ( tws_enable_msi )
+        sc->intr_type = TWS_MSI;
+    if ( tws_setup_irq(sc) == FAILURE ) {
+        tws_log(sc, ALLOC_MEMORY_RES);
+        goto attach_fail_3;
+    }
+
+    /* Init callouts. */
+    callout_init(&sc->print_stats_handle);
+    callout_init(&sc->reset_cb_handle);
+    callout_init(&sc->reinit_handle);
+
+    /*
+     * Create a /dev entry for this device.  The kernel will assign us
+     * a major number automatically.  We use the unit number of this
+     * device as the minor number and name the character device
+     * "tws<unit>".
+     */
+    sc->tws_cdev = make_dev(&tws_ops, device_get_unit(dev),
+        UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u",
+        device_get_unit(dev));
+    sc->tws_cdev->si_drv1 = sc;
+
+    if ( tws_init(sc) == FAILURE ) {
+        tws_log(sc, TWS_INIT_FAILURE);
+        goto attach_fail_4;
+    }
+    if ( tws_init_ctlr(sc) == FAILURE ) {
+        tws_log(sc, TWS_CTLR_INIT_FAILURE);
+        goto attach_fail_4;
+    }
+    if ((error = tws_cam_attach(sc))) {
+        tws_log(sc, TWS_CAM_ATTACH);
+        goto attach_fail_4;
+    }
+    /* send init complete event */
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    tws_send_event(sc, TWS_INIT_COMPLETE);
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+    TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id);
+    return(0);
+
+attach_fail_4:
+    for(i=0;i<sc->irqs;i++) {
+        if (sc->intr_handle[i]) {
+            if ((error = bus_teardown_intr(sc->tws_dev,
+                         sc->irq_res[i], sc->intr_handle[i])))
+                TWS_TRACE(sc, "bus teardown intr", 0, error);
+        }
+    }
+    destroy_dev(sc->tws_cdev);
+    dev_ops_remove_minor(&tws_ops, device_get_unit(sc->tws_dev));
+attach_fail_3:
+    for(i=0;i<sc->irqs;i++) {
+        if ( sc->irq_res[i] ){
+            if (bus_release_resource(sc->tws_dev,
+                 SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i]))
+                TWS_TRACE(sc, "bus irq res", 0, 0);
+        }
+    }
+#ifndef TWS_PULL_MODE_ENABLE
+attach_fail_2:
+#endif
+    if ( sc->mfa_res ){
+        if (bus_release_resource(sc->tws_dev,
+                 SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res))
+            TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id);
+    }
+    if ( sc->reg_res ){
+        if (bus_release_resource(sc->tws_dev,
+                 SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))
+            TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id);
+    }
+attach_fail_1:
+    lockuninit(&sc->q_lock);
+    lockuninit(&sc->sim_lock);
+    lockuninit(&sc->gen_lock);
+    lockuninit(&sc->io_lock);
+    sysctl_ctx_free(&sc->tws_clist);
+    return (ENXIO);
+}
+
+/* Detach device. */
+
+static int
+tws_detach(device_t dev)
+{
+    struct tws_softc *sc = device_get_softc(dev);
+    int error, i;
+    u_int32_t reg;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    tws_send_event(sc, TWS_UNINIT_START);
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+    /* needs to disable interrupt before detaching from cam */
+    tws_turn_off_interrupts(sc);
+    /* clear door bell */
+    tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4);
+    reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4);
+    TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0);
+    sc->obfl_q_overrun = false;
+    tws_init_connect(sc, 1);
+
+    /* Teardown the state in our softc created in our attach routine. */
+    /* Disconnect the interrupt handler. */
+    for(i=0;i<sc->irqs;i++) {
+        if (sc->intr_handle[i]) {
+            if ((error = bus_teardown_intr(sc->tws_dev,
+                         sc->irq_res[i], sc->intr_handle[i])))
+                TWS_TRACE(sc, "bus teardown intr", 0, error);
+        }
+    }
+    /* Release irq resource */
+    for(i=0;i<sc->irqs;i++) {
+        if ( sc->irq_res[i] ){
+            if (bus_release_resource(sc->tws_dev,
+                     SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i]))
+                TWS_TRACE(sc, "bus release irq resource",
+                                       i, sc->irq_res_id[i]);
+        }
+    }
+    if ( sc->intr_type == TWS_MSI ) {
+        pci_release_msi(sc->tws_dev);
+    }
+
+    tws_cam_detach(sc);
+
+    /* Release memory resource */
+    if ( sc->mfa_res ){
+        if (bus_release_resource(sc->tws_dev,
+                 SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res))
+            TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id);
+    }
+    if ( sc->reg_res ){
+        if (bus_release_resource(sc->tws_dev,
+                 SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))
+            TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id);
+    }
+
+    kfree(sc->reqs, M_TWS);
+    kfree(sc->sense_bufs, M_TWS);
+    kfree(sc->scan_ccb, M_TWS);
+    kfree(sc->aen_q.q, M_TWS);
+    kfree(sc->trace_q.q, M_TWS);
+    lockuninit(&sc->q_lock);
+    lockuninit(&sc->sim_lock);
+    lockuninit(&sc->gen_lock);
+    lockuninit(&sc->io_lock);
+    destroy_dev(sc->tws_cdev);
+    dev_ops_remove_minor(&tws_ops, device_get_unit(sc->tws_dev));
+    sysctl_ctx_free(&sc->tws_clist);
+    return (0);
+}
+
+static int
+tws_setup_intr(struct tws_softc *sc, int irqs)
+{
+    int i, error;
+
+    for(i=0;i<irqs;i++) {
+        if ((error = bus_setup_intr(sc->tws_dev, sc->irq_res[i],
+                                INTR_MPSAFE,
+                                tws_intr, sc, &sc->intr_handle[i], NULL))) {
+            tws_log(sc, SETUP_INTR_RES);
+            return(FAILURE);
+        }
+    }
+    return(SUCCESS);
+
+}
+
+
+static int
+tws_setup_irq(struct tws_softc *sc)
+{
+    int messages;
+    u_int16_t cmd;
+
+    cmd = pci_read_config(sc->tws_dev, PCIR_COMMAND, 2);
+    switch(sc->intr_type) {
+        case TWS_INTx :
+            cmd = cmd & ~0x0400;
+            pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2);
+            sc->irqs = 1;
+            sc->irq_res_id[0] = 0;
+            sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ,
+                            &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE);
+            if ( ! sc->irq_res[0] )
+                return(FAILURE);
+            if ( tws_setup_intr(sc, sc->irqs) == FAILURE )
+                return(FAILURE);
+            device_printf(sc->tws_dev, "Using legacy INTx\n");
+            break;
+        case TWS_MSI :
+            cmd = cmd | 0x0400;
+            pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2);
+            sc->irqs = 1;
+            sc->irq_res_id[0] = 1;
+            messages = 1;
+            if (pci_alloc_msi(sc->tws_dev, &messages) != 0 ) {
+                TWS_TRACE(sc, "pci alloc msi fail", 0, messages);
+                return(FAILURE);
+            }
+            sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ,
+                              &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE);
+
+            if ( !sc->irq_res[0]  )
+                return(FAILURE);
+            if ( tws_setup_intr(sc, sc->irqs) == FAILURE )
+                return(FAILURE);
+            device_printf(sc->tws_dev, "Using MSI\n");
+            break;
+
+    }
+
+    return(SUCCESS);
+}
+
+static int
+tws_init(struct tws_softc *sc)
+{
+
+    u_int32_t max_sg_elements;
+    u_int32_t dma_mem_size;
+    int error;
+    u_int32_t reg;
+
+    sc->seq_id = 0;
+    if ( tws_queue_depth > TWS_MAX_REQS )
+        tws_queue_depth = TWS_MAX_REQS;
+    if (tws_queue_depth < TWS_RESERVED_REQS+1)
+        tws_queue_depth = TWS_RESERVED_REQS+1;
+    sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false;
+    max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ?
+                                 TWS_MAX_64BIT_SG_ELEMENTS :
+                                 TWS_MAX_32BIT_SG_ELEMENTS;
+    dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) +
+                             (TWS_SECTOR_SIZE) ;
+    if ( bus_dma_tag_create(NULL,                    /* parent */
+                            TWS_ALIGNMENT,           /* alignment */
+                            0,                       /* boundary */
+                            BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+                            BUS_SPACE_MAXADDR,       /* highaddr */
+                            NULL, NULL,              /* filter, filterarg */
+                            BUS_SPACE_MAXSIZE,       /* maxsize */
+                            max_sg_elements,         /* numsegs */
+                            BUS_SPACE_MAXSIZE,       /* maxsegsize */
+                            0,                       /* flags */
+                            &sc->parent_tag          /* tag */
+                           )) {
+        TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements,
+                                                    sc->is64bit);
+        return(ENOMEM);
+    }
+    /* In bound message frame requires 16byte alignment.
+     * Outbound MF's can live with 4byte alignment - for now just
+     * use 16 for both.
+     */
+    if ( bus_dma_tag_create(sc->parent_tag,       /* parent */
+                            TWS_IN_MF_ALIGNMENT,  /* alignment */
+                            0,                    /* boundary */
+                            BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+                            BUS_SPACE_MAXADDR,    /* highaddr */
+                            NULL, NULL,           /* filter, filterarg */
+                            dma_mem_size,         /* maxsize */
+                            1,                    /* numsegs */
+                            BUS_SPACE_MAXSIZE,    /* maxsegsize */
+                            0,                    /* flags */
+                            &sc->cmd_tag          /* tag */
+                           )) {
+        TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit);
+        return(ENOMEM);
+    }
+
+    if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem,
+                    BUS_DMA_NOWAIT, &sc->cmd_map)) {
+        TWS_TRACE_DEBUG(sc, "DMA mem alloc fail", max_sg_elements, sc->is64bit);
+        return(ENOMEM);
+    }
+
+    /* if bus_dmamem_alloc succeeds then bus_dmamap_load will succeed */
+    sc->dma_mem_phys=0;
+    error = bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem,
+                    dma_mem_size, tws_dmamap_cmds_load_cbfn,
+                    &sc->dma_mem_phys, 0);
+
+    if ( error == EINPROGRESS )
+        TWS_TRACE_DEBUG(sc, "req queued", max_sg_elements, sc->is64bit);
+
+   /*
+    * Create a dma tag for data buffers; size will be the maximum
+    * possible I/O size (128kB).
+    */
+    if (bus_dma_tag_create(sc->parent_tag,         /* parent */
+                           TWS_ALIGNMENT,          /* alignment */
+                           0,                      /* boundary */
+                           BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+                           BUS_SPACE_MAXADDR,      /* highaddr */
+                           NULL, NULL,             /* filter, filterarg */
+                           TWS_MAX_IO_SIZE,        /* maxsize */
+                           max_sg_elements,        /* nsegments */
+                           TWS_MAX_IO_SIZE,        /* maxsegsize */
+                          BUS_DMA_ALLOCALL |      /* flags */
+                          BUS_DMA_ALLOCNOW |
+                           BUS_DMA_PRIVBZONE |
+                          BUS_DMA_PROTECTED,
+                           &sc->data_tag           /* tag */)) {
+        TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit);
+        return(ENOMEM);
+    }
+
+    sc->reqs = kmalloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS,
+                      M_WAITOK | M_ZERO);
+    sc->sense_bufs = kmalloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS,
+                      M_WAITOK | M_ZERO);
+    sc->scan_ccb = kmalloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO);
+
+    if ( !tws_ctlr_ready(sc) )
+        if( !tws_ctlr_reset(sc) )
+            return(FAILURE);
+
+    bzero(&sc->stats, sizeof(struct tws_stats));
+    tws_init_qs(sc);
+    tws_turn_off_interrupts(sc);
+
+    /*
+     * enable pull mode by setting bit1 .
+     * setting bit0 to 1 will enable interrupt coalesing
+     * will revisit.
+     */
+
+#ifdef TWS_PULL_MODE_ENABLE
+
+    reg = tws_read_reg(sc, TWS_I2O0_CTL, 4);
+    TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL);
+    tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4);
+
+#endif
+
+    TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL);
+    if ( tws_init_reqs(sc, dma_mem_size) == FAILURE )
+        return(FAILURE);
+    if ( tws_init_aen_q(sc) == FAILURE )
+        return(FAILURE);
+
+    return(SUCCESS);
+
+}
+
+static int
+tws_init_aen_q(struct tws_softc *sc)
+{
+    sc->aen_q.head=0;
+    sc->aen_q.tail=0;
+    sc->aen_q.depth=256;
+    sc->aen_q.overflow=0;
+    sc->aen_q.q = kmalloc(sizeof(struct tws_event_packet)*sc->aen_q.depth,
+                              M_TWS, M_WAITOK | M_ZERO);
+    return(SUCCESS);
+}
+
+static int
+tws_init_trace_q(struct tws_softc *sc)
+{
+    sc->trace_q.head=0;
+    sc->trace_q.tail=0;
+    sc->trace_q.depth=256;
+    sc->trace_q.overflow=0;
+    sc->trace_q.q = kmalloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth,
+                              M_TWS, M_WAITOK | M_ZERO);
+    return(SUCCESS);
+}
+
+static int
+tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size)
+{
+
+    struct tws_command_packet *cmd_buf;
+    cmd_buf = (struct tws_command_packet *)sc->dma_mem;
+    int i;
+
+    bzero(cmd_buf, dma_mem_size);
+    TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0);
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    for ( i=0; i< tws_queue_depth; i++)
+    {
+        if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) {
+            /* log a ENOMEM failure msg here */
+            return(FAILURE);
+        }
+        sc->reqs[i].cmd_pkt =  &cmd_buf[i];
+
+        sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ;
+        sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys +
+                              (i * sizeof(struct tws_command_packet));
+        sc->sense_bufs[i].posted = false;
+
+        sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys +
+                              sizeof(struct tws_command_header) +
+                              (i * sizeof(struct tws_command_packet));
+        sc->reqs[i].request_id = i;
+        sc->reqs[i].sc = sc;
+
+        sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128;
+
+        sc->reqs[i].state = TWS_REQ_STATE_FREE;
+        if ( i >= TWS_RESERVED_REQS )
+            tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q);
+    }
+    lockmgr(&sc->q_lock, LK_RELEASE);
+    return(SUCCESS);
+}
+
+static void
+tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
+                           int nseg, int error)
+{
+
+    /* kprintf("command load done \n"); */
+
+    *((bus_addr_t *)arg) = segs[0].ds_addr;
+}
+
+void
+tws_send_event(struct tws_softc *sc, u_int8_t event)
+{
+    KKASSERT(lockstatus(&sc->gen_lock, curthread) != 0);
+    TWS_TRACE_DEBUG(sc, "received event ", 0, event);
+    switch (event) {
+
+        case TWS_INIT_START:
+            sc->tws_state = TWS_INIT;
+            break;
+
+        case TWS_INIT_COMPLETE:
+            KASSERT(sc->tws_state == TWS_INIT , ("invalid state transition"));
+            sc->tws_state = TWS_ONLINE;
+            break;
+
+        case TWS_RESET_START:
+            /* multiple reset ? */
+            KASSERT(sc->tws_state != TWS_RESET, ("invalid state transition"));
+
+            /* we can transition to reset state from any state */
+            sc->tws_prev_state = sc->tws_state;
+            sc->tws_state = TWS_RESET;
+            break;
+
+        case TWS_RESET_COMPLETE:
+            KASSERT(sc->tws_state == TWS_RESET, ("invalid state transition"));
+            sc->tws_state = sc->tws_prev_state;
+            break;
+
+        case TWS_SCAN_FAILURE:
+            KASSERT(sc->tws_state == TWS_ONLINE , ("invalid state transition"));
+            sc->tws_state = TWS_OFFLINE;
+            break;
+
+        case TWS_UNINIT_START:
+            KASSERT(sc->tws_state == TWS_ONLINE || sc->tws_state == TWS_OFFLINE,
+                           ("invalid state transition"));
+            sc->tws_state = TWS_UNINIT;
+            break;
+    }
+
+}
+
+uint8_t
+tws_get_state(struct tws_softc *sc)
+{
+
+    return((u_int8_t)sc->tws_state);
+
+}
+
+/* Called during system shutdown after sync. */
+
+static int
+tws_shutdown(device_t dev)
+{
+
+    struct tws_softc *sc = device_get_softc(dev);
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+    tws_turn_off_interrupts(sc);
+    tws_init_connect(sc, 1);
+
+    return (0);
+}
+
+/*
+ * Device suspend routine.
+ */
+static int
+tws_suspend(device_t dev)
+{
+    struct tws_softc *sc = device_get_softc(dev);
+
+    if ( sc )
+        TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    return (0);
+}
+
+/*
+ * Device resume routine.
+ */
+static int
+tws_resume(device_t dev)
+{
+
+    struct tws_softc *sc = device_get_softc(dev);
+
+    if ( sc )
+        TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    return (0);
+}
+
+
+struct tws_request *
+tws_get_request(struct tws_softc *sc, u_int16_t type)
+{
+    struct tws_request *r = NULL;
+
+    switch ( type ) {
+        case TWS_INTERNAL_CMD_REQ :
+            lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+            r = &sc->reqs[0];
+            if ( r->state != TWS_REQ_STATE_FREE ) {
+                r = NULL;
+            } else {
+                r->state = TWS_REQ_STATE_BUSY;
+            }
+            lockmgr(&sc->gen_lock, LK_RELEASE);
+            break;
+        case TWS_AEN_FETCH_REQ :
+            lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+            r = &sc->reqs[1];
+            if ( r->state != TWS_REQ_STATE_FREE ) {
+                r = NULL;
+            } else {
+                r->state = TWS_REQ_STATE_BUSY;
+            }
+            lockmgr(&sc->gen_lock, LK_RELEASE);
+            break;
+        case TWS_PASSTHRU_REQ :
+            lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+            r = &sc->reqs[2];
+            if ( r->state != TWS_REQ_STATE_FREE ) {
+                r = NULL;
+            } else {
+                r->state = TWS_REQ_STATE_BUSY;
+            }
+            lockmgr(&sc->gen_lock, LK_RELEASE);
+            break;
+        case TWS_GETSET_PARAM_REQ :
+            lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+            r = &sc->reqs[3];
+            if ( r->state != TWS_REQ_STATE_FREE ) {
+                r = NULL;
+            } else {
+                r->state = TWS_REQ_STATE_BUSY;
+            }
+            lockmgr(&sc->gen_lock, LK_RELEASE);
+            break;
+        case TWS_SCSI_IO_REQ :
+            lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+            r = tws_q_remove_head(sc, TWS_FREE_Q);
+            if ( r )
+                r->state = TWS_REQ_STATE_TRAN;
+            lockmgr(&sc->q_lock, LK_RELEASE);
+            break;
+        default :
+            TWS_TRACE_DEBUG(sc, "Unknown req type", 0, type);
+            r = NULL;
+
+    }
+
+    if ( r ) {
+        bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache));
+       callout_init(&r->thandle);
+        r->data = NULL;
+        r->length = 0;
+        r->type = type;
+        r->flags = TWS_DIR_UNKNOWN;
+        r->error_code = TWS_REQ_ERR_INVALID;
+        r->ccb_ptr = NULL;
+        r->cb = NULL;
+        r->next = r->prev = NULL;
+    }
+    return(r);
+}
+
+void
+tws_release_request(struct tws_request *req)
+{
+
+    struct tws_softc *sc = req->sc;
+
+    TWS_TRACE_DEBUG(sc, "entry", sc, 0);
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    tws_q_insert_tail(sc, req, TWS_FREE_Q);
+    lockmgr(&sc->q_lock, LK_RELEASE);
+}
+
+static device_method_t tws_methods[] = {
+    /* Device interface */
+    DEVMETHOD(device_probe,     tws_probe),
+    DEVMETHOD(device_attach,    tws_attach),
+    DEVMETHOD(device_detach,    tws_detach),
+    DEVMETHOD(device_shutdown,  tws_shutdown),
+    DEVMETHOD(device_suspend,   tws_suspend),
+    DEVMETHOD(device_resume,    tws_resume),
+
+    DEVMETHOD(bus_print_child,      bus_generic_print_child),
+    DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+    { 0, 0 }
+};
+
+static driver_t tws_driver = {
+        "tws",
+        tws_methods,
+        sizeof(struct tws_softc)
+};
+
+
+static devclass_t tws_devclass;
+
+/* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */
+DRIVER_MODULE(tws, pci, tws_driver, tws_devclass, 0, 0);
+MODULE_DEPEND(tws, cam, 1, 1, 1);
+MODULE_DEPEND(tws, pci, 1, 1, 1);
+
+TUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth);
+#if 0 /* XXX swildner */
+TUNABLE_INT("hw.tws.enable_msi", &tws_enable_msi);
+#endif
diff --git a/sys/dev/raid/tws/tws.h b/sys/dev/raid/tws/tws.h
new file mode 100644 (file)
index 0000000..948defe
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws.h,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+#include <sys/param.h>        /* defines used in kernel.h */
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>       /* types used in module initialization */
+#include <sys/conf.h>         /* cdevsw struct */
+#include <sys/uio.h>          /* uio struct */
+#include <sys/malloc.h>
+#include <sys/bus.h>          /* structs, prototypes for pci bus stuff */
+
+#include <sys/rman.h>
+#include <sys/device.h>
+#include <machine/clock.h>
+
+#include <bus/pci/pcivar.h>   /* For pci_get macros! */
+#include <bus/pci/pcireg.h>
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+
+
+#define TWS_PULL_MODE_ENABLE 1
+
+MALLOC_DECLARE(M_TWS);
+/* externs */
+extern int tws_queue_depth;
+
+
+#define TWS_DRIVER_VERSION_STRING "10.80.00.001"
+#define TWS_MAX_NUM_UNITS             65
+#define TWS_MAX_NUM_LUNS              16
+#define TWS_MAX_IRQS                  2
+#define TWS_SCSI_INITIATOR_ID         66
+#define TWS_MAX_IO_SIZE               0x20000 /* 128kB */
+#define TWS_SECTOR_SIZE               0x200
+#define TWS_POLL_TIMEOUT              60
+#define TWS_IO_TIMEOUT                60
+#define TWS_RESET_TIMEOUT             60
+
+#define TWS_PCI_BAR0                  0x10
+#define TWS_PCI_BAR1                  0x14
+#define TWS_PCI_BAR2                  0x1C
+
+#define TWS_VENDOR_ID                 0x13C1
+#define TWS_DEVICE_ID                 0x1010
+
+#define TWS_INVALID_REQID             0xFFFF
+
+/* bus tag related */
+#define TWS_ALIGNMENT                 4
+#define TWS_IN_MF_ALIGNMENT           16
+#define TWS_OUT_MF_ALIGNMENT          4
+
+#define TWS_MAX_32BIT_SG_ELEMENTS     93     /* max 32-bit sg elements */
+#define TWS_MAX_64BIT_SG_ELEMENTS     46     /* max 64-bit sg elements */
+
+#define TWS_MAX_QS                    4
+#define TWS_MAX_REQS                  256
+#define TWS_RESERVED_REQS             4
+
+/* Request states */
+#define TWS_REQ_STATE_TRAN            0
+#define TWS_REQ_STATE_FREE            1
+#define TWS_REQ_STATE_PENDING         2
+#define TWS_REQ_STATE_BUSY            3
+#define TWS_REQ_STATE_COMPLETE        4
+
+/* Request types */
+#define TWS_INTERNAL_CMD_REQ          0x0
+#define TWS_AEN_FETCH_REQ             0x1
+#define TWS_PASSTHRU_REQ              0x2
+#define TWS_GETSET_PARAM_REQ          0x3
+#define TWS_SCSI_IO_REQ               0x4
+
+/* Driver states */
+
+enum tws_states {
+    TWS_INIT=50,
+    TWS_UNINIT,
+    TWS_OFFLINE,
+    TWS_ONLINE,
+    TWS_RESET,
+};
+
+/* events */
+
+enum tws_events {
+    TWS_INIT_START=100,
+    TWS_INIT_COMPLETE,
+    TWS_UNINIT_START,
+    TWS_RESET_START,
+    TWS_RESET_COMPLETE,
+    TWS_SCAN_FAILURE,
+};
+
+enum tws_req_flags {
+    TWS_DIR_UNKNOWN = 0x1,
+    TWS_DIR_IN = 0x2,
+    TWS_DIR_OUT = 0x4,
+    TWS_DIR_NONE = 0x8,
+};
+
+enum tws_intrs {
+     TWS_INTx,
+     TWS_MSI,
+     TWS_MSIX,
+};
+
+struct tws_msix_info {
+    int tbl_res_id;
+    bus_space_tag_t tbl_tag;
+    bus_space_handle_t tbl_handle;
+    struct resource *tbl_res;
+};
+
+struct tws_ioctl_lock {
+    u_int32_t       lock;
+    time_t          timeout;
+};
+
+
+#define TWS_TRACE_FNAME_LEN  10
+#define TWS_TRACE_FUNC_LEN   15
+#define TWS_TRACE_DESC_LEN   10
+struct tws_trace_rec {
+    struct timespec ts;
+    char fname[TWS_TRACE_FNAME_LEN];
+    char func[TWS_TRACE_FUNC_LEN];
+    int linenum;
+    char desc[TWS_TRACE_DESC_LEN];
+    u_int64_t val1;
+    u_int64_t val2;
+};
+
+struct tws_circular_q {
+    volatile int16_t head;
+    volatile int16_t tail;
+    u_int16_t depth;
+    u_int8_t  overflow;
+    void *    q;
+};
+
+
+
+struct tws_stats {
+    u_int64_t reqs_in;
+    u_int64_t reqs_out;
+    u_int64_t reqs_errored;
+    u_int64_t spurios_intrs;
+    u_int64_t num_intrs;
+    u_int64_t num_aens;
+    u_int64_t ioctls;
+    u_int64_t scsi_ios;
+};
+
+struct tws_init_connect_info {
+    u_int16_t     working_srl;
+    u_int16_t     working_branch;
+    u_int16_t     working_build;
+    u_int16_t     fw_on_ctlr_srl;
+    u_int16_t     fw_on_ctlr_branch;
+    u_int16_t     fw_on_ctlr_build;
+
+};
+
+
+/* ------------ boolean types ------------------- */
+
+/* typedef enum _boolean { false, true } boolean; */
+#define boolean _Bool
+enum err { SUCCESS, FAILURE };
+
+/* ----------- per instance data ---------------- */
+
+/* The softc holds our per-instance data. */
+struct tws_softc {
+    device_t    tws_dev;                  /* bus device */
+    struct cdev *tws_cdev;                /* controller device */
+    u_int32_t   device_id;                /* device id */
+    u_int32_t   subvendor_id;             /* device id */
+    u_int32_t   subdevice_id;             /* device id */
+    u_int8_t    tws_state;                /* driver state */
+    u_int8_t    tws_prev_state;           /* driver prev state */
+    struct sysctl_ctx_list tws_clist;     /* sysctl context */
+    struct sysctl_oid *tws_oidp;          /* sysctl context */
+    struct resource *reg_res;             /* register interface window */
+    struct resource *mfa_res;             /* mfa interface window */
+    int reg_res_id;                       /* register resource id */
+    int mfa_res_id;                       /* register resource id */
+    bus_space_handle_t bus_handle;        /* bus space handle */
+    bus_space_handle_t bus_mfa_handle;     /* bus space handle */
+    bus_space_tag_t bus_tag;              /* bus space tag */
+    bus_space_tag_t bus_mfa_tag;          /* bus space tag for mfa's */
+    u_int64_t mfa_base;                   /* mfa base address */
+    struct resource *irq_res[TWS_MAX_IRQS];/* interrupt resource */
+    int irq_res_id[TWS_MAX_IRQS];         /* intr resource id */
+    void *intr_handle[TWS_MAX_IRQS];      /* interrupt handle */
+    int irqs;                             /* intrs used */
+    struct tws_msix_info msix;            /* msix info */
+    struct cam_sim *sim;                  /* sim for this controller */
+    struct cam_path *path;                /* Ctlr path to CAM */
+    struct lock q_lock;                   /* queue lock */
+    struct lock sim_lock;                 /* sim lock */
+    struct lock gen_lock;                 /* general driver  lock */
+    struct lock io_lock;                  /* IO  lock */
+    struct tws_ioctl_lock ioctl_lock;     /* ioctl lock */
+    u_int32_t seq_id;                     /* Sequence id */
+    int chan;                             /* wait channel */
+    struct tws_circular_q aen_q;          /* aen q */
+    struct tws_circular_q trace_q;        /* trace q */
+    struct tws_stats stats;               /* I/O stats */
+    struct tws_init_connect_info cinfo;   /* compatibility info */
+    boolean is64bit;                      /* True - 64bit else 32bit */
+    u_int8_t intr_type;                   /* Interrupt type used */
+    bus_dma_tag_t parent_tag;             /* parent DMA tag */
+    bus_dma_tag_t cmd_tag;                /* command DMA tag */
+    bus_dmamap_t cmd_map;                 /* command map */
+    void *dma_mem;                        /* pointer to dmable memory */
+    u_int64_t dma_mem_phys;               /* phy addr */
+    bus_dma_tag_t data_tag;               /* data DMA tag */
+    struct tws_request *reqs;             /* pointer to requests */
+    struct tws_sense *sense_bufs;         /* pointer to sense buffers */
+    boolean obfl_q_overrun;               /* OBFL overrun flag  */
+    union ccb *scan_ccb;                  /* pointer to a ccb */
+    struct tws_request *q_head[TWS_MAX_QS]; /* head pointers to q's */
+    struct tws_request *q_tail[TWS_MAX_QS]; /* tail pointers to q's */
+
+    struct callout print_stats_handle;
+    struct callout reset_cb_handle;
+    struct callout reinit_handle;
+};
diff --git a/sys/dev/raid/tws/tws_cam.c b/sys/dev/raid/tws/tws_cam.c
new file mode 100644 (file)
index 0000000..fde1171
--- /dev/null
@@ -0,0 +1,1309 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_cam.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+#include <dev/raid/tws/tws.h>
+#include <dev/raid/tws/tws_services.h>
+#include <dev/raid/tws/tws_hdm.h>
+#include <dev/raid/tws/tws_user.h>
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_sim.h>
+#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_debug.h>
+#include <bus/cam/cam_periph.h>
+
+#include <bus/cam/scsi/scsi_all.h>
+#include <bus/cam/scsi/scsi_message.h>
+
+static int tws_cam_depth=(TWS_MAX_REQS - TWS_RESERVED_REQS);
+static char tws_sev_str[5][8]={"","ERROR","WARNING","INFO","DEBUG"};
+
+static void  tws_action(struct cam_sim *sim, union ccb *ccb);
+static void  tws_poll(struct cam_sim *sim);
+static void tws_bus_scan_cb(struct cam_periph *periph, union ccb *ccb);
+static void tws_scsi_complete(struct tws_request *req);
+
+
+
+void tws_unmap_request(struct tws_softc *sc, struct tws_request *req);
+int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req);
+int tws_bus_scan(struct tws_softc *sc);
+int tws_cam_attach(struct tws_softc *sc);
+void tws_cam_detach(struct tws_softc *sc);
+void tws_reset(void *arg);
+
+static void tws_reset_cb(void *arg);
+static void tws_reinit(void *arg);
+static int32_t tws_execute_scsi(struct tws_softc *sc, union ccb *ccb);
+static void tws_freeze_simq(struct tws_softc *sc);
+static void tws_release_simq(struct tws_softc *sc);
+static void tws_dmamap_data_load_cbfn(void *arg, bus_dma_segment_t *segs,
+                            int nseg, int error);
+static void tws_fill_sg_list(struct tws_softc *sc, void *sgl_src,
+                            void *sgl_dest, u_int16_t num_sgl_entries);
+static void tws_err_complete(struct tws_softc *sc, u_int64_t mfa);
+static void tws_scsi_err_complete(struct tws_request *req,
+                                               struct tws_command_header *hdr);
+static void tws_passthru_err_complete(struct tws_request *req,
+                                               struct tws_command_header *hdr);
+
+
+static void tws_timeout(void *arg);
+static void tws_intr_attn_aen(struct tws_softc *sc);
+static void tws_intr_attn_error(struct tws_softc *sc);
+static void tws_intr_resp(struct tws_softc *sc);
+void tws_intr(void *arg);
+void tws_cmd_complete(struct tws_request *req);
+void tws_aen_complete(struct tws_request *req);
+int tws_send_scsi_cmd(struct tws_softc *sc, int cmd);
+void tws_getset_param_complete(struct tws_request *req);
+int tws_set_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id,
+              u_int32_t param_size, void *data);
+int tws_get_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id,
+              u_int32_t param_size, void *data);
+
+
+extern struct tws_request *tws_get_request(struct tws_softc *sc,
+                                            u_int16_t type);
+extern void *tws_release_request(struct tws_request *req);
+extern int tws_submit_command(struct tws_softc *sc, struct tws_request *req);
+extern boolean tws_get_response(struct tws_softc *sc,
+                                           u_int16_t *req_id, u_int64_t *mfa);
+extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type );
+extern struct tws_request * tws_q_remove_request(struct tws_softc *sc,
+                                   struct tws_request *req, u_int8_t q_type );
+extern void tws_send_event(struct tws_softc *sc, u_int8_t event);
+
+extern struct tws_sense *
+tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa);
+
+extern void tws_fetch_aen(void *arg);
+extern void tws_disable_db_intr(struct tws_softc *sc);
+extern void tws_enable_db_intr(struct tws_softc *sc);
+extern void tws_passthru_complete(struct tws_request *req);
+extern void tws_aen_synctime_with_host(struct tws_softc *sc);
+extern void tws_circular_aenq_insert(struct tws_softc *sc,
+                    struct tws_circular_q *cq, struct tws_event_packet *aen);
+extern int tws_use_32bit_sgls;
+extern boolean tws_ctlr_reset(struct tws_softc *sc);
+extern struct tws_request * tws_q_remove_tail(struct tws_softc *sc,
+                                                           u_int8_t q_type );
+extern void tws_turn_off_interrupts(struct tws_softc *sc);
+extern void tws_turn_on_interrupts(struct tws_softc *sc);
+extern int tws_init_connect(struct tws_softc *sc, u_int16_t mc);
+extern void tws_init_obfl_q(struct tws_softc *sc);
+extern uint8_t tws_get_state(struct tws_softc *sc);
+extern void tws_assert_soft_reset(struct tws_softc *sc);
+extern boolean tws_ctlr_ready(struct tws_softc *sc);
+extern u_int16_t tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa);
+
+
+
+int
+tws_cam_attach(struct tws_softc *sc)
+{
+    struct cam_devq *devq;
+    int error;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, sc);
+    /* Create a device queue for sim */
+
+    /*
+     * if the user sets cam depth to less than 1
+     * cam may get confused
+     */
+    if ( tws_cam_depth < 1 )
+        tws_cam_depth = 1;
+    if ( tws_cam_depth > (tws_queue_depth - TWS_RESERVED_REQS)  )
+        tws_cam_depth = tws_queue_depth - TWS_RESERVED_REQS;
+
+    TWS_TRACE_DEBUG(sc, "depths,ctlr,cam", tws_queue_depth, tws_cam_depth);
+
+    if ((devq = cam_simq_alloc(tws_cam_depth)) == NULL) {
+        tws_log(sc, CAM_SIMQ_ALLOC);
+        return(ENOMEM);
+    }
+
+   /*
+    * Create a SIM entry.  Though we can support tws_cam_depth
+    * simultaneous requests, we claim to be able to handle only
+    * (tws_cam_depth), so that we always have reserved  requests
+    * packet available to service ioctls and internal commands.
+    */
+    sc->sim = cam_sim_alloc(tws_action, tws_poll, "tws", sc,
+                      device_get_unit(sc->tws_dev),
+                      &sc->sim_lock,
+                      tws_cam_depth, 1, devq);
+                      /* 1, 1, devq); */
+    cam_simq_release(devq);
+    if (sc->sim == NULL) {
+        tws_log(sc, CAM_SIM_ALLOC);
+    }
+    /* Register the bus. */
+    lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+    if (xpt_bus_register(sc->sim, 0) != CAM_SUCCESS) {
+        cam_sim_free(sc->sim);
+        sc->sim = NULL; /* so cam_detach will not try to free it */
+        lockmgr(&sc->sim_lock, LK_RELEASE);
+        tws_log(sc, TWS_XPT_BUS_REGISTER);
+        return(ENXIO);
+    }
+    if (xpt_create_path(&sc->path, NULL, cam_sim_path(sc->sim),
+                         CAM_TARGET_WILDCARD,
+                         CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+        xpt_bus_deregister(cam_sim_path(sc->sim));
+        cam_sim_free(sc->sim);
+        tws_log(sc, TWS_XPT_CREATE_PATH);
+        lockmgr(&sc->sim_lock, LK_RELEASE);
+        return(ENXIO);
+    }
+    if ((error = tws_bus_scan(sc))) {
+        tws_log(sc, TWS_BUS_SCAN_REQ);
+        lockmgr(&sc->sim_lock, LK_RELEASE);
+        return(error);
+    }
+    lockmgr(&sc->sim_lock, LK_RELEASE);
+
+    return(0);
+}
+
+void
+tws_cam_detach(struct tws_softc *sc)
+{
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+    if (sc->path)
+        xpt_free_path(sc->path);
+    if (sc->sim) {
+        xpt_bus_deregister(cam_sim_path(sc->sim));
+        cam_sim_free(sc->sim);
+    }
+    lockmgr(&sc->sim_lock, LK_RELEASE);
+}
+
+int
+tws_bus_scan(struct tws_softc *sc)
+{
+    struct cam_path *path;
+    union ccb       *ccb;
+
+    TWS_TRACE_DEBUG(sc, "entry", sc, 0);
+    KASSERT(sc->sim, ("sim not allocated"));
+    KKASSERT(lockstatus(&sc->sim_lock, curthread) != 0);
+
+    ccb = sc->scan_ccb;
+
+    bzero(ccb, sizeof(union ccb));
+    if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->sim),
+                  CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+        kfree(ccb, M_TEMP);
+        /* lockmgr(&sc->sim_lock, LK_RELEASE); */
+        return(EIO);
+    }
+    xpt_setup_ccb(&ccb->ccb_h, path, 5);
+    ccb->ccb_h.func_code = XPT_SCAN_BUS;
+    ccb->ccb_h.cbfcnp = tws_bus_scan_cb;
+    ccb->crcn.flags = CAM_FLAG_NONE;
+    xpt_action(ccb);
+
+    return(0);
+}
+
+static void
+tws_bus_scan_cb(struct cam_periph *periph, union ccb *ccb)
+{
+    struct tws_softc *sc = periph->softc;
+
+    /* calling trace results in non-sleepable lock head panic
+       using printf to debug */
+
+    if (ccb->ccb_h.status != CAM_REQ_CMP) {
+        kprintf("cam_scan failure\n");
+
+        lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+        tws_send_event(sc, TWS_SCAN_FAILURE);
+        lockmgr(&sc->gen_lock, LK_RELEASE);
+    }
+
+    xpt_free_path(ccb->ccb_h.path);
+}
+
+static void
+tws_action(struct cam_sim *sim, union ccb *ccb)
+{
+    struct tws_softc *sc = (struct tws_softc *)cam_sim_softc(sim);
+
+    switch( ccb->ccb_h.func_code ) {
+        case XPT_SCSI_IO:
+        {
+            if ( tws_execute_scsi(sc, ccb) )
+                TWS_TRACE_DEBUG(sc, "execute scsi failed", 0, 0);
+            break;
+        }
+        case XPT_ABORT:
+        {
+            TWS_TRACE_DEBUG(sc, "abort i/o", 0, 0);
+            ccb->ccb_h.status = CAM_UA_ABORT;
+            xpt_done(ccb);
+            break;
+        }
+        case XPT_RESET_BUS:
+        {
+            TWS_TRACE_DEBUG(sc, "reset bus", sim, ccb);
+            break;
+        }
+        case XPT_SET_TRAN_SETTINGS:
+        {
+            TWS_TRACE_DEBUG(sc, "set tran settings", sim, ccb);
+            ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+            xpt_done(ccb);
+
+            break;
+        }
+        case XPT_GET_TRAN_SETTINGS:
+        {
+            TWS_TRACE_DEBUG(sc, "get tran settings", sim, ccb);
+
+            ccb->cts.protocol = PROTO_SCSI;
+            ccb->cts.protocol_version = SCSI_REV_2;
+            ccb->cts.transport = XPORT_SPI;
+            ccb->cts.transport_version = 2;
+
+            ccb->cts.xport_specific.spi.valid = CTS_SPI_VALID_DISC;
+            ccb->cts.xport_specific.spi.flags = CTS_SPI_FLAGS_DISC_ENB;
+            ccb->cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ;
+            ccb->cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB;
+            ccb->ccb_h.status = CAM_REQ_CMP;
+            xpt_done(ccb);
+
+            break;
+        }
+        case XPT_CALC_GEOMETRY:
+        {
+            TWS_TRACE_DEBUG(sc, "calc geometry(ccb,block-size)", ccb,
+                                          ccb->ccg.block_size);
+            cam_calc_geometry(&ccb->ccg, 1/* extended */);
+            xpt_done(ccb);
+
+            break;
+        }
+        case XPT_PATH_INQ:
+        {
+            TWS_TRACE_DEBUG(sc, "path inquiry", sim, ccb);
+            ccb->cpi.version_num = 1;
+            ccb->cpi.hba_inquiry = 0;
+            ccb->cpi.target_sprt = 0;
+            ccb->cpi.hba_misc = 0;
+            ccb->cpi.hba_eng_cnt = 0;
+            ccb->cpi.max_target = TWS_MAX_NUM_UNITS;
+            ccb->cpi.max_lun = TWS_MAX_NUM_LUNS - 1;
+            ccb->cpi.unit_number = cam_sim_unit(sim);
+            ccb->cpi.bus_id = cam_sim_bus(sim);
+            ccb->cpi.initiator_id = TWS_SCSI_INITIATOR_ID;
+            ccb->cpi.base_transfer_speed = 300000;
+            strncpy(ccb->cpi.sim_vid, "FreeBSD", SIM_IDLEN);
+            strncpy(ccb->cpi.hba_vid, "3ware", HBA_IDLEN);
+            strncpy(ccb->cpi.dev_name, cam_sim_name(sim), DEV_IDLEN);
+            ccb->cpi.transport = XPORT_SPI;
+            ccb->cpi.transport_version = 2;
+            ccb->cpi.protocol = PROTO_SCSI;
+            ccb->cpi.protocol_version = SCSI_REV_2;
+            ccb->ccb_h.status = CAM_REQ_CMP;
+            xpt_done(ccb);
+
+            break;
+        }
+        default:
+            TWS_TRACE_DEBUG(sc, "default", sim, ccb);
+            ccb->ccb_h.status = CAM_REQ_INVALID;
+            xpt_done(ccb);
+            break;
+    }
+}
+
+static void
+tws_scsi_complete(struct tws_request *req)
+{
+    struct tws_softc *sc = req->sc;
+
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    tws_q_remove_request(sc, req, TWS_BUSY_Q);
+    lockmgr(&sc->q_lock, LK_RELEASE);
+
+    callout_stop(&req->ccb_ptr->ccb_h.timeout_ch);
+    tws_unmap_request(req->sc, req);
+
+
+    lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+    req->ccb_ptr->ccb_h.status = CAM_REQ_CMP;
+    xpt_done(req->ccb_ptr);
+    lockmgr(&sc->sim_lock, LK_RELEASE);
+
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    tws_q_insert_tail(sc, req, TWS_FREE_Q);
+    lockmgr(&sc->q_lock, LK_RELEASE);
+
+}
+
+void
+tws_getset_param_complete(struct tws_request *req)
+{
+    struct tws_softc *sc = req->sc;
+
+    TWS_TRACE_DEBUG(sc, "getset complete", req, req->request_id);
+
+    callout_stop(&req->thandle);
+    tws_unmap_request(sc, req);
+
+    kfree(req->data, M_TWS);
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    req->state = TWS_REQ_STATE_FREE;
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+}
+
+void
+tws_aen_complete(struct tws_request *req)
+{
+    struct tws_softc *sc = req->sc;
+    struct tws_command_header *sense;
+    struct tws_event_packet event;
+    u_int16_t aen_code=0;
+
+    TWS_TRACE_DEBUG(sc, "aen complete", 0, req->request_id);
+
+    callout_stop(&req->thandle);
+    tws_unmap_request(sc, req);
+
+    sense = (struct tws_command_header *)req->data;
+
+    TWS_TRACE_DEBUG(sc,"sense code, key",sense->sense_data[0],
+                                   sense->sense_data[2]);
+    TWS_TRACE_DEBUG(sc,"sense rid, seve",sense->header_desc.request_id,
+                                   sense->status_block.res__severity);
+    TWS_TRACE_DEBUG(sc,"sense srcnum, error",sense->status_block.srcnum,
+                                   sense->status_block.error);
+    TWS_TRACE_DEBUG(sc,"sense shdr, ssense",sense->header_desc.size_header,
+                                   sense->header_desc.size_sense);
+
+    aen_code = sense->status_block.error;
+
+    switch ( aen_code ) {
+        case TWS_AEN_SYNC_TIME_WITH_HOST :
+            tws_aen_synctime_with_host(sc);
+            break;
+        case TWS_AEN_QUEUE_EMPTY :
+            break;
+        default :
+            bzero(&event, sizeof(struct tws_event_packet));
+            event.sequence_id = sc->seq_id;
+            event.time_stamp_sec = (u_int32_t)TWS_LOCAL_TIME;
+            event.aen_code = sense->status_block.error;
+            event.severity = sense->status_block.res__severity & 0x7;
+            event.event_src = TWS_SRC_CTRL_EVENT;
+            strcpy(event.severity_str, tws_sev_str[event.severity]);
+            event.retrieved = TWS_AEN_NOT_RETRIEVED;
+
+            bcopy(sense->err_specific_desc, event.parameter_data,
+                                    TWS_ERROR_SPECIFIC_DESC_LEN);
+            event.parameter_data[TWS_ERROR_SPECIFIC_DESC_LEN - 1] = '\0';
+            event.parameter_len = (u_int8_t)strlen(event.parameter_data)+1;
+
+            if ( event.parameter_len < TWS_ERROR_SPECIFIC_DESC_LEN ) {
+                event.parameter_len += ((u_int8_t)strlen(event.parameter_data +
+                                                event.parameter_len) + 1);
+            }
+
+            device_printf(sc->tws_dev, "%s: (0x%02X: 0x%04X): %s: %s\n",
+                event.severity_str,
+                event.event_src,
+                event.aen_code,
+                event.parameter_data +
+                     (strlen(event.parameter_data) + 1),
+                event.parameter_data);
+
+            lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+            tws_circular_aenq_insert(sc, &sc->aen_q, &event);
+            sc->seq_id++;
+            lockmgr(&sc->gen_lock, LK_RELEASE);
+            break;
+
+    }
+
+    kfree(req->data, M_TWS);
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    req->state = TWS_REQ_STATE_FREE;
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+    if ( aen_code != TWS_AEN_QUEUE_EMPTY ) {
+        /* timeout(tws_fetch_aen, sc, 1);*/
+        sc->stats.num_aens++;
+        tws_fetch_aen((void *)sc);
+    }
+
+}
+
+void
+tws_cmd_complete(struct tws_request *req)
+{
+    struct tws_softc *sc = req->sc;
+
+    callout_stop(&req->ccb_ptr->ccb_h.timeout_ch);
+    tws_unmap_request(sc, req);
+
+}
+
+static void
+tws_err_complete(struct tws_softc *sc, u_int64_t mfa)
+{
+
+    struct tws_command_header *hdr;
+    struct tws_sense *sen;
+    struct tws_request *req;
+    u_int16_t req_id;
+    u_int32_t reg, status;
+
+    if ( !mfa ) {
+        TWS_TRACE_DEBUG(sc, "null mfa", 0, mfa);
+        return;
+    } else {
+        /* lookup the sense */
+        sen = tws_find_sense_from_mfa(sc, mfa);
+        if ( sen == NULL ) {
+            TWS_TRACE_DEBUG(sc, "found null req", 0, mfa);
+            return;
+        }
+        hdr = sen->hdr;
+        TWS_TRACE_DEBUG(sc, "sen, hdr", sen, hdr);
+        req_id = hdr->header_desc.request_id;
+        req = &sc->reqs[req_id];
+        TWS_TRACE_DEBUG(sc, "req, id", req, req_id);
+        if ( req->error_code != TWS_REQ_SUBMIT_SUCCESS )
+            TWS_TRACE_DEBUG(sc, "submit failure?", 0, req->error_code);
+    }
+
+    switch (req->type) {
+        case TWS_PASSTHRU_REQ :
+            tws_passthru_err_complete(req, hdr);
+            break;
+        case TWS_GETSET_PARAM_REQ :
+            tws_getset_param_complete(req);
+            break;
+        case TWS_SCSI_IO_REQ :
+            tws_scsi_err_complete(req, hdr);
+            break;
+
+    }
+
+    lockmgr(&sc->io_lock, LK_EXCLUSIVE);
+    hdr->header_desc.size_header = 128;
+    reg = (u_int32_t)( mfa>>32);
+    tws_write_reg(sc, TWS_I2O0_HOBQPH, reg, 4);
+    reg = (u_int32_t)(mfa);
+    tws_write_reg(sc, TWS_I2O0_HOBQPL, reg, 4);
+
+    status = tws_read_reg(sc, TWS_I2O0_STATUS, 4);
+    if ( status & TWS_BIT13 ) {
+        TWS_TRACE_DEBUG(sc, "OBFL Overrun", status, TWS_I2O0_STATUS);
+        sc->obfl_q_overrun = true;
+        sen->posted = false;
+    }
+    lockmgr(&sc->io_lock, LK_RELEASE);
+
+}
+
+static void
+tws_scsi_err_complete(struct tws_request *req, struct tws_command_header *hdr)
+{
+    u_int8_t *sense_data;
+    struct tws_softc *sc = req->sc;
+    union ccb *ccb = req->ccb_ptr;
+
+    TWS_TRACE_DEBUG(sc, "sbe, cmd_status", hdr->status_block.error,
+                                 req->cmd_pkt->cmd.pkt_a.status);
+    if ( hdr->status_block.error == TWS_ERROR_LOGICAL_UNIT_NOT_SUPPORTED ||
+         hdr->status_block.error == TWS_ERROR_UNIT_OFFLINE ) {
+
+        if ( ccb->ccb_h.target_lun ) {
+            TWS_TRACE_DEBUG(sc, "invalid lun error",0,0);
+            ccb->ccb_h.status |= CAM_LUN_INVALID;
+        } else {
+            TWS_TRACE_DEBUG(sc, "invalid target error",0,0);
+            ccb->ccb_h.status |= CAM_TID_INVALID;
+        }
+
+    } else {
+        TWS_TRACE_DEBUG(sc, "scsi status  error",0,0);
+        ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
+        if (((ccb->csio.cdb_io.cdb_bytes[0] == 0x1A) &&
+              (hdr->status_block.error == TWS_ERROR_NOT_SUPPORTED))) {
+            ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
+            TWS_TRACE_DEBUG(sc, "page mode not supported",0,0);
+        }
+    }
+
+    /* if there were no error simply mark complete error */
+    if (ccb->ccb_h.status == 0)
+        ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+
+    sense_data = (u_int8_t *)&ccb->csio.sense_data;
+    if (sense_data) {
+        memcpy(sense_data, hdr->sense_data, TWS_SENSE_DATA_LENGTH );
+        ccb->csio.sense_len = TWS_SENSE_DATA_LENGTH;
+        ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+    }
+    ccb->csio.scsi_status = req->cmd_pkt->cmd.pkt_a.status;
+
+    ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+    lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+    xpt_done(ccb);
+    lockmgr(&sc->sim_lock, LK_RELEASE);
+
+    callout_stop(&req->ccb_ptr->ccb_h.timeout_ch);
+    tws_unmap_request(req->sc, req);
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    tws_q_remove_request(sc, req, TWS_BUSY_Q);
+    tws_q_insert_tail(sc, req, TWS_FREE_Q);
+    lockmgr(&sc->q_lock, LK_RELEASE);
+
+}
+
+static void
+tws_passthru_err_complete(struct tws_request *req,
+                                          struct tws_command_header *hdr)
+{
+
+    TWS_TRACE_DEBUG(req->sc, "entry", hdr, req->request_id);
+    req->error_code = hdr->status_block.error;
+    memcpy(&(req->cmd_pkt->hdr), hdr, sizeof(struct tws_command_header));
+    tws_passthru_complete(req);
+}
+
+static void
+tws_drain_busy_queue(struct tws_softc *sc)
+{
+
+    struct tws_request *req;
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    req = tws_q_remove_tail(sc, TWS_BUSY_Q);
+    lockmgr(&sc->q_lock, LK_RELEASE);
+    while ( req ) {
+       callout_stop(&req->ccb_ptr->ccb_h.timeout_ch);
+        tws_unmap_request(req->sc, req);
+
+        TWS_TRACE_DEBUG(sc, "drained", 0, req->request_id);
+
+        lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+        req->ccb_ptr->ccb_h.status = CAM_REQUEUE_REQ;
+        xpt_done(req->ccb_ptr);
+        lockmgr(&sc->sim_lock, LK_RELEASE);
+
+        lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+        tws_q_insert_tail(sc, req, TWS_FREE_Q);
+        req = tws_q_remove_tail(sc, TWS_BUSY_Q);
+        lockmgr(&sc->q_lock, LK_RELEASE);
+    }
+
+}
+
+static void
+tws_drain_reserved_reqs(struct tws_softc *sc)
+{
+
+    struct tws_request *r;
+
+    r = &sc->reqs[1];
+    if ( r->state != TWS_REQ_STATE_FREE ) {
+        TWS_TRACE_DEBUG(sc, "drained aen req", 0, 0);
+       callout_stop(&r->thandle);
+        tws_unmap_request(sc, r);
+        kfree(r->data, M_TWS);
+        lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+        r->state = TWS_REQ_STATE_FREE;
+        lockmgr(&sc->gen_lock, LK_RELEASE);
+    }
+    r = &sc->reqs[2];
+    if ( r->state != TWS_REQ_STATE_FREE ) {
+        TWS_TRACE_DEBUG(sc, "drained passthru req", 0, 0);
+        r->error_code = TWS_REQ_REQUEUE;
+        tws_passthru_complete(r);
+    }
+    r = &sc->reqs[3];
+    if ( r->state != TWS_REQ_STATE_FREE ) {
+        TWS_TRACE_DEBUG(sc, "drained set param req", 0, 0);
+        tws_getset_param_complete(r);
+    }
+
+}
+
+static void
+tws_drain_response_queue(struct tws_softc *sc)
+{
+    tws_intr_resp(sc);
+}
+
+
+static int32_t
+tws_execute_scsi(struct tws_softc *sc, union ccb *ccb)
+{
+    struct tws_command_packet *cmd_pkt;
+    struct tws_request *req;
+    struct ccb_hdr *ccb_h = &(ccb->ccb_h);
+    struct ccb_scsiio *csio = &(ccb->csio);
+    int error;
+    u_int16_t lun;
+
+    KKASSERT(lockstatus(&sc->sim_lock, curthread) != 0);
+    if (ccb_h->target_id >= TWS_MAX_NUM_UNITS) {
+        TWS_TRACE_DEBUG(sc, "traget id too big", ccb_h->target_id, ccb_h->target_lun);
+        ccb_h->status |= CAM_TID_INVALID;
+        xpt_done(ccb);
+        return(0);
+    }
+    if (ccb_h->target_lun >= TWS_MAX_NUM_LUNS) {
+        TWS_TRACE_DEBUG(sc, "target lun 2 big", ccb_h->target_id, ccb_h->target_lun);
+        ccb_h->status |= CAM_LUN_INVALID;
+        xpt_done(ccb);
+        return(0);
+    }
+
+    if(ccb_h->flags & CAM_CDB_PHYS) {
+        TWS_TRACE_DEBUG(sc, "cdb phy", ccb_h->target_id, ccb_h->target_lun);
+        ccb_h->status = CAM_REQ_CMP_ERR;
+        xpt_done(ccb);
+        return(0);
+    }
+
+    /*
+     * We are going to work on this request.  Mark it as enqueued (though
+     * we don't actually queue it...)
+     */
+    ccb_h->status |= CAM_SIM_QUEUED;
+
+    req = tws_get_request(sc, TWS_SCSI_IO_REQ);
+    if ( !req ) {
+        TWS_TRACE_DEBUG(sc, "no reqs", ccb_h->target_id, ccb_h->target_lun);
+        /* tws_freeze_simq(sc); */
+        ccb_h->status |= CAM_REQUEUE_REQ;
+        xpt_done(ccb);
+        return(0);
+    }
+
+    if((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+        if(ccb_h->flags & CAM_DIR_IN)
+            req->flags = TWS_DIR_IN;
+        else
+            req->flags = TWS_DIR_OUT;
+    } else {
+        req->flags = TWS_DIR_NONE; /* no data */
+    }
+
+    req->type = TWS_SCSI_IO_REQ;
+    req->cb = tws_scsi_complete;
+
+    cmd_pkt = req->cmd_pkt;
+    /* cmd_pkt->hdr.header_desc.size_header = 128; */
+    cmd_pkt->cmd.pkt_a.res__opcode = TWS_FW_CMD_EXECUTE_SCSI;
+    cmd_pkt->cmd.pkt_a.unit = ccb_h->target_id;
+    cmd_pkt->cmd.pkt_a.status = 0;
+    cmd_pkt->cmd.pkt_a.sgl_offset = 16;
+
+    /* lower nibble */
+    lun = ccb_h->target_lun & 0XF;
+    lun = lun << 12;
+    cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun | req->request_id;
+    /* upper nibble */
+    lun = ccb_h->target_lun & 0XF0;
+    lun = lun << 8;
+    cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries = lun;
+
+#ifdef TWS_DEBUG
+    if ( csio->cdb_len > 16 )
+         TWS_TRACE(sc, "cdb len too big", ccb_h->target_id, csio->cdb_len);
+#endif
+
+    if(ccb_h->flags & CAM_CDB_POINTER)
+        bcopy(csio->cdb_io.cdb_ptr, cmd_pkt->cmd.pkt_a.cdb, csio->cdb_len);
+    else
+        bcopy(csio->cdb_io.cdb_bytes, cmd_pkt->cmd.pkt_a.cdb, csio->cdb_len);
+
+    if (!(ccb_h->flags & CAM_DATA_PHYS)) {
+         /* Virtual data addresses.  Need to convert them... */
+         if (!(ccb_h->flags & CAM_SCATTER_VALID)) {
+             if (csio->dxfer_len > TWS_MAX_IO_SIZE) {
+                 TWS_TRACE(sc, "I/O is big", csio->dxfer_len, 0);
+                 tws_release_request(req);
+                 ccb_h->status = CAM_REQ_TOO_BIG;
+                 xpt_done(ccb);
+                 return(0);
+             }
+
+             req->length = csio->dxfer_len;
+             if (req->length) {
+                 req->data = csio->data_ptr;
+                 /* there is 1 sgl_entrie */
+                 /* cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries |= 1; */
+             }
+         } else {
+             TWS_TRACE_DEBUG(sc, "got sglist", ccb_h->target_id, ccb_h->target_lun);
+             tws_release_request(req);
+             ccb_h->status = CAM_REQ_CMP_ERR;
+             xpt_done(ccb);
+             return(0);
+         }
+    } else {
+         /* Data addresses are physical. */
+         TWS_TRACE_DEBUG(sc, "Phy data addr", ccb_h->target_id, ccb_h->target_lun);
+         tws_release_request(req);
+         ccb_h->status = CAM_REQ_CMP_ERR;
+         ccb_h->status |= CAM_RELEASE_SIMQ;
+         ccb_h->status &= ~CAM_SIM_QUEUED;
+         xpt_done(ccb);
+         return(0);
+    }
+    /* save ccb ptr */
+    req->ccb_ptr = ccb;
+    /*
+     * tws_map_load_data_callback will fill in the SGL,
+     * and submit the I/O.
+     */
+    sc->stats.scsi_ios++;
+    callout_reset(&ccb_h->timeout_ch, (ccb_h->timeout * hz)/1000, tws_timeout,
+       req);
+    error = tws_map_request(sc, req);
+    return(error);
+}
+
+
+int
+tws_send_scsi_cmd(struct tws_softc *sc, int cmd)
+{
+
+    struct tws_request *req;
+    struct tws_command_packet *cmd_pkt;
+    int error;
+
+    TWS_TRACE_DEBUG(sc, "entry",sc, cmd);
+    req = tws_get_request(sc, TWS_AEN_FETCH_REQ);
+
+    if ( req == NULL )
+        return(ENOMEM);
+
+    req->type = TWS_AEN_FETCH_REQ;
+    req->cb = tws_aen_complete;
+
+    cmd_pkt = req->cmd_pkt;
+    cmd_pkt->cmd.pkt_a.res__opcode = TWS_FW_CMD_EXECUTE_SCSI;
+    cmd_pkt->cmd.pkt_a.status = 0;
+    cmd_pkt->cmd.pkt_a.unit = 0;
+    cmd_pkt->cmd.pkt_a.sgl_offset = 16;
+    cmd_pkt->cmd.pkt_a.lun_l4__req_id = req->request_id;
+
+    cmd_pkt->cmd.pkt_a.cdb[0] = (u_int8_t)cmd;
+    cmd_pkt->cmd.pkt_a.cdb[4] = 128;
+
+    req->length = TWS_SECTOR_SIZE;
+    req->data = kmalloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT);
+    if ( req->data == NULL )
+        return(ENOMEM);
+    bzero(req->data, TWS_SECTOR_SIZE);
+    req->flags = TWS_DIR_IN;
+
+    callout_reset(&req->thandle, (TWS_IO_TIMEOUT * hz), tws_timeout, req);
+    error = tws_map_request(sc, req);
+    return(error);
+
+}
+
+int
+tws_set_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id,
+              u_int32_t param_size, void *data)
+{
+    struct tws_request *req;
+    struct tws_command_packet *cmd_pkt;
+    union tws_command_giga *cmd;
+    struct tws_getset_param *param;
+    int error;
+
+    req = tws_get_request(sc, TWS_GETSET_PARAM_REQ);
+    if ( req == NULL ) {
+        TWS_TRACE_DEBUG(sc, "null req", 0, 0);
+        return(ENOMEM);
+    }
+
+    req->length = TWS_SECTOR_SIZE;
+    req->data = kmalloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT);
+    if ( req->data == NULL )
+        return(ENOMEM);
+    bzero(req->data, TWS_SECTOR_SIZE);
+    param = (struct tws_getset_param *)req->data;
+
+    req->cb = tws_getset_param_complete;
+    req->flags = TWS_DIR_OUT;
+    cmd_pkt = req->cmd_pkt;
+
+    cmd = &cmd_pkt->cmd.pkt_g;
+    cmd->param.sgl_off__opcode =
+            BUILD_SGL_OFF__OPCODE(2, TWS_FW_CMD_SET_PARAM);
+    cmd->param.request_id = (u_int8_t)req->request_id;
+    cmd->param.host_id__unit = 0;
+    cmd->param.param_count = 1;
+    cmd->param.size = 2; /* map routine will add sgls */
+
+    /* Specify which parameter we want to set. */
+    param->table_id = (table_id | TWS_9K_PARAM_DESCRIPTOR);
+    param->parameter_id = (u_int8_t)(param_id);
+    param->parameter_size_bytes = (u_int16_t)param_size;
+    memcpy(param->data, data, param_size);
+
+    callout_reset(&req->thandle, (TWS_IO_TIMEOUT * hz), tws_timeout, req);
+    error = tws_map_request(sc, req);
+    return(error);
+
+}
+
+int
+tws_get_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id,
+              u_int32_t param_size, void *data)
+{
+    struct tws_request *req;
+    struct tws_command_packet *cmd_pkt;
+    union tws_command_giga *cmd;
+    struct tws_getset_param *param;
+    u_int16_t reqid;
+    u_int64_t mfa;
+    int error = SUCCESS;
+
+
+    req = tws_get_request(sc, TWS_GETSET_PARAM_REQ);
+    if ( req == NULL ) {
+        TWS_TRACE_DEBUG(sc, "null req", 0, 0);
+        return(FAILURE);
+    }
+
+    req->length = TWS_SECTOR_SIZE;
+    req->data = kmalloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT);
+    if ( req->data == NULL )
+        return(FAILURE);
+    bzero(req->data, TWS_SECTOR_SIZE);
+    param = (struct tws_getset_param *)req->data;
+
+    req->cb = NULL;
+    req->flags = TWS_DIR_IN;
+    cmd_pkt = req->cmd_pkt;
+
+    cmd = &cmd_pkt->cmd.pkt_g;
+    cmd->param.sgl_off__opcode =
+            BUILD_SGL_OFF__OPCODE(2, TWS_FW_CMD_GET_PARAM);
+    cmd->param.request_id = (u_int8_t)req->request_id;
+    cmd->param.host_id__unit = 0;
+    cmd->param.param_count = 1;
+    cmd->param.size = 2; /* map routine will add sgls */
+
+    /* Specify which parameter we want to set. */
+    param->table_id = (table_id | TWS_9K_PARAM_DESCRIPTOR);
+    param->parameter_id = (u_int8_t)(param_id);
+    param->parameter_size_bytes = (u_int16_t)param_size;
+
+    tws_map_request(sc, req);
+    reqid = tws_poll4_response(sc, &mfa);
+    tws_unmap_request(sc, req);
+
+    if ( reqid == TWS_GETSET_PARAM_REQ ) {
+        memcpy(data, param->data, param_size);
+    } else {
+        error = FAILURE;
+
+    }
+
+    kfree(req->data, M_TWS);
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    req->state = TWS_REQ_STATE_FREE;
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+    return(error);
+
+}
+
+void
+tws_unmap_request(struct tws_softc *sc, struct tws_request *req)
+{
+
+    if (req->data != NULL) {
+        if ( req->flags & TWS_DIR_IN )
+            bus_dmamap_sync(sc->data_tag, req->dma_map,
+                                            BUS_DMASYNC_POSTREAD);
+        if ( req->flags & TWS_DIR_OUT )
+            bus_dmamap_sync(sc->data_tag, req->dma_map,
+                                            BUS_DMASYNC_POSTWRITE);
+        lockmgr(&sc->io_lock, LK_EXCLUSIVE);
+        bus_dmamap_unload(sc->data_tag, req->dma_map);
+        lockmgr(&sc->io_lock, LK_RELEASE);
+    }
+}
+
+int32_t
+tws_map_request(struct tws_softc *sc, struct tws_request *req)
+{
+    int32_t error = 0;
+
+
+    /* If the command involves data, map that too. */
+    if (req->data != NULL) {
+        /*
+         * Map the data buffer into bus space and build the SG list.
+         */
+        lockmgr(&sc->io_lock, LK_EXCLUSIVE);
+        error = bus_dmamap_load(sc->data_tag, req->dma_map,
+                                req->data, req->length,
+                                tws_dmamap_data_load_cbfn, req,
+                                BUS_DMA_WAITOK);
+        lockmgr(&sc->io_lock, LK_RELEASE);
+
+        if (error == EINPROGRESS) {
+            TWS_TRACE(sc, "in progress", 0, error);
+            /* tws_freeze_simq(sc); */
+            error = TWS_REQ_ERR_INPROGRESS;
+        }
+    } else { /* no data involved */
+        error = tws_submit_command(sc, req);
+    }
+    req->error_code = error;
+    return(error);
+}
+
+
+static void
+tws_dmamap_data_load_cbfn(void *arg, bus_dma_segment_t *segs,
+                            int nseg, int error)
+{
+
+    struct tws_request *req = (struct tws_request *)arg;
+    struct tws_softc *sc = req->sc;
+    u_int16_t sgls = nseg;
+    void *sgl_ptr;
+    struct tws_cmd_generic *gcmd;
+
+    if ( error == EFBIG )
+        TWS_TRACE(sc, "not enough data segs", 0, nseg);
+
+
+    if ( req->flags & TWS_DIR_IN )
+        bus_dmamap_sync(req->sc->data_tag, req->dma_map,
+                                            BUS_DMASYNC_PREREAD);
+    if ( req->flags & TWS_DIR_OUT )
+        bus_dmamap_sync(req->sc->data_tag, req->dma_map,
+                                        BUS_DMASYNC_PREWRITE);
+    if ( segs ) {
+        if ( (req->type == TWS_PASSTHRU_REQ &&
+             GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) !=
+                            TWS_FW_CMD_EXECUTE_SCSI) ||
+              req->type == TWS_GETSET_PARAM_REQ) {
+            gcmd = &req->cmd_pkt->cmd.pkt_g.generic;
+            sgl_ptr = (u_int32_t *)(gcmd) + gcmd->size;
+            gcmd->size += sgls *
+                          ((req->sc->is64bit && !tws_use_32bit_sgls) ? 4 :2 );
+            tws_fill_sg_list(req->sc, (void *)segs, sgl_ptr, sgls);
+
+        } else {
+            tws_fill_sg_list(req->sc, (void *)segs,
+                      (void *)req->cmd_pkt->cmd.pkt_a.sg_list, sgls);
+            req->cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries |= sgls ;
+        }
+    }
+
+
+    req->error_code = tws_submit_command(req->sc, req);
+
+}
+
+
+static void
+tws_fill_sg_list(struct tws_softc *sc, void *sgl_src, void *sgl_dest,
+                          u_int16_t num_sgl_entries)
+{
+    int i;
+
+    if ( sc->is64bit ) {
+        struct tws_sg_desc64 *sgl_s = (struct tws_sg_desc64 *)sgl_src;
+
+        if ( !tws_use_32bit_sgls ) {
+            struct tws_sg_desc64 *sgl_d = (struct tws_sg_desc64 *)sgl_dest;
+            if ( num_sgl_entries > TWS_MAX_64BIT_SG_ELEMENTS )
+                TWS_TRACE(sc, "64bit sg overflow", num_sgl_entries, 0);
+            for (i = 0; i < num_sgl_entries; i++) {
+                sgl_d[i].address = sgl_s->address;
+                sgl_d[i].length = sgl_s->length;
+                sgl_d[i].flag = 0;
+                sgl_d[i].reserved = 0;
+                sgl_s = (struct tws_sg_desc64 *) (((u_int8_t *)sgl_s) +
+                                               sizeof(bus_dma_segment_t));
+            }
+        } else {
+            struct tws_sg_desc32 *sgl_d = (struct tws_sg_desc32 *)sgl_dest;
+            if ( num_sgl_entries > TWS_MAX_32BIT_SG_ELEMENTS )
+                TWS_TRACE(sc, "32bit sg overflow", num_sgl_entries, 0);
+            for (i = 0; i < num_sgl_entries; i++) {
+                sgl_d[i].address = sgl_s->address;
+                sgl_d[i].length = sgl_s->length;
+                sgl_d[i].flag = 0;
+                sgl_s = (struct tws_sg_desc64 *) (((u_int8_t *)sgl_s) +
+                                               sizeof(bus_dma_segment_t));
+            }
+        }
+    } else {
+        struct tws_sg_desc32 *sgl_s = (struct tws_sg_desc32 *)sgl_src;
+        struct tws_sg_desc32 *sgl_d = (struct tws_sg_desc32 *)sgl_dest;
+
+        if ( num_sgl_entries > TWS_MAX_32BIT_SG_ELEMENTS )
+            TWS_TRACE(sc, "32bit sg overflow", num_sgl_entries, 0);
+
+
+        for (i = 0; i < num_sgl_entries; i++) {
+            sgl_d[i].address = sgl_s[i].address;
+            sgl_d[i].length = sgl_s[i].length;
+            sgl_d[i].flag = 0;
+        }
+    }
+}
+
+
+void
+tws_intr(void *arg)
+{
+    struct tws_softc *sc = (struct tws_softc *)arg;
+    u_int32_t histat=0, db=0;
+
+    KASSERT(sc, ("null softc"));
+
+    sc->stats.num_intrs++;
+    histat = tws_read_reg(sc, TWS_I2O0_HISTAT, 4);
+    if ( histat & TWS_BIT2 ) {
+        TWS_TRACE_DEBUG(sc, "door bell :)", histat, TWS_I2O0_HISTAT);
+        db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4);
+        if ( db & TWS_BIT21 ) {
+            tws_intr_attn_error(sc);
+            return;
+        }
+        if ( db & TWS_BIT18 ) {
+            tws_intr_attn_aen(sc);
+        }
+    }
+
+    if ( histat & TWS_BIT3 ) {
+        tws_intr_resp(sc);
+    }
+}
+
+static void
+tws_intr_attn_aen(struct tws_softc *sc)
+{
+    u_int32_t db=0;
+
+    /* maskoff db intrs untill all the aens are fetched */
+    /* tws_disable_db_intr(sc); */
+    tws_fetch_aen((void *)sc);
+    tws_write_reg(sc, TWS_I2O0_HOBDBC, TWS_BIT18, 4);
+    db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4);
+
+}
+
+static void
+tws_intr_attn_error(struct tws_softc *sc)
+{
+    u_int32_t db=0;
+
+    TWS_TRACE(sc, "attn error", 0, 0);
+    tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4);
+    db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4);
+    device_printf(sc->tws_dev, "Micro controller error.\n");
+    tws_reset(sc);
+}
+
+static void
+tws_intr_resp(struct tws_softc *sc)
+{
+    u_int16_t req_id;
+    u_int64_t mfa;
+
+    while ( tws_get_response(sc, &req_id, &mfa) ) {
+        sc->stats.reqs_out++;
+        if ( req_id == TWS_INVALID_REQID ) {
+            TWS_TRACE_DEBUG(sc, "invalid req_id", mfa, req_id);
+            sc->stats.reqs_errored++;
+            tws_err_complete(sc, mfa);
+            continue;
+        }
+
+        sc->reqs[req_id].cb(&sc->reqs[req_id]);
+    }
+
+}
+
+
+static void
+tws_poll(struct cam_sim *sim)
+{
+    struct tws_softc *sc = (struct tws_softc *)cam_sim_softc(sim);
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    tws_intr((void *) sc);
+}
+
+void
+tws_timeout(void *arg)
+{
+    struct tws_request *req = (struct tws_request *)arg;
+    struct tws_softc *sc = req->sc;
+
+
+    if ( tws_get_state(sc) != TWS_RESET ) {
+        device_printf(sc->tws_dev, "Request timed out.\n");
+        tws_reset((void *)sc);
+    }
+}
+
+void
+tws_reset(void *arg)
+{
+
+    struct tws_softc *sc = (struct tws_softc *)arg;
+
+    if ( tws_get_state(sc) == TWS_RESET ) {
+        return;
+    }
+    device_printf(sc->tws_dev,  "Resetting controller\n");
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    tws_send_event(sc, TWS_RESET_START);
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+    tws_turn_off_interrupts(sc);
+    lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+    tws_freeze_simq(sc);
+    lockmgr(&sc->sim_lock, LK_RELEASE);
+
+    tws_assert_soft_reset(sc);
+    callout_reset(&sc->reset_cb_handle, hz/10, tws_reset_cb, sc);
+}
+
+static void
+tws_reset_cb(void *arg)
+{
+
+    struct tws_softc *sc = (struct tws_softc *)arg;
+    u_int32_t reg;
+
+    if ( tws_get_state(sc) != TWS_RESET ) {
+        return;
+    }
+    reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4);
+    if (!( reg & TWS_BIT13 )) {
+       callout_reset(&sc->reset_cb_handle, hz/10, tws_reset_cb, sc);
+        return;
+    }
+    tws_drain_response_queue(sc);
+    tws_drain_busy_queue(sc);
+    tws_drain_reserved_reqs(sc);
+    callout_reset(&sc->reinit_handle, 5*hz, tws_reinit, sc);
+}
+
+static void
+tws_reinit(void *arg)
+{
+
+    struct tws_softc *sc = (struct tws_softc *)arg;
+    static int timeout_val=0, try=2  ;
+
+    if ( !tws_ctlr_ready(sc) ) {
+        timeout_val += 5;
+        if ( timeout_val >= TWS_RESET_TIMEOUT ) {
+           timeout_val = 0;
+           if ( try )
+               tws_assert_soft_reset(sc);
+           try--;
+        }
+       callout_reset(&sc->reinit_handle, 5*hz, tws_reinit, sc);
+        return;
+    }
+
+    timeout_val=0;
+    try = 2;
+    sc->obfl_q_overrun = false;
+    if ( tws_init_connect(sc, tws_queue_depth) ) {
+        TWS_TRACE_DEBUG(sc, "initConnect failed", 0, sc->is64bit);
+    }
+    tws_init_obfl_q(sc);
+
+    lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+    tws_release_simq(sc);
+    lockmgr(&sc->sim_lock, LK_RELEASE);
+    tws_turn_on_interrupts(sc);
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    tws_send_event(sc, TWS_RESET_COMPLETE);
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+    if ( sc->chan ) {
+        sc->chan = 0;
+        wakeup((void *)&sc->chan);
+    }
+
+}
+
+
+static void
+tws_freeze_simq(struct tws_softc *sc)
+{
+
+    TWS_TRACE_DEBUG(sc, "freezeing", 0, 0);
+    KKASSERT(lockstatus(&sc->sim_lock, curthread) != 0);
+    xpt_freeze_simq(sc->sim, 1);
+
+}
+static void
+tws_release_simq(struct tws_softc *sc)
+{
+
+    TWS_TRACE_DEBUG(sc, "unfreezeing", 0, 0);
+    KKASSERT(lockstatus(&sc->sim_lock, curthread) != 0);
+    xpt_release_simq(sc->sim, 1);
+
+}
+
+
+TUNABLE_INT("hw.tws.cam_depth", &tws_cam_depth);
diff --git a/sys/dev/raid/tws/tws_hdm.c b/sys/dev/raid/tws/tws_hdm.c
new file mode 100644 (file)
index 0000000..154f956
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_hdm.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+
+#include <dev/raid/tws/tws.h>
+#include <dev/raid/tws/tws_services.h>
+#include <dev/raid/tws/tws_hdm.h>
+
+
+int tws_use_32bit_sgls=0;
+extern u_int64_t mfa_base;
+extern struct tws_request *tws_get_request(struct tws_softc *sc,
+                                           u_int16_t type);
+extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type );
+extern struct tws_request * tws_q_remove_request(struct tws_softc *sc,
+                                   struct tws_request *req, u_int8_t q_type );
+
+extern void tws_cmd_complete(struct tws_request *req);
+extern void tws_print_stats(void *arg);
+extern int tws_send_scsi_cmd(struct tws_softc *sc, int cmd);
+extern int tws_set_param(struct tws_softc *sc, u_int32_t table_id,
+           u_int32_t param_id, u_int32_t param_size, void *data);
+extern int tws_get_param(struct tws_softc *sc, u_int32_t table_id,
+            u_int32_t param_id, u_int32_t param_size, void *data);
+extern void tws_reset(void *arg);
+
+int tws_init_connect(struct tws_softc *sc, u_int16_t mc);
+int tws_init_ctlr(struct tws_softc *sc);
+int tws_submit_command(struct tws_softc *sc, struct tws_request *req);
+void tws_nop_cmd(void *arg);
+u_int16_t tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa);
+boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id,
+                                               u_int64_t *mfa);
+boolean tws_ctlr_ready(struct tws_softc *sc);
+void tws_turn_on_interrupts(struct tws_softc *sc);
+void tws_turn_off_interrupts(struct tws_softc *sc);
+boolean tws_ctlr_reset(struct tws_softc *sc);
+void tws_assert_soft_reset(struct tws_softc *sc);
+
+int tws_send_generic_cmd(struct tws_softc *sc, u_int8_t opcode);
+void tws_fetch_aen(void *arg);
+void tws_disable_db_intr(struct tws_softc *sc);
+void tws_enable_db_intr(struct tws_softc *sc);
+void tws_aen_synctime_with_host(struct tws_softc *sc);
+void tws_init_obfl_q(struct tws_softc *sc);
+void tws_display_ctlr_info(struct tws_softc *sc);
+
+int
+tws_init_ctlr(struct tws_softc *sc)
+{
+    u_int64_t reg;
+    u_int32_t regh, regl;
+
+    TWS_TRACE_DEBUG(sc, "entry", sc, sc->is64bit);
+    sc->obfl_q_overrun = false;
+    if ( tws_init_connect(sc, tws_queue_depth) )
+    {
+        TWS_TRACE_DEBUG(sc, "initConnect failed", 0, sc->is64bit);
+        return(FAILURE);
+
+    }
+
+
+    while( 1 ) {
+        regh = tws_read_reg(sc, TWS_I2O0_IOPOBQPH, 4);
+        regl = tws_read_reg(sc, TWS_I2O0_IOPOBQPL, 4);
+        reg = (((u_int64_t)regh) << 32) | regl;
+        TWS_TRACE_DEBUG(sc, "host outbound clenup",reg, regl);
+        if ( regh == TWS_FIFO_EMPTY32 )
+            break;
+    }
+
+    tws_init_obfl_q(sc);
+    tws_display_ctlr_info(sc);
+    tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4);
+    tws_turn_on_interrupts(sc);
+    return(SUCCESS);
+}
+
+void
+tws_init_obfl_q(struct tws_softc *sc)
+{
+    int i=0;
+    u_int64_t paddr;
+    u_int32_t paddrh, paddrl, status;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, sc->obfl_q_overrun);
+
+    while ( i < tws_queue_depth ) {
+        if ( !sc->sense_bufs[i].posted ) {
+            paddr = sc->sense_bufs[i].hdr_pkt_phy;
+            paddrh = (u_int32_t)( paddr>>32);
+            paddrl = (u_int32_t) paddr;
+            tws_write_reg(sc, TWS_I2O0_HOBQPH, paddrh, 4);
+            tws_write_reg(sc, TWS_I2O0_HOBQPL, paddrl, 4);
+
+            status = tws_read_reg(sc, TWS_I2O0_STATUS, 4);
+            if ( status & TWS_BIT13 ) {
+                TWS_TRACE_DEBUG(sc, "OBFL Overrun", status, TWS_I2O0_STATUS);
+                sc->obfl_q_overrun = true;
+                break;
+            }
+            sc->sense_bufs[i].posted = true;
+        }
+        i++;
+    }
+
+    if ( i == tws_queue_depth )
+        sc->obfl_q_overrun = false;
+}
+
+int
+tws_init_connect(struct tws_softc *sc, u_int16_t mcreadits )
+{
+    struct tws_request *req;
+    struct tws_cmd_init_connect *initc;
+    u_int16_t reqid;
+    u_int64_t mfa;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, mcreadits);
+    req = tws_get_request(sc, TWS_INTERNAL_CMD_REQ);
+
+    if ( req == NULL ) {
+        TWS_TRACE_DEBUG(sc, "no requests", 0, 0);
+        return(FAILURE);
+    }
+
+    tws_swap16(0xbeef); /* just for test */
+    tws_swap32(0xdeadbeef); /* just for test */
+    tws_swap64(0xdeadbeef); /* just for test */
+    initc = &(req->cmd_pkt->cmd.pkt_g.init_connect);
+    /* req->cmd_pkt->hdr.header_desc.size_header = 128; */
+
+    initc->res1__opcode =
+              BUILD_RES__OPCODE(0, TWS_FW_CMD_INIT_CONNECTION);
+    initc->size = 6;
+    initc->request_id = req->request_id;
+    initc->message_credits = mcreadits;
+    initc->features |= TWS_BIT_EXTEND;
+    if ( sc->is64bit && !tws_use_32bit_sgls )
+        initc->features |= TWS_64BIT_SG_ADDRESSES;
+    /* assuming set features is always on */
+
+    initc->size = 6;
+    initc->fw_srl = sc->cinfo.working_srl = TWS_CURRENT_FW_SRL;
+    initc->fw_arch_id = 0;
+    initc->fw_branch = sc->cinfo.working_branch = 0;
+    initc->fw_build = sc->cinfo.working_build = 0;
+
+    req->error_code = tws_submit_command(sc, req);
+    reqid = tws_poll4_response(sc, &mfa);
+    if ( reqid != TWS_INVALID_REQID && reqid == req->request_id ) {
+        sc->cinfo.fw_on_ctlr_srl = initc->fw_srl;
+        sc->cinfo.fw_on_ctlr_branch = initc->fw_branch;
+        sc->cinfo.fw_on_ctlr_build = initc->fw_build;
+        sc->stats.reqs_out++;
+        lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+        req->state = TWS_REQ_STATE_FREE;
+        lockmgr(&sc->gen_lock, LK_RELEASE);
+    }
+    else {
+        /*
+         * REVISIT::If init connect fails we need to reset the ctlr
+         * and try again?
+         */
+        TWS_TRACE(sc, "unexpected req_id ", reqid, 0);
+        TWS_TRACE(sc, "INITCONNECT FAILED", reqid, 0);
+        return(FAILURE);
+    }
+    return(SUCCESS);
+}
+
+void
+tws_display_ctlr_info(struct tws_softc *sc)
+{
+
+    uint8_t fw_ver[16], bios_ver[16], ctlr_model[16], num_phys=0;
+    uint32_t error[4];
+
+    error[0] = tws_get_param(sc, TWS_PARAM_PHYS_TABLE,
+                             TWS_PARAM_CONTROLLER_PHYS_COUNT, 1, &num_phys);
+    error[1] = tws_get_param(sc, TWS_PARAM_VERSION_TABLE,
+                             TWS_PARAM_VERSION_FW, 16, fw_ver);
+    error[2] = tws_get_param(sc, TWS_PARAM_VERSION_TABLE,
+                             TWS_PARAM_VERSION_BIOS, 16, bios_ver);
+    error[3] = tws_get_param(sc, TWS_PARAM_VERSION_TABLE,
+                             TWS_PARAM_CTLR_MODEL, 16, ctlr_model);
+
+    if ( !error[0] && !error[1] && !error[2] && !error[3] ) {
+        device_printf( sc->tws_dev,
+        "Controller details: Model %.16s, %d Phys, Firmware %.16s, BIOS %.16s\n",
+         ctlr_model, num_phys, fw_ver, bios_ver);
+    }
+
+}
+
+int
+tws_send_generic_cmd(struct tws_softc *sc, u_int8_t opcode)
+{
+    struct tws_request *req;
+    struct tws_cmd_generic *cmd;
+
+    TWS_TRACE_DEBUG(sc, "entry", sc, opcode);
+    req = tws_get_request(sc, TWS_INTERNAL_CMD_REQ);
+
+    if ( req == NULL ) {
+        TWS_TRACE_DEBUG(sc, "no requests", 0, 0);
+        return(FAILURE);
+    }
+
+    cmd = &(req->cmd_pkt->cmd.pkt_g.generic);
+    bzero(cmd, sizeof(struct tws_cmd_generic));
+    /* req->cmd_pkt->hdr.header_desc.size_header = 128; */
+    req->cb = tws_cmd_complete;
+
+    cmd->sgl_off__opcode = BUILD_RES__OPCODE(0, opcode);
+    cmd->size = 2;
+    cmd->request_id = req->request_id;
+    cmd->host_id__unit = 0;
+    cmd->status = 0;
+    cmd->flags = 0;
+    cmd->count = 0;
+
+    req->error_code = tws_submit_command(sc, req);
+
+    return(SUCCESS);
+
+}
+
+
+int
+tws_submit_command(struct tws_softc *sc, struct tws_request *req)
+{
+    u_int32_t regl, regh;
+    u_int64_t mfa=0;
+
+    /*
+     * mfa register  read and write must be in order.
+     * Get the io_lock to protect against simultinous
+     * passthru calls
+     */
+    lockmgr(&sc->io_lock, LK_EXCLUSIVE);
+
+    if ( sc->obfl_q_overrun ) {
+        tws_init_obfl_q(sc);
+    }
+
+#ifdef TWS_PULL_MODE_ENABLE
+    regh = (u_int32_t)(req->cmd_pkt_phy >> 32);
+    /* regh = regh | TWS_MSG_ACC_MASK; */
+    mfa = regh;
+    mfa = mfa << 32;
+    regl = (u_int32_t)req->cmd_pkt_phy;
+    regl = regl | TWS_BIT0;
+    mfa = mfa | regl;
+#else
+    regh = tws_read_reg(sc, TWS_I2O0_HIBQPH, 4);
+    mfa = regh;
+    mfa = mfa << 32;
+    regl = tws_read_reg(sc, TWS_I2O0_HIBQPL, 4);
+    mfa = mfa | regl;
+#endif
+
+    lockmgr(&sc->io_lock, LK_RELEASE);
+
+    if ( mfa == TWS_FIFO_EMPTY ) {
+        TWS_TRACE_DEBUG(sc, "inbound fifo empty", mfa, 0);
+
+        /*
+         * Generaly we should not get here.
+         * If the fifo was empty we can't do any thing much
+         * retry later
+         */
+        return(TWS_REQ_ERR_PEND_NOMFA);
+
+    }
+
+#ifndef TWS_PULL_MODE_ENABLE
+    for (int i=mfa; i<(sizeof(struct tws_command_packet)+ mfa -
+                            sizeof( struct tws_command_header)); i++) {
+
+        bus_space_write_1(sc->bus_mfa_tag, sc->bus_mfa_handle,i,
+                               ((u_int8_t *)&req->cmd_pkt->cmd)[i-mfa]);
+
+    }
+#endif
+
+    if ( req->type == TWS_SCSI_IO_REQ ) {
+        lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+        tws_q_insert_tail(sc, req, TWS_BUSY_Q);
+        lockmgr(&sc->q_lock, LK_RELEASE);
+    }
+
+    /*
+     * mfa register  read and write must be in order.
+     * Get the io_lock to protect against simultinous
+     * passthru calls
+     */
+    lockmgr(&sc->io_lock, LK_EXCLUSIVE);
+
+    tws_write_reg(sc, TWS_I2O0_HIBQPH, regh, 4);
+    tws_write_reg(sc, TWS_I2O0_HIBQPL, regl, 4);
+
+    sc->stats.reqs_in++;
+    lockmgr(&sc->io_lock, LK_RELEASE);
+
+    return(TWS_REQ_SUBMIT_SUCCESS);
+
+}
+
+/*
+ * returns true if the respose was available othewise, false.
+ * In the case of error the arg mfa will contain the address and
+ * req_id will be TWS_INVALID_REQID
+ */
+boolean
+tws_get_response(struct tws_softc *sc, u_int16_t *req_id, u_int64_t *mfa)
+{
+    u_int64_t out_mfa=0, val=0;
+    struct tws_outbound_response out_res;
+
+    *req_id = TWS_INVALID_REQID;
+    out_mfa = (u_int64_t)tws_read_reg(sc, TWS_I2O0_HOBQPH, 4);
+
+    if ( out_mfa == TWS_FIFO_EMPTY32 ) {
+        return(false);
+
+    }
+    out_mfa = out_mfa << 32;
+    val = tws_read_reg(sc, TWS_I2O0_HOBQPL, 4);
+    out_mfa = out_mfa | val;
+
+    out_res =  *(struct tws_outbound_response *)&out_mfa;
+
+    if ( !out_res.not_mfa ) {
+        *mfa = out_mfa;
+        return(true);
+    } else {
+        *req_id = out_res.request_id;
+    }
+
+    return(true);
+}
+
+
+
+
+u_int16_t
+tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa)
+{
+    u_int16_t req_id;
+    time_t endt;
+
+    endt = TWS_LOCAL_TIME + TWS_POLL_TIMEOUT;
+    do {
+        if(tws_get_response(sc, &req_id, mfa)) {
+
+            if ( req_id == TWS_INVALID_REQID ) {
+                TWS_TRACE_DEBUG(sc, "invalid req_id", 0, req_id);
+                return(TWS_INVALID_REQID);
+            }
+            return(req_id);
+        }
+    } while (TWS_LOCAL_TIME <= endt);
+    TWS_TRACE_DEBUG(sc, "poll timeout", 0, 0);
+    return(TWS_INVALID_REQID);
+}
+
+boolean
+tws_ctlr_ready(struct tws_softc *sc)
+{
+    u_int32_t reg;
+
+    reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4);
+    if ( reg & TWS_BIT13 )
+        return(true);
+    else
+        return(false);
+}
+
+void
+tws_turn_on_interrupts(struct tws_softc *sc)
+{
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    /* turn on responce and db interrupt only */
+    tws_write_reg(sc, TWS_I2O0_HIMASK, TWS_BIT0, 4);
+
+}
+
+void
+tws_turn_off_interrupts(struct tws_softc *sc)
+{
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+    tws_write_reg(sc, TWS_I2O0_HIMASK, ~0, 4);
+
+}
+
+void
+tws_disable_db_intr(struct tws_softc *sc)
+{
+    u_int32_t reg;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4);
+    reg = reg | TWS_BIT2;
+    tws_write_reg(sc, TWS_I2O0_HIMASK, reg, 4);
+}
+
+void
+tws_enable_db_intr(struct tws_softc *sc)
+{
+    u_int32_t reg;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+    reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4);
+    reg = reg & ~TWS_BIT2;
+    tws_write_reg(sc, TWS_I2O0_HIMASK, reg, 4);
+}
+
+boolean
+tws_ctlr_reset(struct tws_softc *sc)
+{
+
+    u_int32_t reg;
+    time_t endt;
+    /* int i=0; */
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+    tws_assert_soft_reset(sc);
+
+    do {
+        reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4);
+    } while ( reg & TWS_BIT13 );
+
+    endt = TWS_LOCAL_TIME + TWS_RESET_TIMEOUT;
+    do {
+        if(tws_ctlr_ready(sc))
+            return(true);
+    } while (TWS_LOCAL_TIME <= endt);
+    return(false);
+
+}
+
+void
+tws_assert_soft_reset(struct tws_softc *sc)
+{
+    u_int32_t reg;
+
+    reg = tws_read_reg(sc, TWS_I2O0_HIBDB, 4);
+    TWS_TRACE_DEBUG(sc, "in bound door bell read ", reg, TWS_I2O0_HIBDB);
+    tws_write_reg(sc, TWS_I2O0_HIBDB, reg | TWS_BIT8, 4);
+
+}
+
+void
+tws_fetch_aen(void *arg)
+{
+    struct tws_softc *sc = (struct tws_softc *)arg;
+    int error = 0;
+
+    TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+    if ((error = tws_send_scsi_cmd(sc, 0x03 /* REQUEST_SENSE */))) {
+        TWS_TRACE_DEBUG(sc, "aen fetch send in progress", 0, 0);
+    }
+}
+
+void
+tws_aen_synctime_with_host(struct tws_softc *sc)
+{
+
+    int error;
+    long int sync_time;
+
+    TWS_TRACE_DEBUG(sc, "entry", sc, 0);
+
+    sync_time = (TWS_LOCAL_TIME - (3 * 86400)) % 604800;
+    TWS_TRACE_DEBUG(sc, "sync_time,ts", sync_time, time_second);
+    TWS_TRACE_DEBUG(sc, "utc_offset", utc_offset(), 0);
+    error = tws_set_param(sc, TWS_PARAM_TIME_TABLE, TWS_PARAM_TIME_SCHED_TIME,
+                           4, &sync_time);
+    if ( error )
+        TWS_TRACE_DEBUG(sc, "set param failed", sync_time, error);
+}
+
+TUNABLE_INT("hw.tws.use_32bit_sgls", &tws_use_32bit_sgls);
diff --git a/sys/dev/raid/tws/tws_hdm.h b/sys/dev/raid/tws/tws_hdm.h
new file mode 100644 (file)
index 0000000..d2b3d86
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_hdm.h,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+
+/* bit's defination */
+
+#define TWS_BIT0                              0x00000001
+#define TWS_BIT1                              0x00000002
+#define TWS_BIT2                              0x00000004
+#define TWS_BIT3                              0x00000008
+#define TWS_BIT4                              0x00000010
+#define TWS_BIT5                              0x00000020
+#define TWS_BIT6                              0x00000040
+#define TWS_BIT7                              0x00000080
+#define TWS_BIT8                              0x00000100
+#define TWS_BIT9                              0x00000200
+#define TWS_BIT10                             0x00000400
+#define TWS_BIT11                             0x00000800
+#define TWS_BIT12                             0x00001000
+#define TWS_BIT13                             0x00002000
+#define TWS_BIT14                             0x00004000
+#define TWS_BIT15                             0x00008000
+#define TWS_BIT16                             0x00010000
+#define TWS_BIT17                             0x00020000
+#define TWS_BIT18                             0x00040000
+#define TWS_BIT19                             0x00080000
+#define TWS_BIT20                             0x00100000
+#define TWS_BIT21                             0x00200000
+#define TWS_BIT22                             0x00400000
+#define TWS_BIT23                             0x00800000
+#define TWS_BIT24                             0x01000000
+#define TWS_BIT25                             0x02000000
+#define TWS_BIT26                             0x04000000
+#define TWS_BIT27                             0x08000000
+#define TWS_BIT28                             0x10000000
+#define TWS_BIT29                             0x20000000
+#define TWS_BIT30                             0x40000000
+#define TWS_BIT31                             0x80000000
+
+#define TWS_SENSE_DATA_LENGTH                 18
+#define TWS_ERROR_SPECIFIC_DESC_LEN           98
+
+/* response codes */
+#define TWS_SENSE_SCSI_CURRENT_ERROR          0x70
+#define TWS_SENSE_SCSI_DEFERRED_ERROR         0x71
+
+#define TWS_SRC_CTRL_ERROR                    3
+#define TWS_SRC_CTRL_EVENT                    4
+#define TWS_SRC_FREEBSD_DRIVER                5
+#define TWS_SRC_FREEBSD_OS                    8
+
+
+enum tws_sense_severity {
+    error = 1,
+    warning ,
+    info,
+    debug,
+};
+
+/*
+ * Some errors of interest (in cmd_hdr->status_block.error) when a command
+ * is completed by the firmware with an error.
+ */
+#define TWS_ERROR_LOGICAL_UNIT_NOT_SUPPORTED    0x010a
+#define TWS_ERROR_NOT_SUPPORTED                 0x010D
+#define TWS_ERROR_UNIT_OFFLINE                  0x0128
+#define TWS_ERROR_MORE_DATA                     0x0231
+
+
+/* AEN codes of interest. */
+#define TWS_AEN_QUEUE_EMPTY                     0x00
+#define TWS_AEN_SOFT_RESET                      0x01
+#define TWS_AEN_SYNC_TIME_WITH_HOST             0x31
+
+
+/* AEN severity */
+#define TWS_SEVERITY_ERROR                      0x1
+#define TWS_SEVERITY_WARNING                    0x2
+#define TWS_SEVERITY_INFO                       0x3
+#define TWS_SEVERITY_DEBUG                      0x4
+
+#define TWS_64BIT_SG_ADDRESSES                  0x00000001
+#define TWS_BIT_EXTEND                          0x00000002
+
+#define TWS_BASE_FW_SRL                         24
+#define TWS_BASE_FW_BRANCH                      0
+#define TWS_BASE_FW_BUILD                       1
+#define TWS_CURRENT_FW_SRL                      40
+
+#define TWS_CURRENT_FW_BRANCH                   8
+#define TWS_CURRENT_FW_BUILD                    4
+#define TWS_CURRENT_ARCH_ID                     0x000A
+
+
+#define TWS_FIFO_EMPTY                          0xFFFFFFFFFFFFFFFFull
+#define TWS_FIFO_EMPTY32                        0xFFFFFFFFull
+
+
+/* Register offsets from base address. */
+#define TWS_CONTROL_REGISTER_OFFSET             0x0
+#define TWS_STATUS_REGISTER_OFFSET              0x4
+#define TWS_COMMAND_QUEUE_OFFSET                0x8
+#define TWS_RESPONSE_QUEUE_OFFSET               0xC
+#define TWS_COMMAND_QUEUE_OFFSET_LOW            0x20
+#define TWS_COMMAND_QUEUE_OFFSET_HIGH           0x24
+#define TWS_LARGE_RESPONSE_QUEUE_OFFSET         0x30
+
+/* I2O offsets */
+#define TWS_I2O0_STATUS                         0x0
+
+#define TWS_I2O0_HIBDB                          0x20
+
+#define TWS_I2O0_HISTAT                         0x30
+#define TWS_I2O0_HIMASK                         0x34
+
+#define TWS_I2O0_HIBQP                          0x40
+#define TWS_I2O0_HOBQP                          0x44
+
+#define TWS_I2O0_CTL                            0x74
+
+#define TWS_I2O0_IOBDB                          0x9C
+#define TWS_I2O0_HOBDBC                         0xA0
+
+#define TWS_I2O0_SCRPD3                         0xBC
+
+#define TWS_I2O0_HIBQPL                         0xC0 /* 64bit inb port low */
+#define TWS_I2O0_HIBQPH                         0xC4 /* 64bit inb port high */
+#define TWS_I2O0_HOBQPL                         0xC8 /* 64bit out port low */
+#define TWS_I2O0_HOBQPH                         0xCC /* 64bit out port high */
+
+/* IOP related */
+#define TWS_I2O0_IOPOBQPL                       0xD8 /* OBFL */
+#define TWS_I2O0_IOPOBQPH                       0xDC /* OBFH */
+#define TWS_I2O0_SRC_ADDRH                      0xF8 /* Msg ASA */
+
+#define TWS_MSG_ACC_MASK                        0x20000000
+#define TWS_32BIT_MASK                          0xFFFFFFFF
+
+/* revisit */
+#define TWS_FW_CMD_NOP                     0x0
+#define TWS_FW_CMD_INIT_CONNECTION         0x01
+#define TWS_FW_CMD_EXECUTE_SCSI            0x10
+
+#define TWS_FW_CMD_ATA_PASSTHROUGH         0x11
+#define TWS_FW_CMD_GET_PARAM               0x12
+#define TWS_FW_CMD_SET_PARAM               0x13
+
+
+#define BUILD_SGL_OFF__OPCODE(sgl_off, opcode)  \
+        ((sgl_off << 5) & 0xE0) | (opcode & 0x1F)       /* 3:5 */
+
+#define BUILD_RES__OPCODE(res, opcode)          \
+        ((res << 5) & 0xE0) | (opcode & 0x1F)           /* 3:5 */
+
+#define GET_OPCODE(sgl_off__opcode)     \
+        (sgl_off__opcode & 0x1F)                        /* 3:5 */
+
+
+
+/* end revisit */
+
+
+/* Table #'s and id's of parameters of interest in firmware's param table. */
+#define TWS_PARAM_VERSION_TABLE         0x0402
+#define TWS_PARAM_VERSION_FW            3       /* firmware version [16] */
+#define TWS_PARAM_VERSION_BIOS          4       /* BIOSs version [16] */
+#define TWS_PARAM_CTLR_MODEL            8       /* Controller model [16] */
+
+#define TWS_PARAM_CONTROLLER_TABLE      0x0403
+#define TWS_PARAM_CONTROLLER_PORT_COUNT 3       /* number of ports [1] */
+
+#define TWS_PARAM_TIME_TABLE            0x40A
+#define TWS_PARAM_TIME_SCHED_TIME       0x3
+
+#define TWS_PARAM_PHYS_TABLE            0x0001
+#define TWS_PARAM_CONTROLLER_PHYS_COUNT 2       /* number of phys */
+
+#define TWS_9K_PARAM_DESCRIPTOR         0x8000
+
+
+/* ----------- request  ------------- */
+
+
+#pragma pack(1)
+
+struct tws_cmd_init_connect {
+    u_int8_t        res1__opcode;   /* 3:5 */
+    u_int8_t        size;
+    u_int8_t        request_id;
+    u_int8_t        res2;
+    u_int8_t        status;
+    u_int8_t        flags;
+    u_int16_t       message_credits;
+    u_int32_t       features;
+    u_int16_t       fw_srl;
+    u_int16_t       fw_arch_id;
+    u_int16_t       fw_branch;
+    u_int16_t       fw_build;
+    u_int32_t       result;
+};
+
+/* Structure for downloading firmware onto the controller. */
+struct tws_cmd_download_firmware {
+    u_int8_t        sgl_off__opcode;/* 3:5 */
+    u_int8_t        size;
+    u_int8_t        request_id;
+    u_int8_t        unit;
+    u_int8_t        status;
+    u_int8_t        flags;
+    u_int16_t       param;
+    u_int8_t        sgl[1];
+};
+
+/* Structure for hard resetting the controller. */
+struct tws_cmd_reset_firmware {
+    u_int8_t        res1__opcode;   /* 3:5 */
+    u_int8_t        size;
+    u_int8_t        request_id;
+    u_int8_t        unit;
+    u_int8_t        status;
+    u_int8_t        flags;
+    u_int8_t        res2;
+    u_int8_t        param;
+};
+
+
+/* Structure for sending get/set param commands. */
+struct tws_cmd_param {
+    u_int8_t        sgl_off__opcode;/* 3:5 */
+    u_int8_t        size;
+    u_int8_t        request_id;
+    u_int8_t        host_id__unit;  /* 4:4 */
+    u_int8_t        status;
+    u_int8_t        flags;
+    u_int16_t       param_count;
+    u_int8_t        sgl[1];
+};
+
+/* Generic command packet. */
+struct tws_cmd_generic {
+    u_int8_t        sgl_off__opcode;/* 3:5 */
+    u_int8_t        size;
+    u_int8_t        request_id;
+    u_int8_t        host_id__unit;  /* 4:4 */
+    u_int8_t        status;
+    u_int8_t        flags;
+    u_int16_t       count;  /* block cnt, parameter cnt, message credits */
+};
+
+
+
+
+/* Command packet header. */
+struct tws_command_header {
+    u_int8_t        sense_data[TWS_SENSE_DATA_LENGTH];
+    struct { /* status block - additional sense data */
+        u_int16_t       srcnum;
+        u_int8_t        reserved;
+        u_int8_t        status;
+        u_int16_t       error;
+        u_int8_t        res__srcid;     /* 4:4 */
+        u_int8_t        res__severity;  /* 5:3 */
+    } status_block;
+    u_int8_t        err_specific_desc[TWS_ERROR_SPECIFIC_DESC_LEN];
+    struct { /* sense buffer descriptor */
+        u_int8_t        size_header;
+        u_int16_t       request_id;
+        u_int8_t        size_sense;
+    } header_desc;
+};
+
+/* Command - 1024 byte size including header (128+24+896)*/
+union tws_command_giga {
+    struct tws_cmd_init_connect       init_connect;
+    struct tws_cmd_download_firmware  download_fw;
+    struct tws_cmd_reset_firmware     reset_fw;
+    struct tws_cmd_param              param;
+    struct tws_cmd_generic            generic;
+    u_int8_t        padding[1024 - sizeof(struct tws_command_header)];
+};
+
+/* driver command pkt - 1024 byte size including header(128+24+744+128) */
+/* h/w & f/w supported command size excluding header 768 */
+struct tws_command_apache {
+    u_int8_t        res__opcode;    /* 3:5 */
+    u_int8_t        unit;
+    u_int16_t       lun_l4__req_id; /* 4:12 */
+    u_int8_t        status;
+    u_int8_t        sgl_offset;     /* offset (in bytes) to sg_list,
+                                     from the end of sgl_entries */
+    u_int16_t       lun_h4__sgl_entries;
+    u_int8_t        cdb[16];
+    u_int8_t        sg_list[744];   /* 768 - 24 */
+    u_int8_t        padding[128];   /* make it 1024 bytes */
+};
+
+struct tws_command_packet {
+    struct tws_command_header hdr;
+    union {
+        union tws_command_giga pkt_g;
+        struct tws_command_apache pkt_a;
+    } cmd;
+};
+
+/* Structure describing payload for get/set param commands. */
+struct tws_getset_param {
+    u_int16_t       table_id;
+    u_int8_t        parameter_id;
+    u_int8_t        reserved;
+    u_int16_t       parameter_size_bytes;
+    u_int16_t       parameter_actual_size_bytes;
+    u_int8_t        data[1];
+};
+
+struct tws_outbound_response {
+    u_int32_t     not_mfa   :1;   /* 1 if the structure is valid else MFA */
+    u_int32_t     reserved  :7;   /* reserved bits */
+    u_int32_t     status    :8;   /* should be 0 */
+    u_int32_t     request_id:16;  /* request id */
+};
+
+
+/* Scatter/Gather list entry with 32 bit addresses. */
+struct tws_sg_desc32 {
+    u_int32_t     address;
+    u_int32_t     length  :24;
+    u_int32_t     flag    :8;
+};
+
+/* Scatter/Gather list entry with 64 bit addresses. */
+struct tws_sg_desc64 {
+    u_int64_t     address;
+    u_int64_t     length   :32;
+    u_int64_t     reserved :24;
+    u_int64_t     flag     :8;
+};
+
+/*
+ * Packet that describes an AEN/error generated by the controller,
+ * shared with user
+ */
+struct tws_event_packet {
+    u_int32_t       sequence_id;
+    u_int32_t       time_stamp_sec;
+    u_int16_t       aen_code;
+    u_int8_t        severity;
+    u_int8_t        retrieved;
+    u_int8_t        repeat_count;
+    u_int8_t        parameter_len;
+    u_int8_t        parameter_data[TWS_ERROR_SPECIFIC_DESC_LEN];
+    u_int32_t       event_src;
+    u_int8_t        severity_str[20];
+};
+
+
+
+#pragma pack()
+
+struct tws_sense {
+    struct tws_command_header *hdr;
+    u_int64_t  hdr_pkt_phy;
+    boolean posted;
+};
+
+struct tws_request {
+    struct tws_command_packet *cmd_pkt; /* command pkt */
+    u_int64_t    cmd_pkt_phy;    /* cmd pkt physical address */
+    void         *data;          /* ptr to data being passed to fw */
+    u_int32_t    length;         /* length of data being passed to fw */
+
+    u_int32_t    state;          /* request state */
+    u_int32_t    type;           /* request type */
+    u_int32_t    flags;          /* request flags */
+
+    u_int32_t    error_code;     /* error encountered before submission
+                                        of request to fw, if any */
+
+    u_int32_t    request_id;     /* request id for tracking with fw */
+    void         (*cb)(struct tws_request *);      /* callback func */
+    bus_dmamap_t dma_map;        /* dma map */
+    union ccb    *ccb_ptr;       /* pointer to ccb */
+    struct callout thandle;      /* handle to req timeout */
+    struct tws_softc *sc;        /* pointer back to ctlr softc */
+
+    struct tws_request *next;    /* pointer to next request */
+    struct tws_request *prev;    /* pointer to prev request */
+};
diff --git a/sys/dev/raid/tws/tws_services.c b/sys/dev/raid/tws/tws_services.c
new file mode 100644 (file)
index 0000000..2db8c89
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_cam.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+#include <dev/raid/tws/tws.h>
+#include <dev/raid/tws/tws_hdm.h>
+#include <dev/raid/tws/tws_services.h>
+#include <sys/time.h>
+
+void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type );
+struct tws_request * tws_q_remove_request(struct tws_softc *sc,
+                                struct tws_request *req, u_int8_t q_type );
+struct tws_request *tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type );
+void tws_q_insert_head(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type );
+struct tws_request * tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type );
+void tws_print_stats(void *arg);
+
+struct tws_sense *tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa);
+
+
+
+struct error_desc array[] = {
+    { "Cannot add sysctl tree node", 0x2000, ERROR,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Register window not available", 0x2001, ERROR,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Can't allocate register window", 0x2002, ERROR,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Can't allocate interrupt", 0x2003, ERROR,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Can't set up interrupt", 0x2004, ERROR,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Couldn't intialize CAM", 0x2007, ERROR,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Couldn't create SIM device queue", 0x2100, ENOMEM,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Unable to  create SIM entry", 0x2101, ENOMEM,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Unable to  register the bus", 0x2102, ENXIO,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Unable to  create the path", 0x2103, ENXIO,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Bus scan request to CAM failed", 0x2104, ENXIO,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Unable to intialize the driver", 0x2008, ENXIO,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+    { "Unable to intialize the controller", 0x2009, ENXIO,
+       "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" },
+};
+
+void
+tws_trace(const char *file, const char *fun, int linenum,
+          struct tws_softc *sc, char *desc, u_int64_t val1, u_int64_t val2)
+{
+
+
+    struct tws_trace_rec *rec = (struct tws_trace_rec *)sc->trace_q.q;
+    volatile u_int16_t head, tail;
+    char fmt[256];
+
+    head = sc->trace_q.head;
+    tail = sc->trace_q.tail;
+/*
+    getnanotime(&rec[tail].ts);
+*/
+    strncpy(rec[tail].fname, file, TWS_TRACE_FNAME_LEN);
+    strncpy(rec[tail].func, fun, TWS_TRACE_FUNC_LEN);
+    rec[tail].linenum = linenum;
+    strncpy(rec[tail].desc, desc, TWS_TRACE_DESC_LEN);
+    rec[tail].val1 = val1;
+    rec[tail].val2 = val2;
+
+    tail = (tail+1) % sc->trace_q.depth;
+
+    if ( head == tail ) {
+        sc->trace_q.overflow = 1;
+        sc->trace_q.head = (head+1) % sc->trace_q.depth;
+    }
+    sc->trace_q.tail = tail;
+
+/*
+    tws_circular_q_insert(sc, &sc->trace_q,
+                              &rec, sizeof(struct tws_trace_rec));
+*/
+    if ( sc->is64bit )
+        strcpy(fmt, "%05d:%s::%s :%s: 0x%016lx : 0x%016lx \n");
+    else
+        strcpy(fmt, "%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n");
+
+/*
+    kprintf("%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n",
+            linenum, file, fun, desc, val1, val2);
+*/
+    kprintf(fmt, linenum, file, fun, desc, val1, val2);
+}
+
+void
+tws_log(struct tws_softc *sc, int index)
+{
+    device_printf((sc)->tws_dev, array[index].fmt,
+                    array[index].error_str,
+                    array[index].error_code,
+                    array[index].severity_level,
+                    array[index].desc );
+}
+
+/* ----------- swap functions ----------- */
+
+
+u_int16_t
+tws_swap16(u_int16_t val)
+{
+    return((val << 8) | (val >> 8));
+}
+
+u_int32_t
+tws_swap32(u_int32_t val)
+{
+    return(((val << 24) | ((val << 8) & (0xFF0000)) |
+           ((val >> 8) & (0xFF00)) | (val >> 24)));
+}
+
+
+u_int64_t
+tws_swap64(u_int64_t val)
+{
+    return((((u_int64_t)(tws_swap32(((u_int32_t *)(&(val)))[1]))) << 32) |
+           ((u_int32_t)(tws_swap32(((u_int32_t *)(&(val)))[0]))));
+}
+
+
+/* ----------- reg access ----------- */
+
+
+void
+tws_write_reg(struct tws_softc *sc, int offset,
+                  u_int32_t value, int size)
+{
+    bus_space_tag_t         bus_tag = sc->bus_tag;
+    bus_space_handle_t      bus_handle = sc->bus_handle;
+
+    if (size == 4)
+        bus_space_write_4(bus_tag, bus_handle, offset, value);
+    else
+        if (size == 2)
+            bus_space_write_2(bus_tag, bus_handle, offset,
+                                     (u_int16_t)value);
+        else
+            bus_space_write_1(bus_tag, bus_handle, offset, (u_int8_t)value);
+}
+
+u_int32_t
+tws_read_reg(struct tws_softc *sc, int offset, int size)
+{
+    bus_space_tag_t bus_tag = sc->bus_tag;
+    bus_space_handle_t bus_handle = sc->bus_handle;
+
+    if (size == 4)
+        return((u_int32_t)bus_space_read_4(bus_tag, bus_handle, offset));
+    else if (size == 2)
+            return((u_int32_t)bus_space_read_2(bus_tag, bus_handle, offset));
+         else
+            return((u_int32_t)bus_space_read_1(bus_tag, bus_handle, offset));
+}
+
+/* --------------------- Q service --------------------- */
+
+/*
+ * intialize q  pointers with null.
+ */
+void
+tws_init_qs(struct tws_softc *sc)
+{
+
+    lockmgr(&sc->q_lock, LK_EXCLUSIVE);
+    for(int i=0;i<TWS_MAX_QS;i++) {
+        sc->q_head[i] = NULL;
+        sc->q_tail[i] = NULL;
+    }
+    lockmgr(&sc->q_lock, LK_RELEASE);
+
+}
+
+/* called with lock held */
+static void
+tws_insert2_empty_q(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type )
+{
+
+    KKASSERT(lockstatus(&sc->q_lock, curthread) != 0);
+    req->next = req->prev = NULL;
+    sc->q_head[q_type] = sc->q_tail[q_type] = req;
+
+}
+
+/* called with lock held */
+void
+tws_q_insert_head(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type )
+{
+
+    KKASSERT(lockstatus(&sc->q_lock, curthread) != 0);
+    if ( sc->q_head[q_type] == NULL ) {
+        tws_insert2_empty_q(sc, req, q_type);
+    } else {
+        req->next = sc->q_head[q_type];
+        req->prev = NULL;
+        sc->q_head[q_type]->prev = req;
+        sc->q_head[q_type] = req;
+    }
+
+}
+
+/* called with lock held */
+void
+tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
+                                u_int8_t q_type )
+{
+
+    KKASSERT(lockstatus(&sc->q_lock, curthread) != 0);
+    if ( sc->q_tail[q_type] == NULL ) {
+        tws_insert2_empty_q(sc, req, q_type);
+    } else {
+        req->prev = sc->q_tail[q_type];
+        req->next = NULL;
+        sc->q_tail[q_type]->next = req;
+        sc->q_tail[q_type] = req;
+    }
+
+}
+
+/* called with lock held */
+struct tws_request *
+tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type )
+{
+
+    struct tws_request *r;
+
+    KKASSERT(lockstatus(&sc->q_lock, curthread) != 0);
+    r = sc->q_head[q_type];
+    if ( !r )
+        return(NULL);
+    if ( r->next == NULL &&  r->prev == NULL ) {
+        /* last element  */
+        sc->q_head[q_type] = sc->q_tail[q_type] = NULL;
+    } else {
+        sc->q_head[q_type] = r->next;
+        r->next->prev = NULL;
+        r->next = NULL;
+        r->prev = NULL;
+    }
+    return(r);
+}
+
+/* called with lock held */
+struct tws_request *
+tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type )
+{
+
+    struct tws_request *r;
+
+    KKASSERT(lockstatus(&sc->q_lock, curthread) != 0);
+    r = sc->q_tail[q_type];
+    if ( !r )
+        return(NULL);
+    if ( r->next == NULL &&  r->prev == NULL ) {
+        /* last element  */
+        sc->q_head[q_type] = sc->q_tail[q_type] = NULL;
+    } else {
+        sc->q_tail[q_type] = r->prev;
+        r->prev->next = NULL;
+        r->next = NULL;
+        r->prev = NULL;
+    }
+    return(r);
+}
+
+/* returns removed request if successful. return NULL otherwise */
+/* called with lock held */
+struct tws_request *
+tws_q_remove_request(struct tws_softc *sc, struct tws_request *req,
+                                 u_int8_t q_type )
+{
+
+    struct tws_request *r;
+
+    KKASSERT(lockstatus(&sc->q_lock, curthread) != 0);
+    if ( req == NULL ) {
+        TWS_TRACE_DEBUG(sc, "null req", 0, q_type);
+        return(NULL);
+    }
+
+    if ( req == sc->q_head[q_type] )
+        return(tws_q_remove_head(sc, q_type));
+    if ( req == sc->q_tail[q_type] )
+        return(tws_q_remove_tail(sc, q_type));
+
+
+    /* The given node is not at head or tail.
+     * It's in the middle and there are more than
+     * 2 elements on the q.
+     */
+
+    if ( req->next == NULL || req->prev == NULL ) {
+        TWS_TRACE_DEBUG(sc, "invalid req", 0, q_type);
+        return(NULL);
+    }
+
+/* debug only */
+    r = sc->q_head[q_type];
+    while ( r ) {
+        if ( req == r )
+            break;
+        r = r->next;
+    }
+
+    if ( !r ) {
+        TWS_TRACE_DEBUG(sc, "req not in q", 0, req->request_id);
+        return(NULL);
+    }
+/* debug end */
+
+    req->prev->next = r->next;
+    req->next->prev = r->prev;
+    req->next = NULL;
+    req->prev = NULL;
+    return(req);
+}
+
+struct tws_sense *
+tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa)
+{
+    struct tws_sense *s;
+    int i;
+    TWS_TRACE_DEBUG(sc, "entry",sc,mfa);
+
+    i = (mfa - sc->dma_mem_phys) / sizeof(struct tws_command_packet);
+    if ( i>= 0 && i<tws_queue_depth) {
+        s = &sc->sense_bufs[i];
+        if ( mfa == s->hdr_pkt_phy )
+            return(s);
+    }
+
+    TWS_TRACE_DEBUG(sc, "return null",0,mfa);
+    return(NULL);
+
+}
+
+/* --------------------- Q service end --------------------- */
+/* --------------------- misc service start --------------------- */
+
+
+void
+tws_print_stats(void *arg)
+{
+
+    struct tws_softc *sc = (struct tws_softc *)arg;
+
+    TWS_TRACE(sc, "reqs(in, out)", sc->stats.reqs_in, sc->stats.reqs_out);
+    TWS_TRACE(sc, "reqs(err, intrs)", sc->stats.reqs_errored
+                                      , sc->stats.num_intrs);
+    TWS_TRACE(sc, "reqs(ioctls, scsi)", sc->stats.ioctls
+                                      , sc->stats.scsi_ios);
+    callout_reset(&sc->print_stats_handle, 300*hz, tws_print_stats, sc);
+
+}
+/* --------------------- misc service end --------------------- */
diff --git a/sys/dev/raid/tws/tws_services.h b/sys/dev/raid/tws/tws_services.h
new file mode 100644 (file)
index 0000000..502e178
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_services.h,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+
+/* #define TWS_DEBUG on */
+
+void tws_trace(const char *file, const char *fun, int linenum,
+         struct tws_softc *sc,  char *desc, u_int64_t val1, u_int64_t val2);
+void tws_log(struct tws_softc *sc, int index);
+u_int32_t tws_read_reg(struct tws_softc *sc,
+                  int offset, int size);
+void tws_write_reg(struct tws_softc *sc, int offset,
+                  u_int32_t value, int size);
+
+u_int16_t tws_swap16(u_int16_t val);
+u_int32_t tws_swap32(u_int32_t val);
+u_int64_t tws_swap64(u_int64_t val);
+
+void tws_init_qs(struct tws_softc *sc);
+
+
+
+/* ----------------- trace ----------------- */
+
+#define TWS_TRACE_ON on /* Alawys on - use wisely to trace errors */
+
+#ifdef TWS_DEBUG
+    #define TWS_TRACE_DEBUG_ON on
+#endif
+
+#ifdef TWS_TRACE_DEBUG_ON
+    #define TWS_TRACE_DEBUG(sc, desc, val1, val2) \
+            tws_trace(__FILE__, __func__, __LINE__, sc, desc, \
+                                   (u_int64_t)val1, (u_int64_t)val2)
+#else
+    #define TWS_TRACE_DEBUG(sc, desc, val1, val2)
+#endif
+
+#ifdef TWS_TRACE_ON
+    #define TWS_TRACE(sc, desc, val1, val2) \
+            tws_trace(__FILE__, __func__, __LINE__, sc, desc, \
+                                   (u_int64_t)val1, (u_int64_t)val2)
+#else
+    #define TWS_TRACE(sc, desc, val1, val2)
+#endif
+
+/* ---------------- logging ---------------- */
+
+
+/* ---------------- logging ---------------- */
+enum error_index {
+    SYSCTL_TREE_NODE_ADD,
+    PCI_COMMAND_READ,
+    ALLOC_MEMORY_RES,
+    ALLOC_IRQ_RES,
+    SETUP_INTR_RES,
+    TWS_CAM_ATTACH,
+    CAM_SIMQ_ALLOC,
+    CAM_SIM_ALLOC,
+    TWS_XPT_BUS_REGISTER,
+    TWS_XPT_CREATE_PATH,
+    TWS_BUS_SCAN_REQ,
+    TWS_INIT_FAILURE,
+    TWS_CTLR_INIT_FAILURE,
+};
+
+enum severity {
+    ERROR = 1,
+    WARNING,
+    INFO,
+    _DEBUG,                    /* XXX swildner: conflict with DEBUG option */
+};
+
+struct error_desc {
+    char desc[256];
+    u_int32_t error_code;
+    int severity_level;
+    char *fmt;
+    char *error_str;
+};
+
+extern struct error_desc array[];
+/* ----------- q services ------------- */
+
+#define TWS_FREE_Q        0
+#define TWS_PENDING_Q     1
+#define TWS_BUSY_Q        2
+#define TWS_COMPLETE_Q    3
+
+#define TWS_REQ_SUBMIT_SUCCESS 0
+/* req error codes */
+#define TWS_REQ_ERR_INPROGRESS 1
+#define TWS_REQ_ERR_PEND_NOMFA 2
+#define TWS_REQ_REQUEUE        3
+
+#define TWS_REQ_ERR_INVALID    0xdead
+
+
+/* ------------------------ */
+#define TWS_LOCAL_TIME (time_second - (tz.tz_minuteswest * 60) -   \
+                  (wall_cmos_clock ? adjkerntz : 0))
diff --git a/sys/dev/raid/tws/tws_user.c b/sys/dev/raid/tws/tws_user.c
new file mode 100644 (file)
index 0000000..80f80b0
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_user.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+#include <dev/raid/tws/tws.h>
+#include <dev/raid/tws/tws_services.h>
+#include <dev/raid/tws/tws_hdm.h>
+#include <dev/raid/tws/tws_user.h>
+
+
+d_ioctl_t    tws_ioctl;
+
+void tws_passthru_complete(struct tws_request *req);
+extern void tws_circular_aenq_insert(struct tws_softc *sc,
+                    struct tws_circular_q *cq, struct tws_event_packet *aen);
+
+
+static int tws_passthru(struct tws_softc *sc, void *buf);
+static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf);
+
+extern int tws_bus_scan(struct tws_softc *sc);
+extern struct tws_request *tws_get_request(struct tws_softc *sc,
+                                           u_int16_t type);
+extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req);
+extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req);
+extern uint8_t tws_get_state(struct tws_softc *sc);
+extern void tws_reset(void *arg);
+
+int
+tws_ioctl(struct dev_ioctl_args *ap)
+{
+    cdev_t dev = ap->a_head.a_dev;
+    u_long cmd = ap->a_cmd;
+    caddr_t buf = ap->a_data;
+    struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1);
+    int error;
+
+    TWS_TRACE_DEBUG(sc, "entry", sc, cmd);
+    sc->stats.ioctls++;
+    switch(cmd) {
+        case TWS_IOCTL_FIRMWARE_PASS_THROUGH :
+            error = tws_passthru(sc, (void *)buf);
+            break;
+        case TWS_IOCTL_SCAN_BUS :
+            TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0);
+            lockmgr(&sc->sim_lock, LK_EXCLUSIVE);
+            error = tws_bus_scan(sc);
+            lockmgr(&sc->sim_lock, LK_RELEASE);
+            break;
+        default :
+            TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf);
+            error = tws_ioctl_aen(sc, cmd, (void *)buf);
+            break;
+
+    }
+    return(error);
+}
+
+static int
+tws_passthru(struct tws_softc *sc, void *buf)
+{
+    struct tws_request *req;
+    struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf;
+    int error;
+    u_int16_t lun4;
+
+    if ( tws_get_state(sc) == TWS_RESET ) {
+        return(EBUSY);
+    }
+
+    do {
+        req = tws_get_request(sc, TWS_PASSTHRU_REQ);
+        if ( !req ) {
+            sc->chan = 1;
+            error = tsleep((void *)&sc->chan,  0,
+                                   "tws_sleep", TWS_IO_TIMEOUT*hz);
+            if ( error == EWOULDBLOCK ) {
+                return(ETIMEDOUT);
+            }
+        } else {
+            break;
+        }
+    }while(1);
+
+    req->length = ubuf->driver_pkt.buffer_length;
+    TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id);
+    if ( req->length ) {
+        req->data = kmalloc(req->length, M_TWS, M_WAITOK | M_ZERO);
+        error = copyin(ubuf->pdata, req->data, req->length);
+    }
+    req->flags = TWS_DIR_IN | TWS_DIR_OUT;
+    req->cb = tws_passthru_complete;
+
+    memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd,
+                              sizeof(struct tws_command_apache));
+
+    if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) ==
+                                               TWS_FW_CMD_EXECUTE_SCSI ) {
+        lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000;
+        req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id;
+    } else {
+        req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id;
+
+    }
+
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    req->error_code = tws_map_request(sc, req);
+
+    error = lksleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IO_TIMEOUT*hz);
+    if ( error == EWOULDBLOCK ) {
+        error = ETIMEDOUT;
+        TWS_TRACE_DEBUG(sc, "lksleep timeout", error, req->request_id);
+        tws_reset((void *)sc);
+    }
+
+    if ( req->error_code == TWS_REQ_REQUEUE ) {
+        error = EBUSY;
+    }
+
+    tws_unmap_request(sc, req);
+
+    memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache));
+    memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache));
+    if ( !error && req->length ) {
+        error = copyout(req->data, ubuf->pdata, req->length);
+    }
+
+    kfree(req->data, M_TWS);
+    req->state = TWS_REQ_STATE_FREE;
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+    if ( error )
+        TWS_TRACE_DEBUG(sc, "errored", error, 0);
+    if ( req->error_code  != TWS_REQ_SUBMIT_SUCCESS )
+        ubuf->driver_pkt.os_status = error;
+    if ( sc->chan && tws_get_state(sc) != TWS_RESET ) {
+        sc->chan = 0;
+        wakeup((void *)&sc->chan);
+    }
+    return(error);
+}
+
+void
+tws_passthru_complete(struct tws_request *req)
+{
+    struct tws_softc *sc = req->sc;
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    wakeup_one(req);
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+
+}
+
+static void
+tws_retrive_aen(struct tws_softc *sc, u_long cmd,
+                            struct tws_ioctl_packet *ubuf)
+{
+    u_int16_t index=0;
+    struct tws_event_packet eventp, *qp;
+
+    if ( sc->aen_q.head == sc->aen_q.tail ) {
+        ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
+        return;
+    }
+
+    ubuf->driver_pkt.status = 0;
+
+    /*
+     * once this flag is set cli will not display alarms
+     * needs a revisit from tools?
+     */
+    if ( sc->aen_q.overflow ) {
+        ubuf->driver_pkt.status = TWS_AEN_OVERFLOW;
+        sc->aen_q.overflow = 0; /* reset */
+    }
+
+    qp = (struct tws_event_packet *)sc->aen_q.q;
+
+    switch (cmd) {
+        case TWS_IOCTL_GET_FIRST_EVENT :
+            index = sc->aen_q.head;
+            break;
+        case TWS_IOCTL_GET_LAST_EVENT :
+            /* index = tail-1 */
+            index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth;
+            break;
+        case TWS_IOCTL_GET_NEXT_EVENT :
+            memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
+            index = sc->aen_q.head;
+            do {
+                if ( qp[index].sequence_id ==
+                           (eventp.sequence_id + 1) )
+                    break;
+                index  = (index+1) % sc->aen_q.depth;
+            }while ( index != sc->aen_q.tail );
+            if ( index == sc->aen_q.tail ) {
+                ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
+                return;
+            }
+            break;
+        case TWS_IOCTL_GET_PREVIOUS_EVENT :
+            memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
+            index = sc->aen_q.head;
+            do {
+                if ( qp[index].sequence_id ==
+                           (eventp.sequence_id - 1) )
+                    break;
+                index  = (index+1) % sc->aen_q.depth;
+            }while ( index != sc->aen_q.tail );
+            if ( index == sc->aen_q.tail ) {
+                ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
+                return;
+            }
+            break;
+        default :
+            TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd);
+            ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
+            return;
+    }
+
+    memcpy(ubuf->data_buf, &qp[index],
+                           sizeof(struct tws_event_packet));
+    qp[index].retrieved = TWS_AEN_RETRIEVED;
+
+    return;
+
+}
+
+static int
+tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf)
+{
+
+    struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf;
+    struct tws_compatibility_packet cpkt;
+    struct tws_lock_packet lpkt;
+    time_t ctime;
+
+    lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
+    ubuf->driver_pkt.status = 0;
+    switch(cmd) {
+        case TWS_IOCTL_GET_FIRST_EVENT :
+        case TWS_IOCTL_GET_LAST_EVENT :
+        case TWS_IOCTL_GET_NEXT_EVENT :
+        case TWS_IOCTL_GET_PREVIOUS_EVENT :
+            tws_retrive_aen(sc,cmd,ubuf);
+            break;
+        case TWS_IOCTL_GET_LOCK :
+            ctime = TWS_LOCAL_TIME;
+            memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet));
+            if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) ||
+                 (lpkt.force_flag) ||
+                 (ctime >= sc->ioctl_lock.timeout) ) {
+                sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD;
+                sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000);
+                lpkt.time_remaining_msec = lpkt.timeout_msec;
+            }  else {
+                lpkt.time_remaining_msec = (u_int32_t)
+                          ((sc->ioctl_lock.timeout - ctime) * 1000);
+                ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD;
+
+            }
+            break;
+        case TWS_IOCTL_RELEASE_LOCK :
+            if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) {
+                ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD;
+            } else {
+                sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE;
+                ubuf->driver_pkt.status = 0;
+            }
+            break;
+        case TWS_IOCTL_GET_COMPATIBILITY_INFO :
+            TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd);
+
+            memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING,
+                                         sizeof(TWS_DRIVER_VERSION_STRING));
+            cpkt.working_srl = sc->cinfo.working_srl;
+            cpkt.working_branch = sc->cinfo.working_branch;
+            cpkt.working_build = sc->cinfo.working_build;
+            cpkt.driver_srl_high = TWS_CURRENT_FW_SRL;
+            cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH;
+            cpkt.driver_build_high = TWS_CURRENT_FW_BUILD;
+            cpkt.driver_srl_low = TWS_BASE_FW_SRL;
+            cpkt.driver_branch_low = TWS_BASE_FW_BRANCH;
+            cpkt.driver_build_low = TWS_BASE_FW_BUILD;
+            cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl;
+            cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch;
+            cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build;
+            ubuf->driver_pkt.status = 0;
+            int len = sizeof(struct tws_compatibility_packet);
+            if ( ubuf->driver_pkt.buffer_length < len )
+                len = ubuf->driver_pkt.buffer_length;
+            memcpy(ubuf->data_buf, &cpkt, len);
+
+            break;
+        default :
+            TWS_TRACE_DEBUG(sc, "not valid cmd", cmd,
+                           TWS_IOCTL_GET_COMPATIBILITY_INFO);
+            break;
+
+    }
+    lockmgr(&sc->gen_lock, LK_RELEASE);
+    return(SUCCESS);
+
+}
+
+void
+tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq,
+struct tws_event_packet *aen)
+{
+
+    struct tws_event_packet *q = (struct tws_event_packet *)cq->q;
+    volatile u_int16_t head, tail;
+    u_int8_t retr;
+    KKASSERT(lockstatus(&sc->gen_lock, curthread) != 0);
+
+    head = cq->head;
+    tail = cq->tail;
+    retr = q[tail].retrieved;
+
+    memcpy(&q[tail], aen, sizeof(struct tws_event_packet));
+    tail = (tail+1) % cq->depth;
+
+    if ( head == tail ) { /* q is full */
+        if ( retr != TWS_AEN_RETRIEVED )
+            cq->overflow = 1;
+        cq->head = (head+1) % cq->depth;
+    }
+    cq->tail = tail;
+
+}
diff --git a/sys/dev/raid/tws/tws_user.h b/sys/dev/raid/tws/tws_user.h
new file mode 100644 (file)
index 0000000..cefba1d
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2010, LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah
+ * Support: freebsdraid@lsi.com
+ *
+ * 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. Neither the name of the <ORGANIZATION> nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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: src/sys/dev/tws/tws_user.h,v 1.3 2007/05/09 04:16:32 mrangana Exp $
+ */
+
+#define TWS_AEN_NOT_RETRIEVED        0x1
+#define TWS_AEN_RETRIEVED            0x2
+
+#define TWS_AEN_NO_EVENTS            0x1003  /* No more events */
+#define TWS_AEN_OVERFLOW             0x1004  /* AEN overflow occurred */
+
+#define TWS_IOCTL_LOCK_NOT_HELD      0x1001   /* Not locked */
+#define TWS_IOCTL_LOCK_ALREADY_HELD  0x1002   /* Already locked */
+
+#define TWS_IOCTL_LOCK_HELD          0x1
+#define TWS_IOCTL_LOCK_FREE          0x0
+
+#pragma pack(1)
+
+/* Structure used to handle GET/RELEASE LOCK ioctls. */
+struct tws_lock_packet {
+    u_int32_t       timeout_msec;
+    u_int32_t       time_remaining_msec;
+    u_int32_t       force_flag;
+};
+
+/* Structure used to handle GET COMPATIBILITY INFO ioctl. */
+struct tws_compatibility_packet {
+    u_int8_t    driver_version[32];/* driver version */
+    u_int16_t   working_srl;    /* driver & firmware negotiated srl */
+    u_int16_t   working_branch; /* branch # of the firmware that the
+                                    driver is compatible with */
+    u_int16_t   working_build;  /* build # of the firmware that the
+                                        driver is compatible with */
+    u_int16_t   driver_srl_high;/* highest driver supported srl */
+    u_int16_t   driver_branch_high;/* highest driver supported branch */
+    u_int16_t   driver_build_high;/* highest driver supported build */
+    u_int16_t   driver_srl_low;/* lowest driver supported srl */
+    u_int16_t   driver_branch_low;/* lowest driver supported branch */
+    u_int16_t   driver_build_low;/* lowest driver supported build */
+    u_int16_t   fw_on_ctlr_srl; /* srl of running firmware */
+    u_int16_t   fw_on_ctlr_branch;/* branch # of running firmware */
+    u_int16_t   fw_on_ctlr_build;/* build # of running firmware */
+};
+
+
+/* Driver understandable part of the ioctl packet built by the API. */
+struct tws_driver_packet {
+    u_int32_t       control_code;
+    u_int32_t       status;
+    u_int32_t       unique_id;
+    u_int32_t       sequence_id;
+    u_int32_t       os_status;
+    u_int32_t       buffer_length;
+};
+
+/* ioctl packet built by the API. */
+struct tws_ioctl_packet {
+    struct tws_driver_packet      driver_pkt;
+    char                          padding[488];
+    struct tws_command_packet     cmd_pkt;
+    char                          data_buf[1];
+};
+
+#pragma pack()
+
+
+#pragma pack(1)
+/*
+ * We need the structure below to ensure that the first byte of
+ * data_buf is not overwritten by the kernel, after we return
+ * from the ioctl call.  Note that cmd_pkt has been reduced
+ * to an array of 1024 bytes even though it's actually 2048 bytes
+ * in size.  This is because, we don't expect requests from user
+ * land requiring 2048 (273 sg elements) byte cmd pkts.
+ */
+struct tws_ioctl_no_data_buf {
+    struct tws_driver_packet     driver_pkt;
+    void                         *pdata; /* points to data_buf */
+    char                         padding[488 - sizeof(void *)];
+    struct tws_command_packet    cmd_pkt;
+};
+
+#pragma pack()
+
+
+#include <sys/ioccom.h>
+
+#pragma pack(1)
+
+struct tws_ioctl_with_payload {
+    struct tws_driver_packet     driver_pkt;
+    char                         padding[488];
+    struct tws_command_packet    cmd_pkt;
+    union {
+        struct tws_event_packet               event_pkt;
+        struct tws_lock_packet                lock_pkt;
+        struct tws_compatibility_packet       compat_pkt;
+        char                                  data_buf[1];
+    } payload;
+};
+
+#pragma pack()
+
+/* ioctl cmds */
+
+#define TWS_IOCTL_SCAN_BUS                            \
+        _IO('T', 200)
+#define TWS_IOCTL_FIRMWARE_PASS_THROUGH               \
+        _IOWR('T', 202, struct tws_ioctl_no_data_buf)
+#define TWS_IOCTL_GET_FIRST_EVENT                     \
+        _IOWR('T', 203, struct tws_ioctl_with_payload)
+#define TWS_IOCTL_GET_LAST_EVENT                      \
+        _IOWR('T', 204, struct tws_ioctl_with_payload)
+#define TWS_IOCTL_GET_NEXT_EVENT                      \
+        _IOWR('T', 205, struct tws_ioctl_with_payload)
+#define TWS_IOCTL_GET_PREVIOUS_EVENT                  \
+        _IOWR('T', 206, struct tws_ioctl_with_payload)
+#define TWS_IOCTL_GET_LOCK                            \
+        _IOWR('T', 207, struct tws_ioctl_with_payload)
+#define TWS_IOCTL_RELEASE_LOCK                        \
+        _IOWR('T', 208, struct tws_ioctl_with_payload)
+#define TWS_IOCTL_GET_COMPATIBILITY_INFO              \
+        _IOWR('T', 209, struct tws_ioctl_with_payload)