summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordelphij <delphij@FreeBSD.org>2011-10-04 21:40:25 +0000
committerdelphij <delphij@FreeBSD.org>2011-10-04 21:40:25 +0000
commit9da6f154d20f9979b10969ef3d1775c66118abf1 (patch)
tree8bf58a4bedd5b6037fb04497b9166be360ce5c7b
parent66a96c11c836a64c8381f3046d1b055c9bd67456 (diff)
downloadFreeBSD-src-9da6f154d20f9979b10969ef3d1775c66118abf1.zip
FreeBSD-src-9da6f154d20f9979b10969ef3d1775c66118abf1.tar.gz
Add the 9750 SATA+SAS 6Gb/s RAID controller card driver, tws(4). Many
thanks for their contiued support to FreeBSD. This is version 10.80.00.003 from codeset 10.2.1 [1] Obtained from: LSI http://kb.lsi.com/Download16574.aspx [1]
-rw-r--r--share/man/man4/Makefile1
-rw-r--r--share/man/man4/tws.4118
-rw-r--r--sys/amd64/conf/GENERIC1
-rw-r--r--sys/conf/files5
-rw-r--r--sys/dev/tws/tws.c905
-rw-r--r--sys/dev/tws/tws.h265
-rw-r--r--sys/dev/tws/tws_cam.c1342
-rw-r--r--sys/dev/tws/tws_hdm.c535
-rw-r--r--sys/dev/tws/tws_hdm.h420
-rw-r--r--sys/dev/tws/tws_services.c401
-rw-r--r--sys/dev/tws/tws_services.h141
-rw-r--r--sys/dev/tws/tws_user.c379
-rw-r--r--sys/dev/tws/tws_user.h156
-rw-r--r--sys/i386/conf/GENERIC1
-rw-r--r--sys/modules/Makefile1
-rw-r--r--sys/modules/tws/Makefile10
16 files changed, 4681 insertions, 0 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 091eb28..b38a01c 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -447,6 +447,7 @@ MAN= aac.4 \
tun.4 \
twa.4 \
twe.4 \
+ tws.4 \
tx.4 \
txp.4 \
u3g.4 \
diff --git a/share/man/man4/tws.4 b/share/man/man4/tws.4
new file mode 100644
index 0000000..9ce154e
--- /dev/null
+++ b/share/man/man4/tws.4
@@ -0,0 +1,118 @@
+.\"
+.\"Copyright (c) 2010, 2011 iXsystems, Inc.
+.\"All rights reserved.
+.\" written by: Xin LI <delphij@FreeBSD.org>
+.\"
+.\"Redistribution and use in source and binary forms, with or without
+.\"modification, are permitted provided that the following conditions
+.\"are met:
+.\"1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\"THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\"ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\"LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\"OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\"SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 4, 2011
+.Dt TWS 4
+.Os
+.Sh NAME
+.Nm tws
+.Nd 3ware 9750 SATA+SAS 6Gb/s RAID controller card 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 SATA+SAS 6Gb/s RAID controller cards.
+.Pp
+These controllers feature the LSISAS2108 6Gb/s SAS RAID-on-Chip (ROC)
+and are available in 4- and 8-port configurations, supports RAID levels
+0, 1, 5, 6, 10, 50 and single disk, with 96 SATA and/or SAS hard drives and SSDs.
+.Pp
+For further hardware information, see
+.Pa http://www.lsi.com/.
+.Sh HARDWARE
+The
+.Nm
+driver supports the following SATA/SAS RAID controller:
+.Pp
+.Bl -bullet -compact
+.It
+LSI's 3ware SAS 9750 series
+.El
+.Sh LOADER TUNABLES
+Tunables can be set at the
+.Xr loader 8
+prompt before booting the kernel or stored in
+.Xr loader.conf 5 .
+.Bl -tag -width "hw.tws.use_32bit_sgls"
+.It Va hw.tws.cam_depth
+The maximium queued CAM SIM requests for one controller.
+The default value is 256.
+.It Va hw.tws.enable_msi
+This tunable enables MSI support on the controller if set to a non-zero value.
+The default value is 0.
+.It Va hw.tws.queue_depth
+The maximium queued requests for one controller.
+.It Va hw.tws.use_32bit_sgls
+Limit the driver to use only 32-bit SG elements regardless whether the operating
+system is running in 64-bit mode.
+The default value is 0.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/tws?" -compact
+.It Pa /dev/da?
+array/logical disk interface
+.It Pa /dev/tws?
+management interface
+.El
+.Sh DIAGNOSTICS
+Whenever the driver encounters a command failure, it prints out an error code in
+the format:
+.Qq Li "ERROR: (<error source>: <error code>):" ,
+followed by a text description of the error.
+There are other error messages and warnings that the
+driver prints out, depending on the kinds of errors that it encounters.
+If the driver is compiled with
+.Dv TWS_DEBUG
+defined, it prints out a whole bunch of debug
+messages.
+.Sh SEE ALSO
+.Xr da 4 ,
+.Xr scsi 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Manjunath Ranganathaiah
+for LSI and this manual page was written by
+.An Xin LI Aq delphij@FreeBSD.org
+for iXsystems, Inc.
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index db87d0b..325f034 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -151,6 +151,7 @@ device mlx # Mylex DAC960 family
#XXX pointer/int warnings
#device pst # Promise Supertrak SX6000
device twe # 3ware ATA RAID
+device tws # LSI 3ware 9750 SATA+SAS 6Gb/s RAID controller
# atkbdc0 controls both the keyboard and the PS/2 mouse
device atkbdc # AT keyboard controller
diff --git a/sys/conf/files b/sys/conf/files
index 5c5d92d..0633223 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1833,6 +1833,11 @@ dev/twa/tw_osl_freebsd.c optional twa \
compile-with "${NORMAL_C} -I$S/dev/twa"
dev/twe/twe.c optional twe
dev/twe/twe_freebsd.c optional twe
+dev/tws/tws.c optional tws
+dev/tws/tws_cam.c optional tws
+dev/tws/tws_hdm.c optional tws
+dev/tws/tws_services.c optional tws
+dev/tws/tws_user.c optional tws
dev/tx/if_tx.c optional tx
dev/txp/if_txp.c optional txp
dev/uart/uart_bus_acpi.c optional uart acpi
diff --git a/sys/dev/tws/tws.c b/sys/dev/tws/tws.c
new file mode 100644
index 0000000..57a463e
--- /dev/null
+++ b/sys/dev/tws/tws.c
@@ -0,0 +1,905 @@
+/*
+ * 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$
+*/
+
+
+#include <dev/tws/tws.h>
+#include <dev/tws/tws_services.h>
+#include <dev/tws/tws_hdm.h>
+
+#include <cam/cam.h>
+#include <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);
+int tws_setup_intr(struct tws_softc *sc, int irqs);
+int tws_teardown_intr(struct tws_softc *sc);
+
+
+/* Character device entry points */
+
+static struct cdevsw tws_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = tws_open,
+ .d_close = tws_close,
+ .d_read = tws_read,
+ .d_write = tws_write,
+ .d_ioctl = tws_ioctl,
+ .d_name = "tws",
+};
+
+/*
+ * 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 cdev *dev, int oflags, int devtype, d_thread_t *td)
+{
+ struct tws_softc *sc = dev->si_drv1;
+
+ if ( sc )
+ TWS_TRACE_DEBUG(sc, "entry", dev, oflags);
+ return (0);
+}
+
+int
+tws_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td)
+{
+ struct tws_softc *sc = dev->si_drv1;
+
+ if ( sc )
+ TWS_TRACE_DEBUG(sc, "entry", dev, fflag);
+ return (0);
+}
+
+int
+tws_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct tws_softc *sc = dev->si_drv1;
+
+ if ( sc )
+ TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
+ return (0);
+}
+
+int
+tws_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ 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) {
+ printf("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 */
+ mtx_init( &sc->q_lock, "tws_q_lock", NULL, MTX_DEF);
+ mtx_init( &sc->sim_lock, "tws_sim_lock", NULL, MTX_DEF);
+ mtx_init( &sc->gen_lock, "tws_gen_lock", NULL, MTX_DEF);
+ mtx_init( &sc->io_lock, "tws_io_lock", NULL, MTX_DEF);
+
+ if ( tws_init_trace_q(sc) == FAILURE )
+ printf("trace init failure\n");
+ /* send init event */
+ mtx_lock(&sc->gen_lock);
+ tws_send_event(sc, TWS_INIT_START);
+ mtx_unlock(&sc->gen_lock);
+
+
+#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;
+ }
+
+ /*
+ * 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_cdevsw, 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 */
+ mtx_lock(&sc->gen_lock);
+ tws_send_event(sc, TWS_INIT_COMPLETE);
+ mtx_unlock(&sc->gen_lock);
+
+ TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id);
+ return(0);
+
+attach_fail_4:
+ tws_teardown_intr(sc);
+ destroy_dev(sc->tws_cdev);
+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:
+ mtx_destroy(&sc->q_lock);
+ mtx_destroy(&sc->sim_lock);
+ mtx_destroy(&sc->gen_lock);
+ mtx_destroy(&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 i;
+ u_int32_t reg;
+
+ TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+ mtx_lock(&sc->gen_lock);
+ tws_send_event(sc, TWS_UNINIT_START);
+ mtx_unlock(&sc->gen_lock);
+
+ /* 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. */
+ tws_teardown_intr(sc);
+
+ /* 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);
+ }
+
+ free(sc->reqs, M_TWS);
+ free(sc->sense_bufs, M_TWS);
+ free(sc->scan_ccb, M_TWS);
+ free(sc->aen_q.q, M_TWS);
+ free(sc->trace_q.q, M_TWS);
+ mtx_destroy(&sc->q_lock);
+ mtx_destroy(&sc->sim_lock);
+ mtx_destroy(&sc->gen_lock);
+ mtx_destroy(&sc->io_lock);
+ destroy_dev(sc->tws_cdev);
+ sysctl_ctx_free(&sc->tws_clist);
+ return (0);
+}
+
+int
+tws_setup_intr(struct tws_softc *sc, int irqs)
+{
+ int i, error;
+
+ for(i=0;i<irqs;i++) {
+ if (!(sc->intr_handle[i])) {
+ if ((error = bus_setup_intr(sc->tws_dev, sc->irq_res[i],
+ INTR_TYPE_CAM | INTR_MPSAFE,
+#if (__FreeBSD_version >= 700000)
+ NULL,
+#endif
+ tws_intr, sc, &sc->intr_handle[i]))) {
+ tws_log(sc, SETUP_INTR_RES);
+ return(FAILURE);
+ }
+ }
+ }
+ return(SUCCESS);
+
+}
+
+
+int
+tws_teardown_intr(struct tws_softc *sc)
+{
+ int i, error;
+
+ for(i=0;i<sc->irqs;i++) {
+ if (sc->intr_handle[i]) {
+ error = bus_teardown_intr(sc->tws_dev,
+ sc->irq_res[i], sc->intr_handle[i]);
+ sc->intr_handle[i] = NULL;
+ }
+ }
+ 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 */
+ NULL, NULL, /* lockfunc, lockfuncarg */
+ &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 */
+ NULL, NULL, /* lockfunc, lockfuncarg */
+ &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);
+
+ /*
+ * 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_ALLOCNOW, /* flags */
+ busdma_lock_mutex, /* lockfunc */
+ &sc->io_lock, /* lockfuncarg */
+ &sc->data_tag /* tag */)) {
+ TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit);
+ return(ENOMEM);
+ }
+
+ sc->reqs = malloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS,
+ M_WAITOK | M_ZERO);
+ if ( sc->reqs == NULL ) {
+ TWS_TRACE_DEBUG(sc, "malloc failed", 0, sc->is64bit);
+ return(ENOMEM);
+ }
+ sc->sense_bufs = malloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS,
+ M_WAITOK | M_ZERO);
+ if ( sc->sense_bufs == NULL ) {
+ TWS_TRACE_DEBUG(sc, "sense malloc failed", 0, sc->is64bit);
+ return(ENOMEM);
+ }
+ sc->scan_ccb = malloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO);
+ if ( sc->scan_ccb == NULL ) {
+ TWS_TRACE_DEBUG(sc, "ccb malloc failed", 0, sc->is64bit);
+ return(ENOMEM);
+ }
+
+ 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 = malloc(sizeof(struct tws_event_packet)*sc->aen_q.depth,
+ M_TWS, M_WAITOK | M_ZERO);
+ if ( ! sc->aen_q.q )
+ return(FAILURE);
+ 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 = malloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth,
+ M_TWS, M_WAITOK | M_ZERO);
+ if ( ! sc->trace_q.q )
+ return(FAILURE);
+ 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);
+ mtx_lock(&sc->q_lock);
+ 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->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);
+ }
+ mtx_unlock(&sc->q_lock);
+ return(SUCCESS);
+}
+
+static void
+tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
+ int nseg, int error)
+{
+
+ /* printf("command load done \n"); */
+
+ *((bus_addr_t *)arg) = segs[0].ds_addr;
+}
+
+void
+tws_send_event(struct tws_softc *sc, u_int8_t event)
+{
+ mtx_assert(&sc->gen_lock, MA_OWNED);
+ TWS_TRACE_DEBUG(sc, "received event ", 0, event);
+ switch (event) {
+
+ case TWS_INIT_START:
+ sc->tws_state = TWS_INIT;
+ break;
+
+ case TWS_INIT_COMPLETE:
+ if (sc->tws_state != TWS_INIT) {
+ device_printf(sc->tws_dev, "invalid state transition %d => TWS_ONLINE\n", sc->tws_state);
+ } else {
+ sc->tws_state = TWS_ONLINE;
+ }
+ break;
+
+ case TWS_RESET_START:
+ /* We can transition to reset state from any state except reset*/
+ if (sc->tws_state != TWS_RESET) {
+ sc->tws_prev_state = sc->tws_state;
+ sc->tws_state = TWS_RESET;
+ }
+ break;
+
+ case TWS_RESET_COMPLETE:
+ if (sc->tws_state != TWS_RESET) {
+ device_printf(sc->tws_dev, "invalid state transition %d => %d (previous state)\n", sc->tws_state, sc->tws_prev_state);
+ } else {
+ sc->tws_state = sc->tws_prev_state;
+ }
+ break;
+
+ case TWS_SCAN_FAILURE:
+ if (sc->tws_state != TWS_ONLINE) {
+ device_printf(sc->tws_dev, "invalid state transition %d => TWS_OFFLINE\n", sc->tws_state);
+ } else {
+ sc->tws_state = TWS_OFFLINE;
+ }
+ break;
+
+ case TWS_UNINIT_START:
+ if ((sc->tws_state != TWS_ONLINE) && (sc->tws_state != TWS_OFFLINE)) {
+ device_printf(sc->tws_dev, "invalid state transition %d => TWS_UNINIT\n", sc->tws_state);
+ } else {
+ 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 mtx *my_mutex = ((type == TWS_REQ_TYPE_SCSI_IO) ? &sc->q_lock : &sc->gen_lock);
+ struct tws_request *r = NULL;
+
+ mtx_lock(my_mutex);
+
+ if (type == TWS_REQ_TYPE_SCSI_IO) {
+ r = tws_q_remove_head(sc, TWS_FREE_Q);
+ } else {
+ if ( sc->reqs[type].state == TWS_REQ_STATE_FREE ) {
+ r = &sc->reqs[type];
+ }
+ }
+
+ if ( r ) {
+ bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache));
+ r->data = NULL;
+ r->length = 0;
+ r->type = type;
+ r->flags = TWS_DIR_UNKNOWN;
+ r->error_code = TWS_REQ_RET_INVALID;
+ r->cb = NULL;
+ r->ccb_ptr = NULL;
+ r->thandle.callout = NULL;
+ r->next = r->prev = NULL;
+
+ r->state = ((type == TWS_REQ_TYPE_SCSI_IO) ? TWS_REQ_STATE_TRAN : TWS_REQ_STATE_BUSY);
+ }
+
+ mtx_unlock(my_mutex);
+
+ return(r);
+}
+
+void
+tws_release_request(struct tws_request *req)
+{
+
+ struct tws_softc *sc = req->sc;
+
+ TWS_TRACE_DEBUG(sc, "entry", sc, 0);
+ mtx_lock(&sc->q_lock);
+ tws_q_insert_tail(sc, req, TWS_FREE_Q);
+ mtx_unlock(&sc->q_lock);
+}
+
+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);
+TUNABLE_INT("hw.tws.enable_msi", &tws_enable_msi);
diff --git a/sys/dev/tws/tws.h b/sys/dev/tws/tws.h
new file mode 100644
index 0000000..cf9f727
--- /dev/null
+++ b/sys/dev/tws/tws.h
@@ -0,0 +1,265 @@
+/*
+ * 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$
+ */
+
+#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 <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h> /* For pci_get macros! */
+#include <dev/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.003"
+#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_IOCTL_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_FREE 0
+#define TWS_REQ_STATE_BUSY 1
+#define TWS_REQ_STATE_TRAN 2
+#define TWS_REQ_STATE_COMPLETE 3
+
+/* Request types */
+#define TWS_REQ_TYPE_INTERNAL_CMD 0x0
+#define TWS_REQ_TYPE_AEN_FETCH 0x1
+#define TWS_REQ_TYPE_PASSTHRU 0x2
+#define TWS_REQ_TYPE_GETSET_PARAM 0x3
+#define TWS_REQ_TYPE_SCSI_IO 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;
+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 mtx q_lock; /* queue lock */
+ struct mtx sim_lock; /* sim lock */
+ struct mtx gen_lock; /* general driver lock */
+ struct mtx 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 */
+};
diff --git a/sys/dev/tws/tws_cam.c b/sys/dev/tws/tws_cam.c
new file mode 100644
index 0000000..df78b99
--- /dev/null
+++ b/sys/dev/tws/tws_cam.c
@@ -0,0 +1,1342 @@
+/*
+ * Copyright (c) 2010 LSI Corp.
+ * All rights reserved.
+ * Author : Manjunath Ranganathaiah <manjunath.ranganathaiah@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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <dev/tws/tws.h>
+#include <dev/tws/tws_services.h>
+#include <dev/tws/tws_hdm.h>
+#include <dev/tws/tws_user.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_periph.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <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_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, struct tws_request *req);
+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);
+
+
+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);
+extern int tws_setup_intr(struct tws_softc *sc, int irqs);
+extern int tws_teardown_intr(struct tws_softc *sc);
+
+
+
+int
+tws_cam_attach(struct tws_softc *sc)
+{
+ struct cam_devq *devq;
+
+ 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),
+#if (__FreeBSD_version >= 700000)
+ &sc->sim_lock,
+#endif
+ tws_cam_depth, 1, devq);
+ /* 1, 1, devq); */
+ if (sc->sim == NULL) {
+ cam_simq_free(devq);
+ tws_log(sc, CAM_SIM_ALLOC);
+ }
+ /* Register the bus. */
+ mtx_lock(&sc->sim_lock);
+ if (xpt_bus_register(sc->sim,
+#if (__FreeBSD_version >= 700000)
+ sc->tws_dev,
+#endif
+ 0) != CAM_SUCCESS) {
+ cam_sim_free(sc->sim, TRUE); /* passing true will free the devq */
+ sc->sim = NULL; /* so cam_detach will not try to free it */
+ mtx_unlock(&sc->sim_lock);
+ 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));
+ /* Passing TRUE to cam_sim_free will free the devq as well. */
+ cam_sim_free(sc->sim, TRUE);
+ tws_log(sc, TWS_XPT_CREATE_PATH);
+ mtx_unlock(&sc->sim_lock);
+ return(ENXIO);
+ }
+ mtx_unlock(&sc->sim_lock);
+
+ return(0);
+}
+
+void
+tws_cam_detach(struct tws_softc *sc)
+{
+ TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+ mtx_lock(&sc->sim_lock);
+ if (sc->path)
+ xpt_free_path(sc->path);
+ if (sc->sim) {
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ cam_sim_free(sc->sim, TRUE);
+ }
+ mtx_unlock(&sc->sim_lock);
+}
+
+int
+tws_bus_scan(struct tws_softc *sc)
+{
+ union ccb *ccb;
+
+ TWS_TRACE_DEBUG(sc, "entry", sc, 0);
+ if (!(sc->sim))
+ return(ENXIO);
+ mtx_assert(&sc->sim_lock, MA_OWNED);
+ if ((ccb = xpt_alloc_ccb()) == NULL)
+ return(ENOMEM);
+
+ if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(sc->sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_free_ccb(ccb);
+ return(EIO);
+ }
+ xpt_rescan(ccb);
+
+ return(0);
+}
+
+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);
+
+#if (__FreeBSD_version >= 700000 )
+ 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;
+#else
+ ccb->cts.valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID);
+ ccb->cts.flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB);
+#endif
+ 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 = 6000000;
+ 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);
+#if (__FreeBSD_version >= 700000 )
+ ccb->cpi.transport = XPORT_SPI;
+ ccb->cpi.transport_version = 2;
+ ccb->cpi.protocol = PROTO_SCSI;
+ ccb->cpi.protocol_version = SCSI_REV_2;
+ ccb->cpi.maxio = TWS_MAX_IO_SIZE;
+#endif
+ 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;
+
+ mtx_lock(&sc->q_lock);
+ tws_q_remove_request(sc, req, TWS_BUSY_Q);
+ mtx_unlock(&sc->q_lock);
+
+ untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch);
+ tws_unmap_request(req->sc, req);
+
+
+ req->ccb_ptr->ccb_h.status = CAM_REQ_CMP;
+ mtx_lock(&sc->sim_lock);
+ xpt_done(req->ccb_ptr);
+ mtx_unlock(&sc->sim_lock);
+
+ mtx_lock(&sc->q_lock);
+ tws_q_insert_tail(sc, req, TWS_FREE_Q);
+ mtx_unlock(&sc->q_lock);
+}
+
+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);
+
+ untimeout(tws_timeout, req, req->thandle);
+ tws_unmap_request(sc, req);
+
+ free(req->data, M_TWS);
+
+ req->state = TWS_REQ_STATE_FREE;
+}
+
+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);
+
+ untimeout(tws_timeout, req, 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);
+
+ mtx_lock(&sc->gen_lock);
+ tws_circular_aenq_insert(sc, &sc->aen_q, &event);
+ sc->seq_id++;
+ mtx_unlock(&sc->gen_lock);
+ break;
+
+ }
+
+ free(req->data, M_TWS);
+
+ req->state = TWS_REQ_STATE_FREE;
+
+ 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;
+
+ untimeout(tws_timeout, req, 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_RET_SUBMIT_SUCCESS )
+ TWS_TRACE_DEBUG(sc, "submit failure?", 0, req->error_code);
+ }
+
+ switch (req->type) {
+ case TWS_REQ_TYPE_PASSTHRU :
+ tws_passthru_err_complete(req, hdr);
+ break;
+ case TWS_REQ_TYPE_GETSET_PARAM :
+ tws_getset_param_complete(req);
+ break;
+ case TWS_REQ_TYPE_SCSI_IO :
+ tws_scsi_err_complete(req, hdr);
+ break;
+
+ }
+
+ mtx_lock(&sc->io_lock);
+ 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 ) {
+ device_printf(sc->tws_dev, "OBFL Overrun\n");
+ sc->obfl_q_overrun = true;
+ }
+ mtx_unlock(&sc->io_lock);
+}
+
+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;
+ mtx_lock(&sc->sim_lock);
+ xpt_done(ccb);
+ mtx_unlock(&sc->sim_lock);
+
+ untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch);
+ tws_unmap_request(req->sc, req);
+ mtx_lock(&sc->q_lock);
+ tws_q_remove_request(sc, req, TWS_BUSY_Q);
+ tws_q_insert_tail(sc, req, TWS_FREE_Q);
+ mtx_unlock(&sc->q_lock);
+}
+
+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;
+ union ccb *ccb;
+ TWS_TRACE_DEBUG(sc, "entry", 0, 0);
+
+ mtx_lock(&sc->q_lock);
+ req = tws_q_remove_tail(sc, TWS_BUSY_Q);
+ mtx_unlock(&sc->q_lock);
+ while ( req ) {
+ TWS_TRACE_DEBUG(sc, "moved to TWS_COMPLETE_Q", 0, req->request_id);
+ untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch);
+
+ req->error_code = TWS_REQ_RET_RESET;
+ ccb = (union ccb *)(req->ccb_ptr);
+
+ ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ ccb->ccb_h.status |= CAM_REQUEUE_REQ;
+ ccb->ccb_h.status |= CAM_SCSI_BUS_RESET;
+
+ tws_unmap_request(req->sc, req);
+
+ mtx_lock(&sc->sim_lock);
+ xpt_done(req->ccb_ptr);
+ mtx_unlock(&sc->sim_lock);
+
+ mtx_lock(&sc->q_lock);
+ tws_q_insert_tail(sc, req, TWS_FREE_Q);
+ req = tws_q_remove_tail(sc, TWS_BUSY_Q);
+ mtx_unlock(&sc->q_lock);
+ }
+}
+
+
+static void
+tws_drain_reserved_reqs(struct tws_softc *sc)
+{
+ struct tws_request *r;
+
+ r = &sc->reqs[TWS_REQ_TYPE_AEN_FETCH];
+ if ( r->state != TWS_REQ_STATE_FREE ) {
+ TWS_TRACE_DEBUG(sc, "reset aen req", 0, 0);
+ untimeout(tws_timeout, r, r->thandle);
+ tws_unmap_request(sc, r);
+ free(r->data, M_TWS);
+ r->state = TWS_REQ_STATE_FREE;
+ r->error_code = TWS_REQ_RET_RESET;
+ }
+
+ r = &sc->reqs[TWS_REQ_TYPE_PASSTHRU];
+ if ( r->state == TWS_REQ_STATE_BUSY ) {
+ TWS_TRACE_DEBUG(sc, "reset passthru req", 0, 0);
+ r->error_code = TWS_REQ_RET_RESET;
+ }
+
+ r = &sc->reqs[TWS_REQ_TYPE_GETSET_PARAM];
+ if ( r->state != TWS_REQ_STATE_FREE ) {
+ TWS_TRACE_DEBUG(sc, "reset setparam req", 0, 0);
+ untimeout(tws_timeout, r, r->thandle);
+ tws_unmap_request(sc, r);
+ free(r->data, M_TWS);
+ r->state = TWS_REQ_STATE_FREE;
+ r->error_code = TWS_REQ_RET_RESET;
+ }
+}
+
+static void
+tws_drain_response_queue(struct tws_softc *sc)
+{
+ u_int16_t req_id;
+ u_int64_t mfa;
+ while ( tws_get_response(sc, &req_id, &mfa) );
+}
+
+
+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;
+
+ mtx_assert(&sc->sim_lock, MA_OWNED);
+ 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_INVALID;
+ 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_REQ_TYPE_SCSI_IO);
+ if ( !req ) {
+ TWS_TRACE_DEBUG(sc, "no reqs", ccb_h->target_id, ccb_h->target_lun);
+ 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;
+ if(ccb_h->flags & CAM_DIR_OUT)
+ req->flags |= TWS_DIR_OUT;
+ } else {
+ req->flags = TWS_DIR_NONE; /* no data */
+ }
+
+ req->type = TWS_REQ_TYPE_SCSI_IO;
+ 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_INVALID;
+ 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_INVALID;
+ 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++;
+ ccb_h->timeout_ch = timeout(tws_timeout, req, (ccb_h->timeout * hz)/1000);
+ 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_REQ_TYPE_AEN_FETCH);
+
+ if ( req == NULL )
+ return(ENOMEM);
+
+ 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 = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT);
+ if ( req->data == NULL )
+ return(ENOMEM);
+ bzero(req->data, TWS_SECTOR_SIZE);
+ req->flags = TWS_DIR_IN;
+
+ req->thandle = timeout(tws_timeout, req, (TWS_IO_TIMEOUT * hz));
+ 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_REQ_TYPE_GETSET_PARAM);
+ if ( req == NULL ) {
+ TWS_TRACE_DEBUG(sc, "null req", 0, 0);
+ return(ENOMEM);
+ }
+
+ req->length = TWS_SECTOR_SIZE;
+ req->data = malloc(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);
+
+ req->thandle = timeout(tws_timeout, req, (TWS_IOCTL_TIMEOUT * hz));
+ 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_REQ_TYPE_GETSET_PARAM);
+ if ( req == NULL ) {
+ TWS_TRACE_DEBUG(sc, "null req", 0, 0);
+ return(FAILURE);
+ }
+
+ req->length = TWS_SECTOR_SIZE;
+ req->data = malloc(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;
+
+ error = tws_map_request(sc, req);
+ if (!error) {
+ reqid = tws_poll4_response(sc, &mfa);
+ tws_unmap_request(sc, req);
+
+ if ( reqid == TWS_REQ_TYPE_GETSET_PARAM ) {
+ memcpy(data, param->data, param_size);
+ } else {
+ error = FAILURE;
+ }
+ }
+
+ free(req->data, M_TWS);
+ req->state = TWS_REQ_STATE_FREE;
+ 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);
+ mtx_lock(&sc->io_lock);
+ bus_dmamap_unload(sc->data_tag, req->dma_map);
+ mtx_unlock(&sc->io_lock);
+ }
+}
+
+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) {
+ int my_flags = ((req->type == TWS_REQ_TYPE_SCSI_IO) ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT);
+
+ /*
+ * Map the data buffer into bus space and build the SG list.
+ */
+ mtx_lock(&sc->io_lock);
+ error = bus_dmamap_load(sc->data_tag, req->dma_map,
+ req->data, req->length,
+ tws_dmamap_data_load_cbfn, req,
+ my_flags);
+ mtx_unlock(&sc->io_lock);
+
+ if (error == EINPROGRESS) {
+ TWS_TRACE(sc, "in progress", 0, error);
+ tws_freeze_simq(sc, req);
+ }
+ } else { /* no data involved */
+ error = tws_submit_command(sc, req);
+ }
+ 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);
+ req->error_code = error;
+ req->ccb_ptr->ccb_h.status = CAM_REQ_TOO_BIG;
+ return;
+ }
+
+ 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_REQ_TYPE_PASSTHRU &&
+ GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) !=
+ TWS_FW_CMD_EXECUTE_SCSI) ||
+ req->type == TWS_REQ_TYPE_GETSET_PARAM) {
+ 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;
+
+ if (!(sc)) {
+ device_printf(sc->tws_dev, "null softc!!!\n");
+ return;
+ }
+
+ if ( tws_get_state(sc) == TWS_RESET ) {
+ return;
+ }
+
+ if ( tws_get_state(sc) != TWS_ONLINE ) {
+ return;
+ }
+
+ 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 ( req->error_code == TWS_REQ_RET_RESET ) {
+ return;
+ }
+
+ mtx_lock(&sc->gen_lock);
+ if ( req->error_code == TWS_REQ_RET_RESET ) {
+ mtx_unlock(&sc->gen_lock);
+ return;
+ }
+
+ if ( tws_get_state(sc) == TWS_RESET ) {
+ mtx_unlock(&sc->gen_lock);
+ return;
+ }
+
+ tws_teardown_intr(sc);
+ xpt_freeze_simq(sc->sim, 1);
+
+ tws_send_event(sc, TWS_RESET_START);
+
+ if (req->type == TWS_REQ_TYPE_SCSI_IO) {
+ device_printf(sc->tws_dev, "I/O Request timed out... Resetting controller\n");
+ } else if (req->type == TWS_REQ_TYPE_PASSTHRU) {
+ device_printf(sc->tws_dev, "IOCTL Request timed out... Resetting controller\n");
+ } else {
+ device_printf(sc->tws_dev, "Internal Request timed out... Resetting controller\n");
+ }
+
+ tws_assert_soft_reset(sc);
+ tws_turn_off_interrupts(sc);
+ tws_reset_cb( (void*) sc );
+ tws_reinit( (void*) sc );
+
+// device_printf(sc->tws_dev, "Controller Reset complete!\n");
+ tws_send_event(sc, TWS_RESET_COMPLETE);
+ mtx_unlock(&sc->gen_lock);
+
+ xpt_release_simq(sc->sim, 1);
+ tws_setup_intr(sc, sc->irqs);
+}
+
+void
+tws_reset(void *arg)
+{
+ struct tws_softc *sc = (struct tws_softc *)arg;
+
+ mtx_lock(&sc->gen_lock);
+ if ( tws_get_state(sc) == TWS_RESET ) {
+ mtx_unlock(&sc->gen_lock);
+ return;
+ }
+
+ tws_teardown_intr(sc);
+ xpt_freeze_simq(sc->sim, 1);
+
+ tws_send_event(sc, TWS_RESET_START);
+
+ device_printf(sc->tws_dev, "Resetting controller\n");
+
+ tws_assert_soft_reset(sc);
+ tws_turn_off_interrupts(sc);
+ tws_reset_cb( (void*) sc );
+ tws_reinit( (void*) sc );
+
+// device_printf(sc->tws_dev, "Controller Reset complete!\n");
+ tws_send_event(sc, TWS_RESET_COMPLETE);
+ mtx_unlock(&sc->gen_lock);
+
+ xpt_release_simq(sc->sim, 1);
+ tws_setup_intr(sc, sc->irqs);
+}
+
+static void
+tws_reset_cb(void *arg)
+{
+ struct tws_softc *sc = (struct tws_softc *)arg;
+ time_t endt;
+ int found = 0;
+ u_int32_t reg;
+
+ if ( tws_get_state(sc) != TWS_RESET ) {
+ return;
+ }
+
+// device_printf(sc->tws_dev, "Draining Busy Queue\n");
+ tws_drain_busy_queue(sc);
+// device_printf(sc->tws_dev, "Draining Reserved Reqs\n");
+ tws_drain_reserved_reqs(sc);
+// device_printf(sc->tws_dev, "Draining Response Queue\n");
+ tws_drain_response_queue(sc);
+
+// device_printf(sc->tws_dev, "Looking for controller ready flag...\n");
+ endt = TWS_LOCAL_TIME + TWS_POLL_TIMEOUT;
+ while ((TWS_LOCAL_TIME <= endt) && (!found)) {
+ reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4);
+ if ( reg & TWS_BIT13 ) {
+ found = 1;
+// device_printf(sc->tws_dev, " ... Got it!\n");
+ }
+ }
+ if ( !found )
+ device_printf(sc->tws_dev, " ... Controller ready flag NOT found!\n");
+}
+
+static void
+tws_reinit(void *arg)
+{
+ struct tws_softc *sc = (struct tws_softc *)arg;
+ int timeout_val=0;
+ int try=2;
+ int done=0;
+
+
+// device_printf(sc->tws_dev, "Waiting for Controller Ready\n");
+ while ( !done && try ) {
+ if ( tws_ctlr_ready(sc) ) {
+ done = 1;
+ break;
+ } else {
+ timeout_val += 5;
+ if ( timeout_val >= TWS_RESET_TIMEOUT ) {
+ timeout_val = 0;
+ if ( try )
+ tws_assert_soft_reset(sc);
+ try--;
+ }
+ mtx_sleep(sc, &sc->gen_lock, 0, "tws_reinit", 5*hz);
+ }
+ }
+
+ if (!done) {
+ device_printf(sc->tws_dev, "FAILED to get Controller Ready!\n");
+ return;
+ }
+
+ sc->obfl_q_overrun = false;
+// device_printf(sc->tws_dev, "Sending initConnect\n");
+ if ( tws_init_connect(sc, tws_queue_depth) ) {
+ TWS_TRACE_DEBUG(sc, "initConnect failed", 0, sc->is64bit);
+ }
+ tws_init_obfl_q(sc);
+
+ tws_turn_on_interrupts(sc);
+
+ if ( sc->chan ) {
+ sc->chan = 0;
+ wakeup_one((void *)&sc->chan);
+ }
+}
+
+
+static void
+tws_freeze_simq(struct tws_softc *sc, struct tws_request *req)
+{
+ /* Only for IO commands */
+ if (req->type == TWS_REQ_TYPE_SCSI_IO) {
+ union ccb *ccb = (union ccb *)(req->ccb_ptr);
+
+ xpt_freeze_simq(sc->sim, 1);
+ ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ ccb->ccb_h.status |= CAM_REQUEUE_REQ;
+ }
+}
+
+
+TUNABLE_INT("hw.tws.cam_depth", &tws_cam_depth);
diff --git a/sys/dev/tws/tws_hdm.c b/sys/dev/tws/tws_hdm.c
new file mode 100644
index 0000000..3f6bea0
--- /dev/null
+++ b/sys/dev/tws/tws_hdm.c
@@ -0,0 +1,535 @@
+/*
+ * 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$
+ */
+
+
+#include <dev/tws/tws.h>
+#include <dev/tws/tws_services.h>
+#include <dev/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 ) {
+ 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 ) {
+ device_printf(sc->tws_dev, "OBFL Overrun\n");
+ sc->obfl_q_overrun = true;
+ break;
+ }
+ 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);
+#if 0
+ req = tws_get_request(sc, TWS_REQ_TYPE_INTERNAL_CMD);
+#else // 0
+ req = &sc->reqs[TWS_REQ_TYPE_INTERNAL_CMD];
+ bzero(&req->cmd_pkt->cmd, sizeof(struct tws_command_apache));
+ req->data = NULL;
+ req->length = 0;
+ req->type = TWS_REQ_TYPE_INTERNAL_CMD;
+ req->flags = TWS_DIR_UNKNOWN;
+ req->error_code = TWS_REQ_RET_INVALID;
+ req->cb = NULL;
+ req->ccb_ptr = NULL;
+ req->thandle.callout = NULL;
+ req->next = req->prev = NULL;
+ req->state = TWS_REQ_STATE_BUSY;
+#endif // 0
+
+ if ( req == NULL ) {
+ TWS_TRACE_DEBUG(sc, "no requests", 0, 0);
+// device_printf(sc->tws_dev, "No requests for initConnect\n");
+ 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++;
+ req->state = TWS_REQ_STATE_FREE;
+ }
+ 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_REQ_TYPE_INTERNAL_CMD);
+
+ 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
+ */
+ mtx_lock(&sc->io_lock);
+
+ 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
+
+ mtx_unlock(&sc->io_lock);
+
+ 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_RET_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_REQ_TYPE_SCSI_IO ) {
+ mtx_lock(&sc->q_lock);
+ tws_q_insert_tail(sc, req, TWS_BUSY_Q);
+ mtx_unlock(&sc->q_lock);
+ }
+
+ /*
+ * mfa register read and write must be in order.
+ * Get the io_lock to protect against simultinous
+ * passthru calls
+ */
+ mtx_lock(&sc->io_lock);
+
+ tws_write_reg(sc, TWS_I2O0_HIBQPH, regh, 4);
+ tws_write_reg(sc, TWS_I2O0_HIBQPL, regl, 4);
+
+ sc->stats.reqs_in++;
+ mtx_unlock(&sc->io_lock);
+
+ return(TWS_REQ_RET_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/tws/tws_hdm.h b/sys/dev/tws/tws_hdm.h
new file mode 100644
index 0000000..5633fdf
--- /dev/null
+++ b/sys/dev/tws/tws_hdm.h
@@ -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$
+ */
+
+
+/* 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 41
+
+#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;
+};
+
+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 during request processing */
+
+ 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_handle 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/tws/tws_services.c b/sys/dev/tws/tws_services.c
new file mode 100644
index 0000000..07200b5
--- /dev/null
+++ b/sys/dev/tws/tws_services.c
@@ -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$
+ */
+
+#include <dev/tws/tws.h>
+#include <dev/tws/tws_hdm.h>
+#include <dev/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");
+
+/*
+ printf("%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n",
+ linenum, file, fun, desc, val1, val2);
+*/
+ printf(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)
+{
+
+ mtx_lock(&sc->q_lock);
+ for(int i=0;i<TWS_MAX_QS;i++) {
+ sc->q_head[i] = NULL;
+ sc->q_tail[i] = NULL;
+ }
+ mtx_unlock(&sc->q_lock);
+
+}
+
+/* called with lock held */
+static void
+tws_insert2_empty_q(struct tws_softc *sc, struct tws_request *req,
+ u_int8_t q_type )
+{
+
+ mtx_assert(&sc->q_lock, MA_OWNED);
+ 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 )
+{
+
+ mtx_assert(&sc->q_lock, MA_OWNED);
+ 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 )
+{
+
+ mtx_assert(&sc->q_lock, MA_OWNED);
+ 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;
+
+ mtx_assert(&sc->q_lock, MA_OWNED);
+ 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;
+
+ mtx_assert(&sc->q_lock, MA_OWNED);
+ 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;
+
+ mtx_assert(&sc->q_lock, MA_OWNED);
+ 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);
+ timeout(tws_print_stats, sc, 300*hz);
+
+}
+/* --------------------- misc service end --------------------- */
diff --git a/sys/dev/tws/tws_services.h b/sys/dev/tws/tws_services.h
new file mode 100644
index 0000000..643d720
--- /dev/null
+++ b/sys/dev/tws/tws_services.h
@@ -0,0 +1,141 @@
+/*
+ * 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$
+ */
+
+
+/* #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,
+#if 0
+ DEBUG,
+#endif
+};
+
+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
+
+/* req return codes */
+#define TWS_REQ_RET_SUBMIT_SUCCESS 0
+#define TWS_REQ_RET_PEND_NOMFA 1
+#define TWS_REQ_RET_RESET 2
+#define TWS_REQ_RET_INVALID 0xdead
+
+
+/* ------------------------ */
+#if (__FreeBSD_version >= 700000)
+#include <sys/clock.h>
+#define TWS_LOCAL_TIME (time_second - utc_offset())
+#else
+#include <machine/clock.h>
+#define TWS_LOCAL_TIME (time_second - (tz_minuteswest * 60) - \
+ (wall_cmos_clock ? adjkerntz : 0))
+#endif
+
diff --git a/sys/dev/tws/tws_user.c b/sys/dev/tws/tws_user.c
new file mode 100644
index 0000000..98b8a9b
--- /dev/null
+++ b/sys/dev/tws/tws_user.c
@@ -0,0 +1,379 @@
+/*
+ * 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$
+ */
+
+#include <dev/tws/tws.h>
+#include <dev/tws/tws_services.h>
+#include <dev/tws/tws_hdm.h>
+#include <dev/tws/tws_user.h>
+
+
+int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags,
+ d_thread_t *proc);
+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_timeout(void *arg);
+
+int
+tws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags,
+ d_thread_t *proc)
+{
+ 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);
+ mtx_lock(&sc->sim_lock);
+ error = tws_bus_scan(sc);
+ mtx_unlock(&sc->sim_lock);
+ 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_ONLINE) {
+ return(EBUSY);
+ }
+
+ do {
+ req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU);
+ if ( !req ) {
+ sc->chan = 1;
+ error = tsleep((void *)&sc->chan, 0,
+ "tws_sleep", TWS_IOCTL_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 = malloc(req->length, M_TWS, M_WAITOK | M_ZERO);
+ if ( !req->data ) {
+ TWS_TRACE_DEBUG(sc, "malloc failed", 0, req->request_id);
+ req->state = TWS_REQ_STATE_FREE;
+ ubuf->driver_pkt.os_status = ENOMEM;
+ if ( sc->chan ) {
+ sc->chan = 0;
+ wakeup_one((void *)&sc->chan);
+ }
+ return(ENOMEM);
+ }
+ bzero(req->data, req->length);
+ 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;
+
+ }
+
+ error = tws_map_request(sc, req);
+ if (error) {
+ ubuf->driver_pkt.os_status = error;
+ goto out;
+ }
+
+//==================================================================================================
+ mtx_lock(&sc->gen_lock);
+ error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz);
+ mtx_unlock(&sc->gen_lock);
+ if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) {
+ TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id);
+ tws_timeout((void*) req);
+ }
+
+ if ( req->error_code == TWS_REQ_RET_RESET ) {
+ error = EBUSY;
+ req->error_code = EBUSY;
+ TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id);
+ }
+
+ 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);
+ }
+//==================================================================================================
+
+out:
+ free(req->data, M_TWS);
+
+ if ( error )
+ TWS_TRACE_DEBUG(sc, "errored", error, 0);
+
+ if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS )
+ ubuf->driver_pkt.os_status = error;
+
+ req->state = TWS_REQ_STATE_FREE;
+
+ if ( sc->chan && (tws_get_state(sc) == TWS_ONLINE) ) {
+ sc->chan = 0;
+ wakeup_one((void *)&sc->chan);
+ }
+ return(error);
+}
+
+void
+tws_passthru_complete(struct tws_request *req)
+{
+ req->state = TWS_REQ_STATE_COMPLETE;
+ wakeup_one(req);
+
+}
+
+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;
+
+ mtx_lock(&sc->gen_lock);
+ 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;
+
+ }
+ mtx_unlock(&sc->gen_lock);
+ 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;
+ mtx_assert(&sc->gen_lock, MA_OWNED);
+
+ 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/tws/tws_user.h b/sys/dev/tws/tws_user.h
new file mode 100644
index 0000000..5ae29bc
--- /dev/null
+++ b/sys/dev/tws/tws_user.h
@@ -0,0 +1,156 @@
+/*
+ * 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$
+ */
+
+#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)
+
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 69e8d9b0..2f16ccf 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -147,6 +147,7 @@ device iir # Intel Integrated RAID
device ips # IBM (Adaptec) ServeRAID
device mly # Mylex AcceleRAID/eXtremeRAID
device twa # 3ware 9000 series PATA/SATA RAID
+device tws # LSI 3ware 9750 SATA+SAS 6Gb/s RAID controller
# RAID controllers
device aac # Adaptec FSA RAID
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index eec84d35..9b0f24b 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -302,6 +302,7 @@ SUBDIR= ${_3dfx} \
trm \
${_twa} \
twe \
+ tws \
tx \
txp \
uart \
diff --git a/sys/modules/tws/Makefile b/sys/modules/tws/Makefile
new file mode 100644
index 0000000..af26579
--- /dev/null
+++ b/sys/modules/tws/Makefile
@@ -0,0 +1,10 @@
+# Makefile for tws (LSI 3ware 9750 SAS2/SATA-II RAID PCIe) driver
+# $FreeBSD$
+
+KMOD= tws
+.PATH: ${.CURDIR}/../../dev/${KMOD}
+
+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>
OpenPOWER on IntegriCloud