twa(4): Sync with FreeBSD's current code.
authorSascha Wildner <saw@online.de>
Tue, 3 Aug 2010 12:20:56 +0000 (14:20 +0200)
committerSascha Wildner <saw@online.de>
Tue, 3 Aug 2010 12:23:10 +0000 (14:23 +0200)
This adds support for more and newer cards (see the hardware list in
the manual page).

Big thanks to Damian Lubosch <dl@xiqit.de> for testing it on a 9650SE.

34 files changed:
share/man/man4/twa.4
sys/conf/files
sys/conf/options
sys/config/GENERIC
sys/config/LINT
sys/config/X86_64_GENERIC
sys/dev/raid/twa/Makefile
sys/dev/raid/twa/tw_cl.h [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_externs.h [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_fwif.h [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_init.c [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_intr.c [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_io.c [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_ioctl.h [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_misc.c [new file with mode: 0644]
sys/dev/raid/twa/tw_cl_share.h [new file with mode: 0644]
sys/dev/raid/twa/tw_osl.h [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_cam.c [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_externs.h [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_freebsd.c [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_includes.h [copied from sys/dev/raid/twa/twa_includes.h with 72% similarity]
sys/dev/raid/twa/tw_osl_inline.h [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_ioctl.h [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_share.h [new file with mode: 0644]
sys/dev/raid/twa/tw_osl_types.h [moved from sys/dev/raid/twa/twa_includes.h with 64% similarity]
sys/dev/raid/twa/twa.c [deleted file]
sys/dev/raid/twa/twa.h [deleted file]
sys/dev/raid/twa/twa_cam.c [deleted file]
sys/dev/raid/twa/twa_externs.h [deleted file]
sys/dev/raid/twa/twa_freebsd.c [deleted file]
sys/dev/raid/twa/twa_fwimg.c [deleted file]
sys/dev/raid/twa/twa_globals.c [deleted file]
sys/dev/raid/twa/twa_ioctl.h [deleted file]
sys/dev/raid/twa/twa_reg.h [deleted file]

index af0f39e..a8a5290 100644 (file)
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/share/man/man4/twa.4,v 1.4 2005/02/27 13:14:28 brueffer Exp $
-.\" $DragonFly: src/share/man/man4/twa.4,v 1.1 2006/04/30 18:46:39 swildner Exp $
+.\" $FreeBSD: src/share/man/man4/twa.4,v 1.9 2010/05/13 12:07:55 uqs Exp $
 .\"
-.Dd August 15, 2004
+.Dd August 1, 2010
 .Dt TWA 4
 .Os
 .Sh NAME
 .Nm twa
-.Nd 3ware 9000 series SATA RAID controllers driver
+.Nd 3ware 9000/9500/9550/9650 series SATA RAID controllers driver
 .Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device scbus"
 .Cd "device twa"
+.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
+twa_load="YES"
+.Ed
 .Sh DESCRIPTION
 The
 .Nm
-driver provides support for the AMCC's 3ware 9000 series SATA controllers.
+driver provides support for AMCC's 3ware 9000/9500/9550/9650 series
+SATA controllers.
 .Pp
-These controllers are available in 4, 8 or 12-port configurations,
+These controllers are available in 4, 8, 12 or 16-port configurations,
 and support the following RAID levels: 0, 1, 10, 5, 50.
 The device nodes for the controllers are of the form
 .Pa /dev/twa Ns Ar X ,
@@ -69,6 +82,38 @@ AMCC's 3ware 9500S-8MI
 AMCC's 3ware 9500S-12
 .It
 AMCC's 3ware 9500S-12MI
+.It
+AMCC's 3ware 9500SX-4LP
+.It
+AMCC's 3ware 9500SX-8LP
+.It
+AMCC's 3ware 9500SX-12
+.It
+AMCC's 3ware 9500SX-12MI
+.It
+AMCC's 3ware 9500SX-16ML
+.It
+AMCC's 3ware 9550SX-4LP
+.It
+AMCC's 3ware 9550SX-8LP
+.It
+AMCC's 3ware 9550SX-12
+.It
+AMCC's 3ware 9550SX-12MI
+.It
+AMCC's 3ware 9550SX-16ML
+.It
+AMCC's 3ware 9650SE-2LP
+.It
+AMCC's 3ware 9650SE-4LPML
+.It
+AMCC's 3ware 9650SE-8LPML
+.It
+AMCC's 3ware 9650SE-12ML
+.It
+AMCC's 3ware 9650SE-16ML
+.It
+AMCC's 3ware 9650SE-24M8
 .El
 .Sh DIAGNOSTICS
 Whenever the driver encounters a command failure, it prints out an error code in
index c2b03b8..56b3960 100644 (file)
@@ -716,13 +716,14 @@ bus/iicbus/iicbus.c       optional iicbus
 bus/iicbus/iic.c       optional iic
 dev/netif/ic/if_ic.c   optional ic
 dev/disk/trm/trm.c             optional trm
+dev/raid/twa/tw_cl_init.c              optional twa
+dev/raid/twa/tw_cl_intr.c              optional twa
+dev/raid/twa/tw_cl_io.c                        optional twa
+dev/raid/twa/tw_cl_misc.c              optional twa
+dev/raid/twa/tw_osl_cam.c              optional twa
+dev/raid/twa/tw_osl_freebsd.c          optional twa
 dev/raid/twe/twe.c                     optional twe
 dev/raid/twe/twe_freebsd.c             optional twe
-dev/raid/twa/twa.c                     optional twa
-dev/raid/twa/twa_cam.c                 optional twa
-dev/raid/twa/twa_freebsd.c             optional twa
-dev/raid/twa/twa_fwimg.c               optional twa
-dev/raid/twa/twa_globals.c             optional twa
 dev/netif/tx/if_tx.c                   optional tx
 dev/netif/txp/if_txp.c         optional txp
 dev/raid/vinum/vinum.c         optional vinum
index e9f1f32..46644b6 100644 (file)
@@ -74,7 +74,6 @@ AHD_REG_PRETTY_PRINT  opt_aic79xx.h   # Print register bitfields in debug
 ADW_ALLOW_MEMIO                opt_adw.h       # Allow PCI devices to use memory
                                        # mapped I/O
 TWA_DEBUG              opt_twa.h
-TWA_FLASH_FIRMWARE     opt_twa.h
 
 #options for ACPI support
 ACPI_DEBUG             opt_acpi.h
index 0a13082..096c175 100644 (file)
@@ -160,7 +160,7 @@ device              ips             # IBM ServeRAID
 device         amr             # AMI MegaRAID
 device         mlx             # Mylex DAC960 family
 device         twe             # 3ware Escalade 7000/8000's
-device         twa             # 3ware Escalade 9000's
+device         twa             # 3ware 9000 series PATA/SATA RAID
 device         pst             # Promise Supertrack
 
 # atkbdc0 controls both the keyboard and the PS/2 mouse
index 8bb7ef4..4f300d6 100644 (file)
@@ -1255,9 +1255,8 @@ device            amr             # AMI MegaRAID
 # 3ware ATA RAID
 #
 device         twe             # 3ware ATA RAID
-device         twa             # 3ware SATA RAID
+device         twa             # 3ware 9000 series PATA/SATA RAID
 options        TWA_DEBUG=10    # enable debug messages
-options        TWA_FLASH_FIRMWARE
 
 #
 # Promise Supertrack SX6000
index 5f4435d..5a516f5 100644 (file)
@@ -142,7 +142,7 @@ device              ips             # IBM ServeRAID
 device         amr             # AMI MegaRAID
 device         mlx             # Mylex DAC960 family
 device         twe             # 3ware Escalade 7000/8000's
-device         twa             # 3ware Escalade 9000's
+device         twa             # 3ware 9000 series PATA/SATA RAID
 
 # atkbdc0 controls both the keyboard and the PS/2 mouse
 device         atkbdc0 at isa? port IO_KBD
index 9e89c9c..e6cf930 100644 (file)
@@ -1,25 +1,45 @@
-# $FreeBSD$
-# $DragonFly: src/sys/dev/raid/twa/Makefile,v 1.1 2004/04/16 20:13:16 drhodus Exp $
+# $FreeBSD: src/sys/modules/twa/Makefile,v 1.8 2007/05/09 04:16:31 scottl Exp $
+#
+# Copyright (c) 2004-06 Applied Micro Circuits Corporation.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# 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.
+#
 
 #
-# Uncomment the following line to bundle firmware with the driver,
-# which may be flashed onto the controller, if the firmware on the
-# controller is older than the one bundled, and needs to be upgraded.
-# The size of the driver will increase significantly (to over 500KB)
-# if this option is selected.
+# 3ware driver for 9000 series storage controllers.
+#
+# Author: Vinod Kashyap
+# Modifications by: Adam Radford
 #
-FLASH_FIRMWARE=1
 
 KMOD = twa
-.PATH: ${.CURDIR}
-SRCS = twa_freebsd.c twa_cam.c twa.c twa_globals.c \
+SRCS=  tw_osl_freebsd.c tw_osl_cam.c \
+      tw_cl_init.c tw_cl_io.c tw_cl_intr.c tw_cl_misc.c \
       bus_if.h device_if.h pci_if.h opt_scsi.h opt_cam.h opt_twa.h
 
-.if defined(FLASH_FIRMWARE)
-CFLAGS+=-DTWA_FLASH_FIRMWARE
-SRCS += twa_fwimg.c
-.endif
+# Uncomment the following line to turn on Enclosure Services support.
+#CFLAGS+= -DTWA_ENCLOSURE_SUPPORT
 
-#CFLAGS+=-DTWA_DEBUG=0
+#CFLAGS+= -DTWA_DEBUG=0
 
 .include <bsd.kmod.mk>
diff --git a/sys/dev/raid/twa/tw_cl.h b/sys/dev/raid/twa/tw_cl.h
new file mode 100644 (file)
index 0000000..8963c9e
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl.h,v 1.5 2010/07/09 17:38:15 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ */
+
+
+
+#ifndef TW_CL_H
+
+#define TW_CL_H
+
+
+/*
+ * Common Layer internal macros, structures and functions.
+ */
+
+
+#define TW_CLI_SECTOR_SIZE             0x200
+#define TW_CLI_REQUEST_TIMEOUT_PERIOD  60 /* seconds */
+#define TW_CLI_RESET_TIMEOUT_PERIOD    60 /* seconds */
+#define TW_CLI_MAX_RESET_ATTEMPTS      2
+
+/* Possible values of ctlr->ioctl_lock.lock. */
+#define TW_CLI_LOCK_FREE               0x0     /* lock is free */
+#define TW_CLI_LOCK_HELD               0x1     /* lock is held */
+
+/* Possible values of req->state. */
+#define TW_CLI_REQ_STATE_INIT          0x0     /* being initialized */
+#define TW_CLI_REQ_STATE_BUSY          0x1     /* submitted to controller */
+#define TW_CLI_REQ_STATE_PENDING       0x2     /* in pending queue */
+#define TW_CLI_REQ_STATE_COMPLETE      0x3     /* completed by controller */
+
+/* Possible values of req->flags. */
+#define TW_CLI_REQ_FLAGS_7K            (1<<0)  /* 7000 cmd pkt */
+#define TW_CLI_REQ_FLAGS_9K            (1<<1)  /* 9000 cmd pkt */
+#define TW_CLI_REQ_FLAGS_INTERNAL      (1<<2)  /* internal request */
+#define TW_CLI_REQ_FLAGS_PASSTHRU      (1<<3)  /* passthru request */
+#define TW_CLI_REQ_FLAGS_EXTERNAL      (1<<4)  /* external request */
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+/* Register offsets in PCI config space. */
+#define TW_CLI_PCI_CONFIG_COMMAND_OFFSET       0x4 /* cmd register offset */
+#define TW_CLI_PCI_CONFIG_STATUS_OFFSET                0x6 /* status register offset */
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+
+#ifdef TW_OSL_DEBUG
+struct tw_cli_q_stats {
+       TW_UINT32       cur_len;/* current # of entries in q */
+       TW_UINT32       max_len;         /* max # of entries in q, ever reached */
+};
+#endif /* TW_OSL_DEBUG */
+
+
+/* Queues of CL internal request context packets. */
+#define TW_CLI_FREE_Q          0       /* free q */
+#define TW_CLI_BUSY_Q          1       /* q of reqs submitted to fw */
+#define TW_CLI_PENDING_Q       2       /* q of reqs deferred due to 'q full' */
+#define TW_CLI_COMPLETE_Q      3       /* q of reqs completed by fw */
+#define TW_CLI_Q_COUNT         4       /* total number of queues */
+
+
+/* CL's internal request context. */
+struct tw_cli_req_context {
+       struct tw_cl_req_handle *req_handle;/* handle to track requests between
+                                               OSL & CL */
+       struct tw_cli_ctlr_context  *ctlr; /* ptr to CL's controller context */
+       struct tw_cl_command_packet *cmd_pkt;/* ptr to ctlr cmd pkt */
+       TW_UINT64       cmd_pkt_phys;   /* cmd pkt physical address */
+       TW_VOID         *data;          /* ptr to data being passed to fw */
+       TW_UINT32       length;         /* length of data being passed to fw */
+       TW_UINT64       data_phys;      /* physical address of data */
+
+       TW_UINT32       state;          /* request state */
+       TW_UINT32       flags;          /* request flags */
+
+       TW_UINT32       error_code;     /* error encountered before submission
+                                       of request to fw, if any */
+
+       TW_VOID         *orig_req;      /* ptr to original request for use
+                                       during callback */
+       TW_VOID         (*tw_cli_callback)(struct tw_cli_req_context *req);
+                                       /* CL internal callback */
+       TW_UINT32       request_id;     /* request id for tracking with fw */
+       struct tw_cl_link link;         /* to link this request in a list */
+};
+
+
+/* CL's internal controller context. */
+struct tw_cli_ctlr_context {
+       struct tw_cl_ctlr_handle *ctlr_handle;  /* handle to track ctlr between
+                                                       OSL & CL. */
+       struct tw_cli_req_context *req_ctxt_buf;/* pointer to the array of CL's
+                                               internal request context pkts */
+       struct tw_cl_command_packet *cmd_pkt_buf;/* ptr to array of cmd pkts */
+
+       TW_UINT64               cmd_pkt_phys;   /* phys addr of cmd_pkt_buf */
+
+       TW_UINT32               device_id;      /* controller device id */
+       TW_UINT32               arch_id;        /* controller architecture id */
+       TW_UINT8                active;                   /* Initialization done, and controller is active. */
+       TW_UINT8                interrupts_enabled;       /* Interrupts on controller enabled. */
+       TW_UINT8                internal_req_busy;        /* Data buffer for internal requests in use. */
+       TW_UINT8                get_more_aens;            /* More AEN's need to be retrieved. */
+       TW_UINT8                reset_in_progress;        /* Controller is being reset. */
+       TW_UINT8                reset_phase1_in_progress; /* In 'phase 1' of reset. */
+       TW_UINT32               flags;          /* controller settings */
+       TW_UINT32               sg_size_factor; /* SG element size should be a
+                                                       multiple of this */
+
+       /* Request queues and arrays. */
+       struct tw_cl_link       req_q_head[TW_CLI_Q_COUNT];
+
+       TW_UINT8                *internal_req_data;/* internal req data buf */
+       TW_UINT64               internal_req_data_phys;/* phys addr of internal
+                                                       req data buf */
+       TW_UINT32               max_simult_reqs; /* max simultaneous requests
+                                                       supported */
+       TW_UINT32               max_aens_supported;/* max AEN's supported */
+       /* AEN handler fields. */
+       struct tw_cl_event_packet *aen_queue;   /* circular queue of AENs from
+                                                       firmware/CL/OSL */
+       TW_UINT32               aen_head;       /* AEN queue head */
+       TW_UINT32               aen_tail;       /* AEN queue tail */
+       TW_UINT32               aen_cur_seq_id; /* index of the last event+1 */
+       TW_UINT32               aen_q_overflow; /* indicates if unretrieved
+                                               events were overwritten */
+       TW_UINT32               aen_q_wrapped;  /* indicates if AEN queue ever
+                                                       wrapped */
+
+       TW_UINT16               working_srl;    /* driver & firmware negotiated
+                                                       srl */
+       TW_UINT16               working_branch; /* branch # of the firmware
+                                       that the driver is compatible with */
+       TW_UINT16               working_build;  /* build # of the firmware
+                                       that the driver is compatible with */
+       TW_UINT16               fw_on_ctlr_srl; /* srl of running firmware */
+       TW_UINT16               fw_on_ctlr_branch;/* branch # of running
+                                                       firmware */
+       TW_UINT16               fw_on_ctlr_build;/* build # of running
+                                                       firmware */
+       TW_UINT32               operating_mode; /* base mode/current mode */
+
+       TW_INT32                host_intr_pending;/* host intr processing
+                                                       needed */
+       TW_INT32                attn_intr_pending;/* attn intr processing
+                                                       needed */
+       TW_INT32                cmd_intr_pending;/* cmd intr processing
+                                                       needed */
+       TW_INT32                resp_intr_pending;/* resp intr processing
+                                                       needed */
+
+       TW_LOCK_HANDLE          gen_lock_handle;/* general purpose lock */
+       TW_LOCK_HANDLE          *gen_lock;/* ptr to general purpose lock */
+       TW_LOCK_HANDLE          io_lock_handle; /* lock held during cmd
+                                               submission */
+       TW_LOCK_HANDLE          *io_lock;/* ptr to lock held during cmd
+                                               submission */
+
+#ifdef TW_OSL_CAN_SLEEP
+       TW_SLEEP_HANDLE         sleep_handle;   /* handle to co-ordinate sleeps
+                                               & wakeups */
+#endif /* TW_OSL_CAN_SLEEP */
+
+       struct {
+               TW_UINT32       lock;           /* lock state */
+               TW_TIME         timeout;        /* time at which the lock will
+                                               become available, even if not
+                                               explicitly released */
+       } ioctl_lock;           /* lock for use by user applications, for
+                               synchronization between ioctl calls */
+#ifdef TW_OSL_DEBUG
+       struct tw_cli_q_stats   q_stats[TW_CLI_Q_COUNT];/* queue statistics */
+#endif /* TW_OSL_DEBUG */
+};
+
+
+
+/*
+ * Queue primitives
+ */
+
+#ifdef TW_OSL_DEBUG
+
+#define TW_CLI_Q_INIT(ctlr, q_type)    do {                            \
+       (ctlr)->q_stats[q_type].cur_len = 0;                            \
+       (ctlr)->q_stats[q_type].max_len = 0;                            \
+} while (0)
+
+
+#define TW_CLI_Q_INSERT(ctlr, q_type)  do {                            \
+       struct tw_cli_q_stats *q_stats = &((ctlr)->q_stats[q_type]);    \
+                                                                       \
+       if (++(q_stats->cur_len) > q_stats->max_len)                    \
+               q_stats->max_len = q_stats->cur_len;                    \
+} while (0)
+
+
+#define TW_CLI_Q_REMOVE(ctlr, q_type)                                  \
+       (ctlr)->q_stats[q_type].cur_len--
+
+#else /* TW_OSL_DEBUG */
+
+#define TW_CLI_Q_INIT(ctlr, q_index)
+#define TW_CLI_Q_INSERT(ctlr, q_index)
+#define TW_CLI_Q_REMOVE(ctlr, q_index)
+
+#endif /* TW_OSL_DEBUG */
+
+
+/* Initialize a queue of requests. */
+static __inline TW_VOID
+tw_cli_req_q_init(struct tw_cli_ctlr_context *ctlr, TW_UINT8 q_type)
+{
+       TW_CL_Q_INIT(&(ctlr->req_q_head[q_type]));
+       TW_CLI_Q_INIT(ctlr, q_type);
+}
+
+
+
+/* Insert the given request at the head of the given queue (q_type). */
+static __inline TW_VOID
+tw_cli_req_q_insert_head(struct tw_cli_req_context *req, TW_UINT8 q_type)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+
+       tw_osl_get_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+       TW_CL_Q_INSERT_HEAD(&(ctlr->req_q_head[q_type]), &(req->link));
+       TW_CLI_Q_INSERT(ctlr, q_type);
+       tw_osl_free_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+}
+
+
+
+/* Insert the given request at the tail of the given queue (q_type). */
+static __inline TW_VOID
+tw_cli_req_q_insert_tail(struct tw_cli_req_context *req, TW_UINT8 q_type)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+
+       tw_osl_get_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+       TW_CL_Q_INSERT_TAIL(&(ctlr->req_q_head[q_type]), &(req->link));
+       TW_CLI_Q_INSERT(ctlr, q_type);
+       tw_osl_free_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+}
+
+
+
+/* Remove and return the request at the head of the given queue (q_type). */
+static __inline struct tw_cli_req_context *
+tw_cli_req_q_remove_head(struct tw_cli_ctlr_context *ctlr, TW_UINT8 q_type)
+{
+       struct tw_cli_req_context       *req = TW_CL_NULL;
+       struct tw_cl_link               *link;
+
+       tw_osl_get_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+       if ((link = TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[q_type]))) !=
+               TW_CL_NULL) {
+               req = TW_CL_STRUCT_HEAD(link,
+                       struct tw_cli_req_context, link);
+               TW_CL_Q_REMOVE_ITEM(&(ctlr->req_q_head[q_type]), &(req->link));
+               TW_CLI_Q_REMOVE(ctlr, q_type);
+       }
+       tw_osl_free_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+       return(req);
+}
+
+
+
+/* Remove the given request from the given queue (q_type). */
+static __inline TW_VOID
+tw_cli_req_q_remove_item(struct tw_cli_req_context *req, TW_UINT8 q_type)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+
+       tw_osl_get_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+       TW_CL_Q_REMOVE_ITEM(&(ctlr->req_q_head[q_type]), &(req->link));
+       TW_CLI_Q_REMOVE(ctlr, q_type);
+       tw_osl_free_lock(ctlr->ctlr_handle, ctlr->gen_lock);
+}
+
+
+
+/* Create an event packet for an event/error posted by the controller. */
+#define tw_cli_create_ctlr_event(ctlr, event_src, cmd_hdr)     do {    \
+       TW_UINT8 severity =                                             \
+               GET_SEVERITY((cmd_hdr)->status_block.res__severity);    \
+                                                                       \
+       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_TRUE, event_src,    \
+               (cmd_hdr)->status_block.error,                          \
+               severity,                                               \
+               tw_cli_severity_string_table[severity],                 \
+               (cmd_hdr)->err_specific_desc +                          \
+               tw_osl_strlen((cmd_hdr)->err_specific_desc) + 1,        \
+               (cmd_hdr)->err_specific_desc);                          \
+       /* Print 18 bytes of sense information. */                      \
+       tw_cli_dbg_printf(2, ctlr->ctlr_handle,                         \
+               tw_osl_cur_func(),                                      \
+               "sense info: %x %x %x %x %x %x %x %x %x "               \
+               "%x %x %x %x %x %x %x %x %x",                           \
+               (cmd_hdr)->sense_data[0], (cmd_hdr)->sense_data[1],     \
+               (cmd_hdr)->sense_data[2], (cmd_hdr)->sense_data[3],     \
+               (cmd_hdr)->sense_data[4], (cmd_hdr)->sense_data[5],     \
+               (cmd_hdr)->sense_data[6], (cmd_hdr)->sense_data[7],     \
+               (cmd_hdr)->sense_data[8], (cmd_hdr)->sense_data[9],     \
+               (cmd_hdr)->sense_data[10], (cmd_hdr)->sense_data[11],   \
+               (cmd_hdr)->sense_data[12], (cmd_hdr)->sense_data[13],   \
+               (cmd_hdr)->sense_data[14], (cmd_hdr)->sense_data[15],   \
+               (cmd_hdr)->sense_data[16], (cmd_hdr)->sense_data[17]);  \
+} while (0)
+
+
+
+#endif /* TW_CL_H */
diff --git a/sys/dev/raid/twa/tw_cl_externs.h b/sys/dev/raid/twa/tw_cl_externs.h
new file mode 100644 (file)
index 0000000..441a841
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_externs.h,v 1.3 2007/05/09 04:16:32 scottl Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ */
+
+
+
+#ifndef TW_CL_EXTERNS_H
+
+#define TW_CL_EXTERNS_H
+
+
+/*
+ * Data structures and functions global to the Common Layer.
+ */
+
+
+extern TW_INT8                 tw_cli_fw_img[];
+extern TW_INT32                        tw_cli_fw_img_size;
+extern TW_INT8                 *tw_cli_severity_string_table[];
+
+
+/* Do controller initialization. */
+extern TW_INT32        tw_cli_start_ctlr(struct tw_cli_ctlr_context *ctlr);
+
+/* Establish a logical connection with the firmware on the controller. */
+extern TW_INT32        tw_cli_init_connection(struct tw_cli_ctlr_context *ctlr,
+       TW_UINT16 message_credits, TW_UINT32 set_features,
+       TW_UINT16 current_fw_srl, TW_UINT16 current_fw_arch_id,
+       TW_UINT16 current_fw_branch, TW_UINT16 current_fw_build,
+       TW_UINT16 *fw_on_ctlr_srl, TW_UINT16 *fw_on_ctlr_arch_id,
+       TW_UINT16 *fw_on_ctlr_branch, TW_UINT16 *fw_on_ctlr_build,
+       TW_UINT32 *init_connect_result);
+
+
+
+/* Functions in tw_cl_io.c */
+
+/* Submit a command packet to the firmware on the controller. */
+extern TW_INT32        tw_cli_submit_cmd(struct tw_cli_req_context *req);
+
+/* Get a firmware parameter. */
+extern TW_INT32        tw_cli_get_param(struct tw_cli_ctlr_context *ctlr,
+       TW_INT32 table_id, TW_INT32 parameter_id, TW_VOID *param_data,
+       TW_INT32 size, TW_VOID (* callback)(struct tw_cli_req_context *req));
+
+/* Set a firmware parameter. */
+extern TW_INT32        tw_cli_set_param(struct tw_cli_ctlr_context *ctlr,
+       TW_INT32 table_id, TW_INT32 param_id, TW_INT32 param_size,
+       TW_VOID *data, TW_VOID (* callback)(struct tw_cli_req_context *req));
+
+/* Submit a command to the firmware and poll for completion. */
+extern TW_INT32        tw_cli_submit_and_poll_request(struct tw_cli_req_context *req,
+       TW_UINT32 timeout);
+
+/* Soft reset the controller. */
+extern TW_INT32        tw_cli_soft_reset(struct tw_cli_ctlr_context *ctlr);
+
+/* Send down a SCSI command to the firmware (usually, an internal Req Sense. */
+extern TW_INT32        tw_cli_send_scsi_cmd(struct tw_cli_req_context *req,
+       TW_INT32 cmd);
+
+/* Get an AEN from the firmware (by sending down a Req Sense). */
+extern TW_INT32        tw_cli_get_aen(struct tw_cli_ctlr_context *ctlr);
+
+/* Fill in the scatter/gather list. */
+extern TW_VOID tw_cli_fill_sg_list(struct tw_cli_ctlr_context *ctlr,
+       TW_VOID *sgl_src, TW_VOID *sgl_dest, TW_INT32 num_sgl_entries);
+
+
+
+/* Functions in tw_cl_intr.c */
+
+/* Process a host interrupt. */
+extern TW_VOID tw_cli_process_host_intr(struct tw_cli_ctlr_context *ctlr);
+
+/* Process an attention interrupt. */
+extern TW_VOID tw_cli_process_attn_intr(struct tw_cli_ctlr_context *ctlr);
+
+/* Process a command interrupt. */
+extern TW_VOID tw_cli_process_cmd_intr(struct tw_cli_ctlr_context *ctlr);
+
+/* Process a response interrupt from the controller. */
+extern TW_INT32        tw_cli_process_resp_intr(struct tw_cli_ctlr_context *ctlr);
+
+/* Submit any requests in the pending queue to the firmware. */
+extern TW_INT32        tw_cli_submit_pending_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* Process all requests in the complete queue. */
+extern TW_VOID tw_cli_process_complete_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* CL internal callback for SCSI/fw passthru requests. */
+extern TW_VOID tw_cli_complete_io(struct tw_cli_req_context *req);
+
+/* Completion routine for SCSI requests. */
+extern TW_VOID tw_cli_scsi_complete(struct tw_cli_req_context *req);
+
+/* Callback for get/set param requests. */
+extern TW_VOID tw_cli_param_callback(struct tw_cli_req_context *req);
+
+/* Callback for Req Sense commands to get AEN's. */
+extern TW_VOID tw_cli_aen_callback(struct tw_cli_req_context *req);
+
+/* Decide what to do with a retrieved AEN. */
+extern TW_UINT16       tw_cli_manage_aen(struct tw_cli_ctlr_context *ctlr,
+       struct tw_cli_req_context *req);
+
+/* Enable controller interrupts. */
+extern TW_VOID
+       tw_cli_enable_interrupts(struct tw_cli_ctlr_context *ctlr_handle);
+
+/* Disable controller interrupts. */
+extern TW_VOID
+       tw_cli_disable_interrupts(struct tw_cli_ctlr_context *ctlr_handle);
+
+
+
+/* Functions in tw_cl_misc.c */
+
+/* Print if dbg_level is appropriate (by calling OS Layer). */
+extern TW_VOID tw_cli_dbg_printf(TW_UINT8 dbg_level,
+       struct tw_cl_ctlr_handle *ctlr_handle, const TW_INT8 *cur_func,
+       TW_INT8 *fmt, ...);
+
+/* Describe meaning of each set bit in the given register. */
+extern TW_INT8 *tw_cli_describe_bits(TW_UINT32 reg, TW_INT8 *str);
+
+/* Complete all requests in the complete queue with a RESET status. */
+extern TW_VOID tw_cli_drain_complete_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* Complete all requests in the busy queue with a RESET status. */
+extern TW_VOID tw_cli_drain_busy_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* Complete all requests in the pending queue with a RESET status. */
+extern TW_VOID tw_cli_drain_pending_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* Drain the controller response queue. */
+extern TW_INT32        tw_cli_drain_response_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* Find a particular response in the controller response queue. */
+extern TW_INT32        tw_cli_find_response(struct tw_cli_ctlr_context *ctlr,
+       TW_INT32 req_id);
+
+/* Drain the controller AEN queue. */
+extern TW_INT32        tw_cli_drain_aen_queue(struct tw_cli_ctlr_context *ctlr);
+
+/* Determine if a given AEN has been posted by the firmware. */
+extern TW_INT32        tw_cli_find_aen(struct tw_cli_ctlr_context *ctlr,
+       TW_UINT16 aen_code);
+
+/* Poll for a given status to show up in the firmware status register. */
+extern TW_INT32        tw_cli_poll_status(struct tw_cli_ctlr_context *ctlr,
+       TW_UINT32 status, TW_UINT32 timeout);
+
+/* Get a free CL internal request context packet. */
+extern struct tw_cli_req_context *
+       tw_cli_get_request(struct tw_cli_ctlr_context *ctlr
+       );
+
+/* Notify OSL of controller info (fw/BIOS versions, etc.). */
+extern TW_VOID tw_cli_notify_ctlr_info(struct tw_cli_ctlr_context *ctlr);
+
+/* Make sure that the firmware status register reports a proper status. */
+extern TW_INT32        tw_cli_check_ctlr_state(struct tw_cli_ctlr_context *ctlr,
+       TW_UINT32 status_reg);
+
+
+
+#endif /* TW_CL_EXTERNS_H */
diff --git a/sys/dev/raid/twa/tw_cl_fwif.h b/sys/dev/raid/twa/tw_cl_fwif.h
new file mode 100644 (file)
index 0000000..3c8e8a4
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_fwif.h,v 1.4 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ */
+
+
+
+#ifndef TW_CL_FWIF_H
+
+#define TW_CL_FWIF_H
+
+
+/*
+ * Macros and data structures for interfacing with the firmware.
+ */
+
+
+/* Register offsets from base address. */
+#define        TWA_CONTROL_REGISTER_OFFSET             0x0
+#define        TWA_STATUS_REGISTER_OFFSET              0x4
+#define        TWA_COMMAND_QUEUE_OFFSET                0x8
+#define        TWA_RESPONSE_QUEUE_OFFSET               0xC
+#define        TWA_COMMAND_QUEUE_OFFSET_LOW            0x20
+#define        TWA_COMMAND_QUEUE_OFFSET_HIGH           0x24
+#define        TWA_LARGE_RESPONSE_QUEUE_OFFSET         0x30
+
+
+/* Control register bit definitions. */
+#define TWA_CONTROL_ISSUE_HOST_INTERRUPT       0x00000020
+#define TWA_CONTROL_DISABLE_INTERRUPTS         0x00000040
+#define TWA_CONTROL_ENABLE_INTERRUPTS          0x00000080
+#define TWA_CONTROL_ISSUE_SOFT_RESET           0x00000100
+#define TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT  0x00004000
+#define TWA_CONTROL_UNMASK_COMMAND_INTERRUPT   0x00008000
+#define TWA_CONTROL_MASK_RESPONSE_INTERRUPT    0x00010000
+#define TWA_CONTROL_MASK_COMMAND_INTERRUPT     0x00020000
+#define TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT  0x00040000
+#define TWA_CONTROL_CLEAR_HOST_INTERRUPT       0x00080000
+#define TWA_CONTROL_CLEAR_PCI_ABORT            0x00100000
+#define TWA_CONTROL_CLEAR_QUEUE_ERROR          0x00400000
+#define TWA_CONTROL_CLEAR_PARITY_ERROR         0x00800000
+
+
+/* Status register bit definitions. */
+#define TWA_STATUS_ROM_BIOS_IN_SBUF            0x00000002
+#define TWA_STATUS_COMMAND_QUEUE_EMPTY         0x00001000
+#define TWA_STATUS_MICROCONTROLLER_READY       0x00002000
+#define TWA_STATUS_RESPONSE_QUEUE_EMPTY                0x00004000
+#define TWA_STATUS_COMMAND_QUEUE_FULL          0x00008000
+#define TWA_STATUS_RESPONSE_INTERRUPT          0x00010000
+#define TWA_STATUS_COMMAND_INTERRUPT           0x00020000
+#define TWA_STATUS_ATTENTION_INTERRUPT         0x00040000
+#define TWA_STATUS_HOST_INTERRUPT              0x00080000
+#define TWA_STATUS_PCI_ABORT_INTERRUPT         0x00100000
+#define TWA_STATUS_MICROCONTROLLER_ERROR       0x00200000
+#define TWA_STATUS_QUEUE_ERROR_INTERRUPT       0x00400000
+#define TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT  0x00800000
+#define TWA_STATUS_MINOR_VERSION_MASK          0x0F000000
+#define TWA_STATUS_MAJOR_VERSION_MASK          0xF0000000
+
+#define TWA_STATUS_UNEXPECTED_BITS             0x00F00000
+
+
+/* PCI related defines. */
+#define TWA_IO_CONFIG_REG                      0x10
+
+#define TWA_PCI_CONFIG_CLEAR_PARITY_ERROR      0xc100
+#define TWA_PCI_CONFIG_CLEAR_PCI_ABORT         0x2000
+
+#define TWA_RESET_PHASE1_NOTIFICATION_RESPONSE 0xFFFF
+#define TWA_RESET_PHASE1_WAIT_TIME_MS          500
+
+
+/* Command packet opcodes. */
+#define TWA_FW_CMD_NOP                         0x00
+#define TWA_FW_CMD_INIT_CONNECTION             0x01
+#define TWA_FW_CMD_READ                                0x02
+#define TWA_FW_CMD_WRITE                       0x03
+#define TWA_FW_CMD_READVERIFY                  0x04
+#define TWA_FW_CMD_VERIFY                      0x05
+#define TWA_FW_CMD_ZEROUNIT                    0x08
+#define TWA_FW_CMD_REPLACEUNIT                 0x09
+#define TWA_FW_CMD_HOTSWAP                     0x0A
+#define TWA_FW_CMD_SELFTESTS                   0x0B
+#define TWA_FW_CMD_SYNC_PARAM                  0x0C
+#define TWA_FW_CMD_REORDER_UNITS               0x0D
+
+#define TWA_FW_CMD_EXECUTE_SCSI                        0x10
+#define TWA_FW_CMD_ATA_PASSTHROUGH             0x11
+#define TWA_FW_CMD_GET_PARAM                   0x12
+#define TWA_FW_CMD_SET_PARAM                   0x13
+#define TWA_FW_CMD_CREATEUNIT                  0x14
+#define TWA_FW_CMD_DELETEUNIT                  0x15
+#define TWA_FW_CMD_DOWNLOAD_FIRMWARE           0x16
+#define TWA_FW_CMD_REBUILDUNIT                 0x17
+#define TWA_FW_CMD_POWER_MANAGEMENT            0x18
+
+#define TWA_FW_CMD_REMOTE_PRINT                        0x1B
+#define TWA_FW_CMD_HARD_RESET_FIRMWARE         0x1C
+#define TWA_FW_CMD_DEBUG                       0x1D
+
+#define TWA_FW_CMD_DIAGNOSTICS                 0x1F
+
+
+/* Misc defines. */
+#define TWA_SHUTDOWN_MESSAGE_CREDITS   0x001
+#define TWA_64BIT_SG_ADDRESSES         0x00000001
+#define TWA_EXTENDED_INIT_CONNECT      0x00000002
+#define TWA_BASE_MODE                  1
+#define TWA_BASE_FW_SRL                        24
+#define TWA_BASE_FW_BRANCH             0
+#define TWA_BASE_FW_BUILD              1
+#define TWA_CURRENT_FW_SRL             41
+#define TWA_CURRENT_FW_BRANCH_9K       4
+#define TWA_CURRENT_FW_BUILD_9K                8
+#define TWA_CURRENT_FW_BRANCH_9K_X     8
+#define TWA_CURRENT_FW_BUILD_9K_X      4
+#define TWA_MULTI_LUN_FW_SRL           28
+#define TWA_ARCH_ID_9K                 0x5     /* 9000 PCI controllers */
+#define TWA_ARCH_ID_9K_X               0x6     /* 9000 PCI-X controllers */
+#define TWA_CTLR_FW_SAME_OR_NEWER      0x00000001
+#define TWA_CTLR_FW_COMPATIBLE         0x00000002
+#define TWA_SENSE_DATA_LENGTH          18
+
+
+#define TWA_ARCH_ID(device_id)                                         \
+       (((device_id) == TW_CL_DEVICE_ID_9K) ? TWA_ARCH_ID_9K :         \
+       TWA_ARCH_ID_9K_X)
+#define TWA_CURRENT_FW_BRANCH(arch_id)                                 \
+       (((arch_id) == TWA_ARCH_ID_9K) ? TWA_CURRENT_FW_BRANCH_9K :     \
+       TWA_CURRENT_FW_BRANCH_9K_X)
+#define TWA_CURRENT_FW_BUILD(arch_id)                                  \
+       (((arch_id) == TWA_ARCH_ID_9K) ? TWA_CURRENT_FW_BUILD_9K :      \
+       TWA_CURRENT_FW_BUILD_9K_X)
+
+/*
+ * All SG addresses and DMA'able memory allocated by the OSL should be
+ * TWA_ALIGNMENT bytes aligned, and have a size that is a multiple of
+ * TWA_SG_ELEMENT_SIZE_FACTOR.
+ */
+#define TWA_ALIGNMENT(device_id)                       0x4
+#define TWA_SG_ELEMENT_SIZE_FACTOR(device_id)          \
+       (((device_id) == TW_CL_DEVICE_ID_9K) ? 512 : 4)
+
+
+/*
+ * Some errors of interest (in cmd_hdr->status_block.error) when a command
+ * is completed by the firmware with a bad status.
+ */
+#define TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED   0x010a
+#define TWA_ERROR_UNIT_OFFLINE                 0x0128
+#define TWA_ERROR_MORE_DATA                    0x0231
+
+
+/* AEN codes of interest. */
+#define TWA_AEN_QUEUE_EMPTY            0x00
+#define TWA_AEN_SOFT_RESET             0x01
+#define TWA_AEN_SYNC_TIME_WITH_HOST    0x31
+
+
+/* Table #'s and id's of parameters of interest in firmware's param table. */
+#define TWA_PARAM_VERSION_TABLE                0x0402
+#define TWA_PARAM_VERSION_FW           3       /* firmware version [16] */
+#define TWA_PARAM_VERSION_BIOS         4       /* BIOSs version [16] */
+#define TWA_PARAM_CTLR_MODEL           8       /* Controller model [16] */
+
+#define TWA_PARAM_CONTROLLER_TABLE     0x0403
+#define TWA_PARAM_CONTROLLER_PORT_COUNT        3       /* number of ports [1] */
+
+#define TWA_PARAM_TIME_TABLE           0x40A
+#define TWA_PARAM_TIME_SCHED_TIME      0x3
+
+#define TWA_9K_PARAM_DESCRIPTOR                0x8000
+
+
+#pragma pack(1)
+/* 7000 structures. */
+struct tw_cl_command_init_connect {
+       TW_UINT8        res1__opcode;   /* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        res2;
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       message_credits;
+       TW_UINT32       features;
+       TW_UINT16       fw_srl;
+       TW_UINT16       fw_arch_id;
+       TW_UINT16       fw_branch;
+       TW_UINT16       fw_build;
+       TW_UINT32       result;
+};
+
+
+/* Structure for downloading firmware onto the controller. */
+struct tw_cl_command_download_firmware {
+       TW_UINT8        sgl_off__opcode;/* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        unit;
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       param;
+       TW_UINT8        sgl[1];
+};
+
+
+/* Structure for hard resetting the controller. */
+struct tw_cl_command_reset_firmware {
+       TW_UINT8        res1__opcode;   /* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        unit;
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT8        res2;
+       TW_UINT8        param;
+};
+
+
+/* Structure for sending get/set param commands. */
+struct tw_cl_command_param {
+       TW_UINT8        sgl_off__opcode;/* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        host_id__unit;  /* 4:4 */
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       param_count;
+       TW_UINT8        sgl[1];
+};
+
+
+/* Generic command packet. */
+struct tw_cl_command_generic {
+       TW_UINT8        sgl_off__opcode;/* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        host_id__unit;  /* 4:4 */
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       count;  /* block cnt, parameter cnt, message credits */
+};
+
+
+/* Command packet header. */
+struct tw_cl_command_header {
+       TW_UINT8        sense_data[TWA_SENSE_DATA_LENGTH];
+       struct {
+               TW_INT8         reserved[4];
+               TW_UINT16       error;
+               TW_UINT8        padding;
+               TW_UINT8        res__severity;  /* 5:3 */
+       } status_block;
+       TW_UINT8        err_specific_desc[98];
+       struct {
+               TW_UINT8        size_header;
+               TW_UINT16       reserved;
+               TW_UINT8        size_sense;
+       } header_desc;
+};
+
+
+/* 7000 Command packet. */
+union tw_cl_command_7k {
+       struct tw_cl_command_init_connect       init_connect;
+       struct tw_cl_command_download_firmware  download_fw;
+       struct tw_cl_command_reset_firmware     reset_fw;
+       struct tw_cl_command_param              param;
+       struct tw_cl_command_generic            generic;
+       TW_UINT8        padding[1024 - sizeof(struct tw_cl_command_header)];
+};
+
+
+/* 9000 Command Packet. */
+struct tw_cl_command_9k {
+       TW_UINT8        res__opcode;    /* 3:5 */
+       TW_UINT8        unit;
+       TW_UINT16       lun_l4__req_id; /* 4:12 */
+       TW_UINT8        status;
+       TW_UINT8        sgl_offset; /* offset (in bytes) to sg_list, from the
+                                       end of sgl_entries */
+       TW_UINT16       lun_h4__sgl_entries;
+       TW_UINT8        cdb[16];
+       TW_UINT8        sg_list[872];/* total struct size =
+                                       1024-sizeof(cmd_hdr) */
+};
+
+
+/* Full command packet. */
+struct tw_cl_command_packet {
+       struct tw_cl_command_header     cmd_hdr;
+       union {
+               union tw_cl_command_7k  cmd_pkt_7k;
+               struct tw_cl_command_9k cmd_pkt_9k;
+       } command;
+};
+
+
+/* Structure describing payload for get/set param commands. */
+struct tw_cl_param_9k {
+       TW_UINT16       table_id;
+       TW_UINT8        parameter_id;
+       TW_UINT8        reserved;
+       TW_UINT16       parameter_size_bytes;
+       TW_UINT16       parameter_actual_size_bytes;
+       TW_UINT8        data[1];
+};
+#pragma pack()
+
+
+/* Functions to read from, and write to registers */
+#define TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, value)              \
+       tw_osl_write_reg(ctlr_handle, TWA_CONTROL_REGISTER_OFFSET, value, 4)
+
+
+#define TW_CLI_READ_STATUS_REGISTER(ctlr_handle)                       \
+       tw_osl_read_reg(ctlr_handle, TWA_STATUS_REGISTER_OFFSET, 4)
+
+
+#define TW_CLI_WRITE_COMMAND_QUEUE(ctlr_handle, value) do {            \
+       if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {                      \
+               /* First write the low 4 bytes, then the high 4. */     \
+               tw_osl_write_reg(ctlr_handle, TWA_COMMAND_QUEUE_OFFSET_LOW, \
+                       (TW_UINT32)(value), 4);                         \
+               tw_osl_write_reg(ctlr_handle, TWA_COMMAND_QUEUE_OFFSET_HIGH,\
+                       (TW_UINT32)(((TW_UINT64)value)>>32), 4);        \
+       } else                                                          \
+               tw_osl_write_reg(ctlr_handle, TWA_COMMAND_QUEUE_OFFSET, \
+                                       (TW_UINT32)(value), 4);         \
+} while (0)
+
+
+#define TW_CLI_READ_RESPONSE_QUEUE(ctlr_handle)                                \
+       tw_osl_read_reg(ctlr_handle, TWA_RESPONSE_QUEUE_OFFSET, 4)
+
+
+#define TW_CLI_READ_LARGE_RESPONSE_QUEUE(ctlr_handle)                  \
+       tw_osl_read_reg(ctlr_handle, TWA_LARGE_RESPONSE_QUEUE_OFFSET, 4)
+
+
+#define TW_CLI_SOFT_RESET(ctlr)                                        \
+       TW_CLI_WRITE_CONTROL_REGISTER(ctlr,                     \
+               TWA_CONTROL_ISSUE_SOFT_RESET |                  \
+               TWA_CONTROL_CLEAR_HOST_INTERRUPT |              \
+               TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT |         \
+               TWA_CONTROL_MASK_COMMAND_INTERRUPT |            \
+               TWA_CONTROL_MASK_RESPONSE_INTERRUPT |           \
+               TWA_CONTROL_DISABLE_INTERRUPTS)
+
+/* Detect inconsistencies in the status register. */
+#define TW_CLI_STATUS_ERRORS(x)                                        \
+       ((x & TWA_STATUS_UNEXPECTED_BITS) &&                    \
+        (x & TWA_STATUS_MICROCONTROLLER_READY))
+
+
+/*
+ * Functions for making transparent, the bit fields in firmware
+ * interface structures.
+ */
+#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 BUILD_HOST_ID__UNIT(host_id, unit)     \
+       ((host_id << 4) & 0xF0) | (unit & 0xF)          /* 4:4 */
+
+#define BUILD_RES__SEVERITY(res, severity)     \
+       ((res << 3) & 0xF8) | (severity & 0x7)          /* 5:3 */
+
+#define BUILD_LUN_L4__REQ_ID(lun, req_id)      \
+       (((lun << 12) & 0xF000) | (req_id & 0xFFF))     /* 4:12 */
+
+#define BUILD_LUN_H4__SGL_ENTRIES(lun, sgl_entries)    \
+       (((lun << 8) & 0xF000) | (sgl_entries & 0xFFF)) /* 4:12 */
+
+
+#define GET_OPCODE(sgl_off__opcode)    \
+       (sgl_off__opcode & 0x1F)                /* 3:5 */
+
+#define GET_SGL_OFF(sgl_off__opcode)   \
+       ((sgl_off__opcode >> 5) & 0x7)          /* 3:5 */
+
+#define GET_UNIT(host_id__unit)                \
+       (host_id__unit & 0xF)                   /* 4:4 */
+
+#define GET_HOST_ID(host_id__unit)     \
+       ((host_id__unit >> 4) & 0xF)            /* 4:4 */
+
+#define GET_SEVERITY(res__severity)    \
+       (res__severity & 0x7)                   /* 5:3 */
+
+#define GET_RESP_ID(undef2__resp_id__undef1)   \
+       ((undef2__resp_id__undef1 >> 4) & 0xFF) /* 20:8:4 */
+
+#define GET_RESP_ID_9K_X(undef2__resp_id)      \
+       ((undef2__resp_id) & 0xFFF)             /* 20:12 */
+
+#define GET_LARGE_RESP_ID(misc__large_resp_id) \
+       ((misc__large_resp_id) & 0xFFFF)        /* 16:16 */
+
+#define GET_REQ_ID(lun_l4__req_id)     \
+       (lun_l4__req_id & 0xFFF)                /* 4:12 */
+
+#define GET_LUN_L4(lun_l4__req_id)     \
+       ((lun_l4__req_id >> 12) & 0xF)          /* 4:12 */
+
+#define GET_SGL_ENTRIES(lun_h4__sgl_entries)   \
+       (lun_h4__sgl_entries & 0xFFF)           /* 4:12 */
+
+#define GET_LUN_H4(lun_h4__sgl_entries)        \
+       ((lun_h4__sgl_entries >> 12) & 0xF)     /* 4:12 */
+
+
+
+#endif /* TW_CL_FWIF_H */
diff --git a/sys/dev/raid/twa/tw_cl_init.c b/sys/dev/raid/twa/tw_cl_init.c
new file mode 100644 (file)
index 0000000..3a8c2a1
--- /dev/null
@@ -0,0 +1,708 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_init.c,v 1.6 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+/*
+ * Common Layer initialization functions.
+ */
+
+
+#include "tw_osl_share.h"
+#include "tw_cl_share.h"
+#include "tw_cl_fwif.h"
+#include "tw_cl_ioctl.h"
+#include "tw_cl.h"
+#include "tw_cl_externs.h"
+#include "tw_osl_ioctl.h"
+
+
+/*
+ * Function name:      tw_cl_ctlr_supported
+ * Description:                Determines if a controller is supported.
+ *
+ * Input:              vendor_id -- vendor id of the controller
+ *                     device_id -- device id of the controller
+ * Output:             None
+ * Return value:       TW_CL_TRUE-- controller supported
+ *                     TW_CL_FALSE-- controller not supported
+ */
+TW_INT32
+tw_cl_ctlr_supported(TW_INT32 vendor_id, TW_INT32 device_id)
+{
+       if ((vendor_id == TW_CL_VENDOR_ID) &&
+               ((device_id == TW_CL_DEVICE_ID_9K) ||
+                (device_id == TW_CL_DEVICE_ID_9K_X) ||
+                (device_id == TW_CL_DEVICE_ID_9K_E) ||
+                (device_id == TW_CL_DEVICE_ID_9K_SA)))
+               return(TW_CL_TRUE);
+       return(TW_CL_FALSE);
+}
+
+
+
+/*
+ * Function name:      tw_cl_get_pci_bar_info
+ * Description:                Returns PCI BAR info.
+ *
+ * Input:              device_id -- device id of the controller
+ *                     bar_type -- type of PCI BAR in question
+ * Output:             bar_num -- PCI BAR number corresponding to bar_type
+ *                     bar0_offset -- byte offset from BAR 0 (0x10 in
+ *                                     PCI config space)
+ *                     bar_size -- size, in bytes, of the BAR in question
+ * Return value:       0 -- success
+ *                     non-zero -- failure
+ */
+TW_INT32
+tw_cl_get_pci_bar_info(TW_INT32 device_id, TW_INT32 bar_type,
+       TW_INT32 *bar_num, TW_INT32 *bar0_offset, TW_INT32 *bar_size)
+{
+       TW_INT32        error = TW_OSL_ESUCCESS;
+
+       switch(device_id) {
+       case TW_CL_DEVICE_ID_9K:
+               switch(bar_type) {
+               case TW_CL_BAR_TYPE_IO:
+                       *bar_num = 0;
+                       *bar0_offset = 0;
+                       *bar_size = 4;
+                       break;
+
+               case TW_CL_BAR_TYPE_MEM:
+                       *bar_num = 1;
+                       *bar0_offset = 0x4;
+                       *bar_size = 8;
+                       break;
+
+               case TW_CL_BAR_TYPE_SBUF:
+                       *bar_num = 2;
+                       *bar0_offset = 0xC;
+                       *bar_size = 8;
+                       break;
+               }
+               break;
+
+       case TW_CL_DEVICE_ID_9K_X:
+       case TW_CL_DEVICE_ID_9K_E:
+       case TW_CL_DEVICE_ID_9K_SA:
+               switch(bar_type) {
+               case TW_CL_BAR_TYPE_IO:
+                       *bar_num = 2;
+                       *bar0_offset = 0x10;
+                       *bar_size = 4;
+                       break;
+
+               case TW_CL_BAR_TYPE_MEM:
+                       *bar_num = 1;
+                       *bar0_offset = 0x8;
+                       *bar_size = 8;
+                       break;
+
+               case TW_CL_BAR_TYPE_SBUF:
+                       *bar_num = 0;
+                       *bar0_offset = 0;
+                       *bar_size = 8;
+                       break;
+               }
+               break;
+
+       default:
+               error = TW_OSL_ENOTTY;
+               break;
+       }
+
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cl_get_mem_requirements
+ * Description:                Provides info about Common Layer requirements for a
+ *                     controller, given the controller type (in 'flags').
+ * Input:              ctlr_handle -- controller handle
+ *                     flags -- more info passed by the OS Layer
+ *                     device_id -- device id of the controller
+ *                     max_simult_reqs -- maximum # of simultaneous
+ *                                     requests that the OS Layer expects
+ *                                     the Common Layer to support
+ *                     max_aens -- maximun # of AEN's needed to be supported
+ * Output:             alignment -- alignment needed for all DMA'able
+ *                                     buffers
+ *                     sg_size_factor -- every SG element should have a size
+ *                                     that's a multiple of this number
+ *                     non_dma_mem_size -- # of bytes of memory needed for
+ *                                     non-DMA purposes
+ *                     dma_mem_size -- # of bytes of DMA'able memory needed
+ *                     per_req_dma_mem_size -- # of bytes of DMA'able memory
+ *                                     needed per request, if applicable
+ *                     per_req_non_dma_mem_size -- # of bytes of memory needed
+ *                                     per request for non-DMA purposes,
+ *                                     if applicable
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_get_mem_requirements(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_UINT32 flags, TW_INT32 device_id, TW_INT32 max_simult_reqs,
+       TW_INT32 max_aens, TW_UINT32 *alignment, TW_UINT32 *sg_size_factor,
+       TW_UINT32 *non_dma_mem_size, TW_UINT32 *dma_mem_size
+       )
+{
+       if (device_id == 0)
+               device_id = TW_CL_DEVICE_ID_9K;
+
+       if (max_simult_reqs > TW_CL_MAX_SIMULTANEOUS_REQUESTS) {
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1000, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Too many simultaneous requests to support!",
+                       "requested = %d, supported = %d, error = %d\n",
+                       max_simult_reqs, TW_CL_MAX_SIMULTANEOUS_REQUESTS,
+                       TW_OSL_EBIG);
+               return(TW_OSL_EBIG);
+       }
+
+       *alignment = TWA_ALIGNMENT(device_id);
+       *sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR(device_id);
+
+       /*
+        * Total non-DMA memory needed is the sum total of memory needed for
+        * the controller context, request packets (including the 1 needed for
+        * CL internal requests), and event packets.
+        */
+
+       *non_dma_mem_size = sizeof(struct tw_cli_ctlr_context) +
+               (sizeof(struct tw_cli_req_context) * max_simult_reqs) +
+               (sizeof(struct tw_cl_event_packet) * max_aens);
+
+
+       /*
+        * Total DMA'able memory needed is the sum total of memory needed for
+        * all command packets (including the 1 needed for CL internal
+        * requests), and memory needed to hold the payload for internal
+        * requests.
+        */
+
+       *dma_mem_size = (sizeof(struct tw_cl_command_packet) *
+               (max_simult_reqs)) + (TW_CLI_SECTOR_SIZE);
+
+       return(0);
+}
+
+
+
+/*
+ * Function name:      tw_cl_init_ctlr
+ * Description:                Initializes driver data structures for the controller.
+ *
+ * Input:              ctlr_handle -- controller handle
+ *                     flags -- more info passed by the OS Layer
+ *                     device_id -- device id of the controller
+ *                     max_simult_reqs -- maximum # of simultaneous requests
+ *                                     that the OS Layer expects the Common
+ *                                     Layer to support
+ *                     max_aens -- maximun # of AEN's needed to be supported
+ *                     non_dma_mem -- ptr to allocated non-DMA memory
+ *                     dma_mem -- ptr to allocated DMA'able memory
+ *                     dma_mem_phys -- physical address of dma_mem
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_init_ctlr(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags,
+       TW_INT32 device_id, TW_INT32 max_simult_reqs, TW_INT32 max_aens,
+       TW_VOID *non_dma_mem, TW_VOID *dma_mem, TW_UINT64 dma_mem_phys
+       )
+{
+       struct tw_cli_ctlr_context      *ctlr;
+       struct tw_cli_req_context       *req;
+       TW_UINT8                        *free_non_dma_mem;
+       TW_INT32                        error = TW_OSL_ESUCCESS;
+       TW_INT32                        i;
+
+       tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       if (flags & TW_CL_START_CTLR_ONLY) {
+               ctlr = (struct tw_cli_ctlr_context *)
+                       (ctlr_handle->cl_ctlr_ctxt);
+               goto start_ctlr;
+       }
+
+       if (max_simult_reqs > TW_CL_MAX_SIMULTANEOUS_REQUESTS) {
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1000, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Too many simultaneous requests to support!",
+                       "requested = %d, supported = %d, error = %d\n",
+                       max_simult_reqs, TW_CL_MAX_SIMULTANEOUS_REQUESTS,
+                       TW_OSL_EBIG);
+               return(TW_OSL_EBIG);
+       }
+
+       if ((non_dma_mem == TW_CL_NULL) || (dma_mem == TW_CL_NULL)
+               ) {
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1001, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Insufficient memory for Common Layer's internal usage",
+                       "error = %d\n", TW_OSL_ENOMEM);
+               return(TW_OSL_ENOMEM);
+       }
+
+       tw_osl_memzero(non_dma_mem, sizeof(struct tw_cli_ctlr_context) +
+               (sizeof(struct tw_cli_req_context) * max_simult_reqs) +
+               (sizeof(struct tw_cl_event_packet) * max_aens));
+
+       tw_osl_memzero(dma_mem,
+               (sizeof(struct tw_cl_command_packet) *
+               max_simult_reqs) +
+               TW_CLI_SECTOR_SIZE);
+
+       free_non_dma_mem = (TW_UINT8 *)non_dma_mem;
+
+       ctlr = (struct tw_cli_ctlr_context *)free_non_dma_mem;
+       free_non_dma_mem += sizeof(struct tw_cli_ctlr_context);
+
+       ctlr_handle->cl_ctlr_ctxt = ctlr;
+       ctlr->ctlr_handle = ctlr_handle;
+
+       ctlr->device_id = (TW_UINT32)device_id;
+       ctlr->arch_id = TWA_ARCH_ID(device_id);
+       ctlr->flags = flags;
+       ctlr->sg_size_factor = TWA_SG_ELEMENT_SIZE_FACTOR(device_id);
+       ctlr->max_simult_reqs = max_simult_reqs;
+       ctlr->max_aens_supported = max_aens;
+
+       /* Initialize queues of CL internal request context packets. */
+       tw_cli_req_q_init(ctlr, TW_CLI_FREE_Q);
+       tw_cli_req_q_init(ctlr, TW_CLI_BUSY_Q);
+       tw_cli_req_q_init(ctlr, TW_CLI_PENDING_Q);
+       tw_cli_req_q_init(ctlr, TW_CLI_COMPLETE_Q);
+
+       /* Initialize all locks used by CL. */
+       ctlr->gen_lock = &(ctlr->gen_lock_handle);
+       tw_osl_init_lock(ctlr_handle, "tw_cl_gen_lock", ctlr->gen_lock);
+       ctlr->io_lock = &(ctlr->io_lock_handle);
+       tw_osl_init_lock(ctlr_handle, "tw_cl_io_lock", ctlr->io_lock);
+
+       /* Initialize CL internal request context packets. */
+       ctlr->req_ctxt_buf = (struct tw_cli_req_context *)free_non_dma_mem;
+       free_non_dma_mem += (sizeof(struct tw_cli_req_context) *
+               max_simult_reqs);
+
+       ctlr->cmd_pkt_buf = (struct tw_cl_command_packet *)dma_mem;
+       ctlr->cmd_pkt_phys = dma_mem_phys;
+
+       ctlr->internal_req_data = (TW_UINT8 *)
+               (ctlr->cmd_pkt_buf +
+               max_simult_reqs);
+       ctlr->internal_req_data_phys = ctlr->cmd_pkt_phys +
+               (sizeof(struct tw_cl_command_packet) *
+               max_simult_reqs);
+
+       for (i = 0; i < max_simult_reqs; i++) {
+               req = &(ctlr->req_ctxt_buf[i]);
+
+               req->cmd_pkt = &(ctlr->cmd_pkt_buf[i]);
+               req->cmd_pkt_phys = ctlr->cmd_pkt_phys +
+                       (i * sizeof(struct tw_cl_command_packet));
+
+               req->request_id = i;
+               req->ctlr = ctlr;
+
+               /* Insert request into the free queue. */
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+
+       /* Initialize the AEN queue. */
+       ctlr->aen_queue = (struct tw_cl_event_packet *)free_non_dma_mem;
+
+
+start_ctlr:
+       /*
+        * Disable interrupts.  Interrupts will be enabled in tw_cli_start_ctlr
+        * (only) if initialization succeeded.
+        */
+       tw_cli_disable_interrupts(ctlr);
+
+       /* Initialize the controller. */
+       if ((error = tw_cli_start_ctlr(ctlr))) {
+               /* Soft reset the controller, and try one more time. */
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1002, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Controller initialization failed. Retrying...",
+                       "error = %d\n", error);
+               if ((error = tw_cli_soft_reset(ctlr))) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1003, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Controller soft reset failed",
+                               "error = %d\n", error);
+                       return(error);
+               } else if ((error = tw_cli_start_ctlr(ctlr))) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1004, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Controller initialization retry failed",
+                               "error = %d\n", error);
+                       return(error);
+               }
+       }
+       /* Notify some info about the controller to the OSL. */
+       tw_cli_notify_ctlr_info(ctlr);
+
+       /* Mark the controller active. */
+       ctlr->active = TW_CL_TRUE;
+       return(error);
+}
+
+/*
+ * Function name:      tw_cli_start_ctlr
+ * Description:                Establishes a logical connection with the controller.
+ *                     Determines whether or not the driver is compatible
+ *                      with the firmware on the controller, before proceeding
+ *                      to work with it.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_start_ctlr(struct tw_cli_ctlr_context *ctlr)
+{
+       TW_UINT16       fw_on_ctlr_srl = 0;
+       TW_UINT16       fw_on_ctlr_arch_id = 0;
+       TW_UINT16       fw_on_ctlr_branch = 0;
+       TW_UINT16       fw_on_ctlr_build = 0;
+       TW_UINT32       init_connect_result = 0;
+       TW_INT32        error = TW_OSL_ESUCCESS;
+
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Wait for the controller to become ready. */
+       if ((error = tw_cli_poll_status(ctlr,
+                       TWA_STATUS_MICROCONTROLLER_READY,
+                       TW_CLI_REQUEST_TIMEOUT_PERIOD))) {
+               tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1009, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Microcontroller not ready",
+                       "error = %d", error);
+               return(error);
+       }
+       /* Drain the response queue. */
+       if ((error = tw_cli_drain_response_queue(ctlr))) {
+               tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x100A, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Can't drain response queue",
+                       "error = %d", error);
+               return(error);
+       }
+       /* Establish a logical connection with the controller. */
+       if ((error = tw_cli_init_connection(ctlr,
+                       (TW_UINT16)(ctlr->max_simult_reqs),
+                       TWA_EXTENDED_INIT_CONNECT, TWA_CURRENT_FW_SRL,
+                       (TW_UINT16)(ctlr->arch_id),
+                       TWA_CURRENT_FW_BRANCH(ctlr->arch_id),
+                       TWA_CURRENT_FW_BUILD(ctlr->arch_id),
+                       &fw_on_ctlr_srl, &fw_on_ctlr_arch_id,
+                       &fw_on_ctlr_branch, &fw_on_ctlr_build,
+                       &init_connect_result))) {
+               tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x100B, 0x2, TW_CL_SEVERITY_WARNING_STRING,
+                       "Can't initialize connection in current mode",
+                       "error = %d", error);
+               return(error);
+       }
+       {
+                /* See if we can at least work with the firmware on the
+                 * controller in the current mode.
+                */
+               if (init_connect_result & TWA_CTLR_FW_COMPATIBLE) {
+                       /* Yes, we can.  Make note of the operating mode. */
+                       if (init_connect_result & TWA_CTLR_FW_SAME_OR_NEWER) {
+                               ctlr->working_srl = TWA_CURRENT_FW_SRL;
+                               ctlr->working_branch =
+                                       TWA_CURRENT_FW_BRANCH(ctlr->arch_id);
+                               ctlr->working_build =
+                                       TWA_CURRENT_FW_BUILD(ctlr->arch_id);
+                       } else {
+                               ctlr->working_srl = fw_on_ctlr_srl;
+                               ctlr->working_branch = fw_on_ctlr_branch;
+                               ctlr->working_build = fw_on_ctlr_build;
+                       }
+               } else {
+                       /*
+                        * No, we can't.  See if we can at least work with
+                        * it in the base mode.
+                        */
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1010, 0x2, TW_CL_SEVERITY_WARNING_STRING,
+                               "Driver/Firmware mismatch. "
+                               "Negotiating for base level...",
+                               " ");
+                       if ((error = tw_cli_init_connection(ctlr,
+                                       (TW_UINT16)(ctlr->max_simult_reqs),
+                                       TWA_EXTENDED_INIT_CONNECT,
+                                       TWA_BASE_FW_SRL,
+                                       (TW_UINT16)(ctlr->arch_id),
+                                       TWA_BASE_FW_BRANCH, TWA_BASE_FW_BUILD,
+                                       &fw_on_ctlr_srl, &fw_on_ctlr_arch_id,
+                                       &fw_on_ctlr_branch, &fw_on_ctlr_build,
+                                       &init_connect_result))) {
+                               tw_cl_create_event(ctlr->ctlr_handle,
+                                       TW_CL_FALSE,
+                                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                                       0x1011, 0x1,
+                                       TW_CL_SEVERITY_ERROR_STRING,
+                                       "Can't initialize connection in "
+                                       "base mode",
+                                       " ");
+                               return(error);
+                       }
+                       if (!(init_connect_result & TWA_CTLR_FW_COMPATIBLE)) {
+                               /*
+                                * The firmware on the controller is not even
+                                * compatible with our base mode.  We cannot
+                                * work with it.  Bail...
+                                */
+                               return(1);
+                       }
+                       /*
+                        * We can work with this firmware, but only in
+                        * base mode.
+                        */
+                       ctlr->working_srl = TWA_BASE_FW_SRL;
+                       ctlr->working_branch = TWA_BASE_FW_BRANCH;
+                       ctlr->working_build = TWA_BASE_FW_BUILD;
+                       ctlr->operating_mode = TWA_BASE_MODE;
+               }
+               ctlr->fw_on_ctlr_srl = fw_on_ctlr_srl;
+               ctlr->fw_on_ctlr_branch = fw_on_ctlr_branch;
+               ctlr->fw_on_ctlr_build = fw_on_ctlr_build;
+       }
+
+       /* Drain the AEN queue */
+       if ((error = tw_cli_drain_aen_queue(ctlr)))
+               /*
+                * We will just print that we couldn't drain the AEN queue.
+                * There's no need to bail out.
+                */
+               tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1014, 0x2, TW_CL_SEVERITY_WARNING_STRING,
+                       "Can't drain AEN queue",
+                       "error = %d", error);
+
+       /* Enable interrupts. */
+       tw_cli_enable_interrupts(ctlr);
+
+       return(TW_OSL_ESUCCESS);
+}
+
+
+/*
+ * Function name:      tw_cl_shutdown_ctlr
+ * Description:                Closes logical connection with the controller.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ *                     flags   -- more info passed by the OS Layer
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_shutdown_ctlr(struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags)
+{
+       struct tw_cli_ctlr_context      *ctlr =
+               (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(), "entered");
+       /*
+        * Mark the controller as inactive, disable any further interrupts,
+        * and notify the controller that we are going down.
+        */
+       ctlr->active = TW_CL_FALSE;
+
+       tw_cli_disable_interrupts(ctlr);
+
+       /* Let the controller know that we are going down. */
+       if ((error = tw_cli_init_connection(ctlr, TWA_SHUTDOWN_MESSAGE_CREDITS,
+                       0, 0, 0, 0, 0, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL,
+                       TW_CL_NULL, TW_CL_NULL)))
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1015, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Can't close connection with controller",
+                       "error = %d", error);
+
+       if (flags & TW_CL_STOP_CTLR_ONLY)
+               goto ret;
+
+       /* Destroy all locks used by CL. */
+       tw_osl_destroy_lock(ctlr_handle, ctlr->gen_lock);
+       tw_osl_destroy_lock(ctlr_handle, ctlr->io_lock);
+
+ret:
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_init_connection
+ * Description:                Sends init_connection cmd to firmware
+ *
+ * Input:              ctlr            -- ptr to per ctlr structure
+ *                     message_credits -- max # of requests that we might send
+ *                                      down simultaneously.  This will be
+ *                                      typically set to 256 at init-time or
+ *                                     after a reset, and to 1 at shutdown-time
+ *                     set_features    -- indicates if we intend to use 64-bit
+ *                                     sg, also indicates if we want to do a
+ *                                     basic or an extended init_connection;
+ *
+ * Note: The following input/output parameters are valid, only in case of an
+ *             extended init_connection:
+ *
+ *                     current_fw_srl          -- srl of fw we are bundled
+ *                                             with, if any; 0 otherwise
+ *                     current_fw_arch_id      -- arch_id of fw we are bundled
+ *                                             with, if any; 0 otherwise
+ *                     current_fw_branch       -- branch # of fw we are bundled
+ *                                             with, if any; 0 otherwise
+ *                     current_fw_build        -- build # of fw we are bundled
+ *                                             with, if any; 0 otherwise
+ * Output:             fw_on_ctlr_srl          -- srl of fw on ctlr
+ *                     fw_on_ctlr_arch_id      -- arch_id of fw on ctlr
+ *                     fw_on_ctlr_branch       -- branch # of fw on ctlr
+ *                     fw_on_ctlr_build        -- build # of fw on ctlr
+ *                     init_connect_result     -- result bitmap of fw response
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_init_connection(struct tw_cli_ctlr_context *ctlr,
+       TW_UINT16 message_credits, TW_UINT32 set_features,
+       TW_UINT16 current_fw_srl, TW_UINT16 current_fw_arch_id,
+       TW_UINT16 current_fw_branch, TW_UINT16 current_fw_build,
+       TW_UINT16 *fw_on_ctlr_srl, TW_UINT16 *fw_on_ctlr_arch_id,
+       TW_UINT16 *fw_on_ctlr_branch, TW_UINT16 *fw_on_ctlr_build,
+       TW_UINT32 *init_connect_result)
+{
+       struct tw_cli_req_context               *req;
+       struct tw_cl_command_init_connect       *init_connect;
+       TW_INT32                                error = TW_OSL_EBUSY;
+
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Get a request packet. */
+       if ((req = tw_cli_get_request(ctlr
+               )) == TW_CL_NULL)
+               goto out;
+
+       req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
+
+       /* Build the cmd pkt. */
+       init_connect = &(req->cmd_pkt->command.cmd_pkt_7k.init_connect);
+
+       req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
+
+       init_connect->res1__opcode =
+               BUILD_RES__OPCODE(0, TWA_FW_CMD_INIT_CONNECTION);
+       init_connect->request_id =
+               (TW_UINT8)(TW_CL_SWAP16(req->request_id));
+       init_connect->message_credits = TW_CL_SWAP16(message_credits);
+       init_connect->features = TW_CL_SWAP32(set_features);
+       if (ctlr->flags & TW_CL_64BIT_ADDRESSES)
+               init_connect->features |= TW_CL_SWAP32(TWA_64BIT_SG_ADDRESSES);
+       if (set_features & TWA_EXTENDED_INIT_CONNECT) {
+               /*
+                * Fill in the extra fields needed for an extended
+                * init_connect.
+                */
+               init_connect->size = 6;
+               init_connect->fw_srl = TW_CL_SWAP16(current_fw_srl);
+               init_connect->fw_arch_id = TW_CL_SWAP16(current_fw_arch_id);
+               init_connect->fw_branch = TW_CL_SWAP16(current_fw_branch);
+               init_connect->fw_build = TW_CL_SWAP16(current_fw_build);
+       } else
+               init_connect->size = 3;
+
+       /* Submit the command, and wait for it to complete. */
+       error = tw_cli_submit_and_poll_request(req,
+               TW_CLI_REQUEST_TIMEOUT_PERIOD);
+       if (error == TW_OSL_ETIMEDOUT)
+               /* Clean-up done by tw_cli_submit_and_poll_request. */
+               return(error);
+       if (error)
+               goto out;
+       if ((error = init_connect->status)) {
+               tw_cli_create_ctlr_event(ctlr,
+                       TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                       &(req->cmd_pkt->cmd_hdr));
+               goto out;
+       }
+       if (set_features & TWA_EXTENDED_INIT_CONNECT) {
+               *fw_on_ctlr_srl = TW_CL_SWAP16(init_connect->fw_srl);
+               *fw_on_ctlr_arch_id = TW_CL_SWAP16(init_connect->fw_arch_id);
+               *fw_on_ctlr_branch = TW_CL_SWAP16(init_connect->fw_branch);
+               *fw_on_ctlr_build = TW_CL_SWAP16(init_connect->fw_build);
+               *init_connect_result = TW_CL_SWAP32(init_connect->result);
+       }
+       tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       return(error);
+
+out:
+       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+               0x1016, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+               "init_connection failed",
+               "error = %d", error);
+       if (req)
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       return(error);
+}
diff --git a/sys/dev/raid/twa/tw_cl_intr.c b/sys/dev/raid/twa/tw_cl_intr.c
new file mode 100644 (file)
index 0000000..8f0cee9
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_intr.c,v 1.5 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+/*
+ * Common Layer interrupt handling functions.
+ */
+
+
+#include "tw_osl_share.h"
+#include "tw_cl_share.h"
+#include "tw_cl_fwif.h"
+#include "tw_cl_ioctl.h"
+#include "tw_cl.h"
+#include "tw_cl_externs.h"
+#include "tw_osl_ioctl.h"
+
+
+
+/*
+ * Function name:      twa_interrupt
+ * Description:                Interrupt handler.  Determines the kind of interrupt,
+ *                     and returns TW_CL_TRUE if it recognizes the interrupt.
+ *
+ * Input:              ctlr_handle     -- controller handle
+ * Output:             None
+ * Return value:       TW_CL_TRUE -- interrupt recognized
+ *                     TW_CL_FALSE-- interrupt not recognized
+ */
+TW_INT32
+tw_cl_interrupt(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+       struct tw_cli_ctlr_context      *ctlr =
+               (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+       TW_UINT32                       status_reg;
+       TW_INT32                        rc = TW_CL_FALSE;
+
+       tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* If we don't have controller context, bail */
+       if (ctlr == NULL)
+               goto out;
+
+       /*
+        * Bail If we get an interrupt while resetting, or shutting down.
+        */
+       if (ctlr->reset_in_progress || !(ctlr->active))
+               goto out;
+
+       /* Read the status register to determine the type of interrupt. */
+       status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
+       if (tw_cli_check_ctlr_state(ctlr, status_reg))
+               goto out;
+
+       /* Clear the interrupt. */
+       if (status_reg & TWA_STATUS_HOST_INTERRUPT) {
+               tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+                       "Host interrupt");
+               TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+                       TWA_CONTROL_CLEAR_HOST_INTERRUPT);
+       }
+       if (status_reg & TWA_STATUS_ATTENTION_INTERRUPT) {
+               tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+                       "Attention interrupt");
+               rc |= TW_CL_TRUE; /* request for a deferred isr call */
+               tw_cli_process_attn_intr(ctlr);
+               TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+                       TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT);
+       }
+       if (status_reg & TWA_STATUS_COMMAND_INTERRUPT) {
+               tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+                       "Command interrupt");
+               rc |= TW_CL_TRUE; /* request for a deferred isr call */
+               tw_cli_process_cmd_intr(ctlr);
+               if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) == TW_CL_NULL)
+                       TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+                               TWA_CONTROL_MASK_COMMAND_INTERRUPT);
+       }
+       if (status_reg & TWA_STATUS_RESPONSE_INTERRUPT) {
+               tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
+                       "Response interrupt");
+               rc |= TW_CL_TRUE; /* request for a deferred isr call */
+               tw_cli_process_resp_intr(ctlr);
+       }
+out:
+       return(rc);
+}
+
+
+
+/*
+ * Function name:      tw_cli_process_host_intr
+ * Description:                This function gets called if we triggered an interrupt.
+ *                     We don't use it as of now.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_process_host_intr(struct tw_cli_ctlr_context *ctlr)
+{
+       tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+}
+
+
+
+/*
+ * Function name:      tw_cli_process_attn_intr
+ * Description:                This function gets called if the fw posted an AEN
+ *                     (Asynchronous Event Notification).  It fetches
+ *                     all the AEN's that the fw might have posted.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_process_attn_intr(struct tw_cli_ctlr_context *ctlr)
+{
+       TW_INT32        error;
+
+       tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       if ((error = tw_cli_get_aen(ctlr))) {
+               /*
+                * If the driver is already in the process of retrieveing AEN's,
+                * we will be returned TW_OSL_EBUSY.  In this case,
+                * tw_cli_param_callback or tw_cli_aen_callback will eventually
+                * retrieve the AEN this attention interrupt is for.  So, we
+                * don't need to print the failure.
+                */
+               if (error != TW_OSL_EBUSY)
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1200, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Failed to fetch AEN",
+                               "error = %d", error);
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_process_cmd_intr
+ * Description:                This function gets called if we hit a queue full
+ *                     condition earlier, and the fw is now ready for
+ *                     new cmds.  Submits any pending requests.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_process_cmd_intr(struct tw_cli_ctlr_context *ctlr)
+{
+       tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Start any requests that might be in the pending queue. */
+       tw_cli_submit_pending_queue(ctlr);
+
+       /*
+        * If tw_cli_submit_pending_queue was unsuccessful due to a "cmd queue
+        * full" condition, cmd_intr will already have been unmasked by
+        * tw_cli_submit_cmd.  We don't need to do it again... simply return.
+        */
+}
+
+
+
+/*
+ * Function name:      tw_cli_process_resp_intr
+ * Description:                Looks for cmd completions from fw; queues cmds completed
+ *                     by fw into complete queue.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       0       -- no ctlr error
+ *                     non-zero-- ctlr error
+ */
+TW_INT32
+tw_cli_process_resp_intr(struct tw_cli_ctlr_context *ctlr)
+{
+       TW_UINT32                       resp;
+       struct tw_cli_req_context       *req;
+       TW_INT32                        error;
+       TW_UINT32                       status_reg;
+
+       tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       for (;;) {
+               status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+               if ((error = tw_cli_check_ctlr_state(ctlr, status_reg)))
+                       break;
+               if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) {
+                       tw_cli_dbg_printf(7, ctlr->ctlr_handle,
+                               tw_osl_cur_func(), "Response queue empty");
+                       break;
+               }
+
+               /* Response queue is not empty. */
+               resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
+               {
+                       req = &(ctlr->req_ctxt_buf[GET_RESP_ID(resp)]);
+               }
+
+               if (req->state != TW_CLI_REQ_STATE_BUSY) {
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1201, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Unposted command completed!!",
+                               "request = %p, status = %d",
+                               req, req->state);
+#ifdef TW_OSL_DEBUG
+                       tw_cl_print_ctlr_stats(ctlr->ctlr_handle);
+#endif /* TW_OSL_DEBUG */
+                       tw_cl_reset_ctlr(ctlr->ctlr_handle);
+                       return(TW_OSL_EIO);
+               }
+
+               /*
+                * Remove the request from the busy queue, mark it as complete,
+                * and enqueue it in the complete queue.
+                */
+               tw_cli_req_q_remove_item(req, TW_CLI_BUSY_Q);
+               req->state = TW_CLI_REQ_STATE_COMPLETE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_COMPLETE_Q);
+
+       }
+
+       /* Complete this, and other requests in the complete queue. */
+       tw_cli_process_complete_queue(ctlr);
+
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_submit_pending_queue
+ * Description:                Kick starts any requests in the pending queue.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       0       -- all pending requests submitted successfully
+ *                     non-zero-- otherwise
+ */
+TW_INT32
+tw_cli_submit_pending_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+       TW_INT32                        error = TW_OSL_ESUCCESS;
+
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /*
+        * Pull requests off the pending queue, and submit them.
+        */
+       while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q)) !=
+               TW_CL_NULL) {
+               if ((error = tw_cli_submit_cmd(req))) {
+                       if (error == TW_OSL_EBUSY) {
+                               tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "Requeueing pending request");
+                               req->state = TW_CLI_REQ_STATE_PENDING;
+                               /*
+                                * Queue the request at the head of the pending
+                                * queue, and break away, so we don't try to
+                                * submit any more requests.
+                                */
+                               tw_cli_req_q_insert_head(req, TW_CLI_PENDING_Q);
+                               break;
+                       } else {
+                               tw_cl_create_event(ctlr->ctlr_handle,
+                                       TW_CL_FALSE,
+                                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                                       0x1202, 0x1,
+                                       TW_CL_SEVERITY_ERROR_STRING,
+                                       "Could not start request "
+                                       "in pending queue",
+                                       "request = %p, opcode = 0x%x, "
+                                       "error = %d", req,
+                                       GET_OPCODE(req->cmd_pkt->
+                                               command.cmd_pkt_9k.res__opcode),
+                                       error);
+                               /*
+                                * Set the appropriate error and call the CL
+                                * internal callback if there's one.  If the
+                                * request originator is polling for completion,
+                                * he should be checking req->error to
+                                * determine that the request did not go
+                                * through.  The request originators are
+                                * responsible for the clean-up.
+                                */
+                               req->error_code = error;
+                               req->state = TW_CLI_REQ_STATE_COMPLETE;
+                               if (req->tw_cli_callback)
+                                       req->tw_cli_callback(req);
+                               error = TW_OSL_ESUCCESS;
+                       }
+               }
+       }
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_process_complete_queue
+ * Description:                Calls the CL internal callback routine, if any, for
+ *                     each request in the complete queue.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_process_complete_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+
+       tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /*
+        * Pull commands off the completed list, dispatch them appropriately.
+        */
+       while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q)) !=
+               TW_CL_NULL) {
+               /* Call the CL internal callback, if there's one. */
+               if (req->tw_cli_callback)
+                       req->tw_cli_callback(req);
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_complete_io
+ * Description:                CL internal callback for SCSI/fw passthru requests.
+ *
+ * Input:              req     -- ptr to CL internal request context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_complete_io(struct tw_cli_req_context *req)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+       struct tw_cl_req_packet         *req_pkt =
+               (struct tw_cl_req_packet *)(req->orig_req);
+
+       tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       req_pkt->status = TW_CL_ERR_REQ_SUCCESS;
+       if (req->error_code) {
+               req_pkt->status = TW_CL_ERR_REQ_UNABLE_TO_SUBMIT_COMMAND;
+               goto out;
+       }
+
+       if (req->state != TW_CLI_REQ_STATE_COMPLETE) {
+               tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1203, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "I/O completion on incomplete command!!",
+                       "request = %p, status = %d",
+                       req, req->state);
+#ifdef TW_OSL_DEBUG
+               tw_cl_print_ctlr_stats(ctlr->ctlr_handle);
+#endif /* TW_OSL_DEBUG */
+               tw_cl_reset_ctlr(ctlr->ctlr_handle);
+               req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+               goto out;
+       }
+
+       if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) {
+               /* Copy the command packet back into OSL's space. */
+               tw_osl_memcpy(req_pkt->gen_req_pkt.pt_req.cmd_pkt, req->cmd_pkt,
+                       sizeof(struct tw_cl_command_packet));
+       } else
+               tw_cli_scsi_complete(req);
+
+out:
+       req_pkt->tw_osl_callback(req->req_handle);
+       tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+}
+
+
+
+/*
+ * Function name:      tw_cli_scsi_complete
+ * Description:                Completion routine for SCSI requests.
+ *
+ * Input:              req     -- ptr to CL internal request context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_scsi_complete(struct tw_cli_req_context *req)
+{
+       struct tw_cl_req_packet         *req_pkt =
+               (struct tw_cl_req_packet *)(req->orig_req);
+       struct tw_cl_scsi_req_packet    *scsi_req =
+               &(req_pkt->gen_req_pkt.scsi_req);
+       struct tw_cl_command_9k         *cmd =
+               &(req->cmd_pkt->command.cmd_pkt_9k);
+       struct tw_cl_command_header     *cmd_hdr;
+       TW_UINT16                       error;
+       TW_UINT8                        *cdb;
+
+       tw_cli_dbg_printf(8, req->ctlr->ctlr_handle, tw_osl_cur_func(),
+               "entered");
+
+       scsi_req->scsi_status = cmd->status;
+       if (! cmd->status)
+               return;
+
+       tw_cli_dbg_printf(1, req->ctlr->ctlr_handle, tw_osl_cur_func(),
+               "req_id = 0x%x, status = 0x%x",
+               GET_REQ_ID(cmd->lun_l4__req_id), cmd->status);
+
+       cmd_hdr = &(req->cmd_pkt->cmd_hdr);
+       error = cmd_hdr->status_block.error;
+       if ((error == TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) ||
+                       (error == TWA_ERROR_UNIT_OFFLINE)) {
+               if (GET_LUN_L4(cmd->lun_l4__req_id))
+                       req_pkt->status |= TW_CL_ERR_REQ_INVALID_LUN;
+               else
+                       req_pkt->status |= TW_CL_ERR_REQ_INVALID_TARGET;
+       } else {
+               tw_cli_dbg_printf(2, req->ctlr->ctlr_handle,
+                       tw_osl_cur_func(),
+                       "cmd = %x %x %x %x %x %x %x",
+                       GET_OPCODE(cmd->res__opcode),
+                       GET_SGL_OFF(cmd->res__opcode),
+                       cmd->unit,
+                       cmd->lun_l4__req_id,
+                       cmd->status,
+                       cmd->sgl_offset,
+                       cmd->lun_h4__sgl_entries);
+
+               cdb = (TW_UINT8 *)(cmd->cdb);
+               tw_cli_dbg_printf(2, req->ctlr->ctlr_handle,
+                       tw_osl_cur_func(),
+                       "cdb = %x %x %x %x %x %x %x %x "
+                       "%x %x %x %x %x %x %x %x",
+                       cdb[0], cdb[1], cdb[2], cdb[3],
+                       cdb[4], cdb[5], cdb[6], cdb[7],
+                       cdb[8], cdb[9], cdb[10], cdb[11],
+                       cdb[12], cdb[13], cdb[14], cdb[15]);
+
+               /*
+                * Print the error. Firmware doesn't yet support
+                * the 'Mode Sense' cmd.  Don't print if the cmd
+                * is 'Mode Sense', and the error is 'Invalid field
+                * in CDB'.
+                */
+               if (! ((cdb[0] == 0x1A) && (error == 0x10D)))
+                       tw_cli_create_ctlr_event(req->ctlr,
+                               TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                               cmd_hdr);
+       }
+
+       if (scsi_req->sense_data) {
+               tw_osl_memcpy(scsi_req->sense_data, cmd_hdr->sense_data,
+                       TWA_SENSE_DATA_LENGTH);
+               scsi_req->sense_len = TWA_SENSE_DATA_LENGTH;
+               req_pkt->status |= TW_CL_ERR_REQ_AUTO_SENSE_VALID;
+       }
+       req_pkt->status |= TW_CL_ERR_REQ_SCSI_ERROR;
+}
+
+
+
+/*
+ * Function name:      tw_cli_param_callback
+ * Description:                Callback for get/set_param requests.
+ *
+ * Input:              req     -- ptr to completed request pkt
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_param_callback(struct tw_cli_req_context *req)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+       union tw_cl_command_7k          *cmd =
+               &(req->cmd_pkt->command.cmd_pkt_7k);
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /*
+        * If the request was never submitted to the controller, the function
+        * that sets req->error is responsible for calling tw_cl_create_event.
+        */
+       if (! req->error_code)
+               if (cmd->param.status) {
+                       tw_cli_create_ctlr_event(ctlr,
+                               TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                               &(req->cmd_pkt->cmd_hdr));
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1204, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "get/set_param failed",
+                               "status = %d", cmd->param.status);
+               }
+
+       ctlr->internal_req_busy = TW_CL_FALSE;
+       tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+
+       if ((ctlr->get_more_aens) && (!(ctlr->reset_in_progress))) {
+               ctlr->get_more_aens = TW_CL_FALSE;
+               tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "Fetching more AEN's");
+               if ((error = tw_cli_get_aen(ctlr)))
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1205, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Failed to fetch all AEN's from param_callback",
+                               "error = %d", error);
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_aen_callback
+ * Description:                Callback for requests to fetch AEN's.
+ *
+ * Input:              req     -- ptr to completed request pkt
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_aen_callback(struct tw_cli_req_context *req)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+       struct tw_cl_command_header     *cmd_hdr;
+       struct tw_cl_command_9k         *cmd =
+               &(req->cmd_pkt->command.cmd_pkt_9k);
+       TW_UINT16                       aen_code = TWA_AEN_QUEUE_EMPTY;
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+               "req_id = 0x%x, req error = %d, status = 0x%x",
+               GET_REQ_ID(cmd->lun_l4__req_id), req->error_code, cmd->status);
+
+       /*
+        * If the request was never submitted to the controller, the function
+        * that sets error is responsible for calling tw_cl_create_event.
+        */
+       if (!(error = req->error_code))
+               if ((error = cmd->status)) {
+                       cmd_hdr = (struct tw_cl_command_header *)
+                               (&(req->cmd_pkt->cmd_hdr));
+                       tw_cli_create_ctlr_event(ctlr,
+                               TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                               cmd_hdr);
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1206, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Request Sense failed",
+                               "opcode = 0x%x, status = %d",
+                               GET_OPCODE(cmd->res__opcode), cmd->status);
+               }
+
+       if (error) {
+               ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+               return;
+       }
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+               "Request Sense command succeeded");
+
+       aen_code = tw_cli_manage_aen(ctlr, req);
+
+       if (aen_code != TWA_AEN_SYNC_TIME_WITH_HOST) {
+               ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+               if (aen_code != TWA_AEN_QUEUE_EMPTY)
+                       if ((error = tw_cli_get_aen(ctlr)))
+                               tw_cl_create_event(ctlr->ctlr_handle,
+                                       TW_CL_FALSE,
+                                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                                       0x1207, 0x1,
+                                       TW_CL_SEVERITY_ERROR_STRING,
+                                       "Failed to fetch all AEN's",
+                                       "error = %d", error);
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_manage_aen
+ * Description:                Handles AEN's.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ *                     req     -- ptr to CL internal request context
+ * Output:             None
+ * Return value:       None
+ */
+TW_UINT16
+tw_cli_manage_aen(struct tw_cli_ctlr_context *ctlr,
+       struct tw_cli_req_context *req)
+{
+       struct tw_cl_command_header     *cmd_hdr;
+       TW_UINT16                       aen_code;
+       TW_TIME                         local_time;
+       TW_TIME                         sync_time;
+       TW_UINT32                       error;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       cmd_hdr = (struct tw_cl_command_header *)(req->data);
+       aen_code = cmd_hdr->status_block.error;
+
+       switch (aen_code) {
+       case TWA_AEN_SYNC_TIME_WITH_HOST:
+               tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "Received AEN_SYNC_TIME");
+               /*
+                * Free the internal req pkt right here, since
+                * tw_cli_set_param will need it.
+                */
+               ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+
+               /*
+                * We will use a callback in tw_cli_set_param only when
+                * interrupts are enabled and we can expect our callback
+                * to get called.  Setting the get_more_aens
+                * flag will make the callback continue to try to retrieve
+                * more AEN's.
+                */
+               if (ctlr->interrupts_enabled)
+                       ctlr->get_more_aens = TW_CL_TRUE;
+               /* Calculate time (in seconds) since last Sunday 12.00 AM. */
+               local_time = tw_osl_get_local_time();
+               sync_time = (local_time - (3 * 86400)) % 604800;
+               if ((error = tw_cli_set_param(ctlr, TWA_PARAM_TIME_TABLE,
+                               TWA_PARAM_TIME_SCHED_TIME, 4,
+                               &sync_time,
+                               (ctlr->interrupts_enabled)
+                               ? tw_cli_param_callback : TW_CL_NULL)))
+                       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                               0x1208, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Unable to sync time with ctlr",
+                               "error = %d", error);
+
+               break;
+
+
+       case TWA_AEN_QUEUE_EMPTY:
+               tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "AEN queue empty");
+               break;
+
+
+       default:
+               /* Queue the event. */
+
+               tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "Queueing AEN");
+               tw_cli_create_ctlr_event(ctlr,
+                       TW_CL_MESSAGE_SOURCE_CONTROLLER_EVENT,
+                       cmd_hdr);
+               break;
+       } /* switch */
+       return(aen_code);
+}
+
+
+
+/*
+ * Function name:      tw_cli_enable_interrupts
+ * Description:                Enables interrupts on the controller
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_enable_interrupts(struct tw_cli_ctlr_context *ctlr)
+{
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       ctlr->interrupts_enabled = TW_CL_TRUE;
+       TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+               TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT |
+               TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT |
+               TWA_CONTROL_ENABLE_INTERRUPTS);
+}
+
+
+
+/*
+ * Function name:      twa_setup
+ * Description:                Disables interrupts on the controller
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_disable_interrupts(struct tw_cli_ctlr_context *ctlr)
+{
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+               TWA_CONTROL_DISABLE_INTERRUPTS);
+       ctlr->interrupts_enabled = TW_CL_FALSE;
+}
diff --git a/sys/dev/raid/twa/tw_cl_io.c b/sys/dev/raid/twa/tw_cl_io.c
new file mode 100644 (file)
index 0000000..bd2244a
--- /dev/null
@@ -0,0 +1,1430 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_io.c,v 1.6 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+/*
+ * Common Layer I/O functions.
+ */
+
+
+#include "tw_osl_share.h"
+#include "tw_cl_share.h"
+#include "tw_cl_fwif.h"
+#include "tw_cl_ioctl.h"
+#include "tw_cl.h"
+#include "tw_cl_externs.h"
+#include "tw_osl_ioctl.h"
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_xpt_sim.h>
+
+
+
+/*
+ * Function name:      tw_cl_start_io
+ * Description:                Interface to OS Layer for accepting SCSI requests.
+ *
+ * Input:              ctlr_handle     -- controller handle
+ *                     req_pkt         -- OSL built request packet
+ *                     req_handle      -- request handle
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_start_io(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle)
+{
+       struct tw_cli_ctlr_context              *ctlr;
+       struct tw_cli_req_context               *req;
+       struct tw_cl_command_9k                 *cmd;
+       struct tw_cl_scsi_req_packet            *scsi_req;
+       TW_INT32                                error;
+
+       tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+
+       if (ctlr->reset_in_progress) {
+               tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                       "I/O during reset: returning busy.");
+               return(TW_OSL_EBUSY);
+       }
+
+       /*
+        * If working with a firmware version that does not support multiple
+        * luns, and this request is directed at a non-zero lun, error it
+        * back right away.
+        */
+       if ((req_pkt->gen_req_pkt.scsi_req.lun) &&
+               (ctlr->working_srl < TWA_MULTI_LUN_FW_SRL)) {
+               req_pkt->status |= (TW_CL_ERR_REQ_INVALID_LUN |
+                       TW_CL_ERR_REQ_SCSI_ERROR);
+               req_pkt->tw_osl_callback(req_handle);
+               return(TW_CL_ERR_REQ_SUCCESS);
+       }
+
+       if ((req = tw_cli_get_request(ctlr
+               )) == TW_CL_NULL) {
+               tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                       "Out of request context packets: returning busy");
+               return(TW_OSL_EBUSY);
+       }
+
+       req_handle->cl_req_ctxt = req;
+       req->req_handle = req_handle;
+       req->orig_req = req_pkt;
+       req->tw_cli_callback = tw_cli_complete_io;
+
+       req->flags |= TW_CLI_REQ_FLAGS_EXTERNAL;
+       req->flags |= TW_CLI_REQ_FLAGS_9K;
+
+       scsi_req = &(req_pkt->gen_req_pkt.scsi_req);
+
+       /* Build the cmd pkt. */
+       cmd = &(req->cmd_pkt->command.cmd_pkt_9k);
+
+       req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
+
+       cmd->res__opcode = BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI);
+       cmd->unit = (TW_UINT8)(scsi_req->unit);
+       cmd->lun_l4__req_id = TW_CL_SWAP16(
+               BUILD_LUN_L4__REQ_ID(scsi_req->lun, req->request_id));
+       cmd->status = 0;
+       cmd->sgl_offset = 16; /* offset from end of hdr = max cdb len */
+       tw_osl_memcpy(cmd->cdb, scsi_req->cdb, scsi_req->cdb_len);
+
+       if (req_pkt->flags & TW_CL_REQ_CALLBACK_FOR_SGLIST) {
+               TW_UINT32       num_sgl_entries;
+
+               req_pkt->tw_osl_sgl_callback(req_handle, cmd->sg_list,
+                       &num_sgl_entries);
+               cmd->lun_h4__sgl_entries =
+                       TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun,
+                               num_sgl_entries));
+       } else {
+               cmd->lun_h4__sgl_entries =
+                       TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun,
+                               scsi_req->sgl_entries));
+               tw_cli_fill_sg_list(ctlr, scsi_req->sg_list,
+                       cmd->sg_list, scsi_req->sgl_entries);
+       }
+
+       if ((error = tw_cli_submit_cmd(req))) {
+               tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                       "Could not start request. request = %p, error = %d",
+                       req, error);
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_submit_cmd
+ * Description:                Submits a cmd to firmware.
+ *
+ * Input:              req     -- ptr to CL internal request context
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_submit_cmd(struct tw_cli_req_context *req)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+       struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
+       TW_UINT32                       status_reg;
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Serialize access to the controller cmd queue. */
+       tw_osl_get_lock(ctlr_handle, ctlr->io_lock);
+
+       /* For 9650SE first write low 4 bytes */
+       if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
+           (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA))
+               tw_osl_write_reg(ctlr_handle,
+                                TWA_COMMAND_QUEUE_OFFSET_LOW,
+                                (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
+
+       /* Check to see if we can post a command. */
+       status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
+       if ((error = tw_cli_check_ctlr_state(ctlr, status_reg)))
+               goto out;
+
+       if (status_reg & TWA_STATUS_COMMAND_QUEUE_FULL) {
+               struct tw_cl_req_packet *req_pkt =
+                       (struct tw_cl_req_packet *)(req->orig_req);
+
+               tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(),
+                       "Cmd queue full");
+
+               if ((req->flags & TW_CLI_REQ_FLAGS_INTERNAL)
+                       || ((req_pkt) &&
+                       (req_pkt->flags & TW_CL_REQ_RETRY_ON_BUSY))
+                       ) {
+                       if (req->state != TW_CLI_REQ_STATE_PENDING) {
+                               tw_cli_dbg_printf(2, ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "pending internal/ioctl request");
+                               req->state = TW_CLI_REQ_STATE_PENDING;
+                               tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
+                               error = 0;
+                               /* Unmask command interrupt. */
+                               TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+                                       TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
+                       } else
+                               error = TW_OSL_EBUSY;
+               } else {
+                       tw_osl_ctlr_busy(ctlr_handle, req->req_handle);
+                       error = TW_OSL_EBUSY;
+               }
+       } else {
+               tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
+                       "Submitting command");
+
+               /* Insert command into busy queue */
+               req->state = TW_CLI_REQ_STATE_BUSY;
+               tw_cli_req_q_insert_tail(req, TW_CLI_BUSY_Q);
+
+               if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
+                   (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) {
+                       /* Now write the high 4 bytes */
+                       tw_osl_write_reg(ctlr_handle,
+                                        TWA_COMMAND_QUEUE_OFFSET_HIGH,
+                                        (TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4);
+               } else {
+                       if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+                               /* First write the low 4 bytes, then the high 4. */
+                               tw_osl_write_reg(ctlr_handle,
+                                                TWA_COMMAND_QUEUE_OFFSET_LOW,
+                                                (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
+                               tw_osl_write_reg(ctlr_handle,
+                                                TWA_COMMAND_QUEUE_OFFSET_HIGH,
+                                                (TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4);
+                       } else
+                               tw_osl_write_reg(ctlr_handle,
+                                                TWA_COMMAND_QUEUE_OFFSET,
+                                                (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
+               }
+       }
+out:
+       tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
+
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cl_fw_passthru
+ * Description:                Interface to OS Layer for accepting firmware
+ *                     passthru requests.
+ * Input:              ctlr_handle     -- controller handle
+ *                     req_pkt         -- OSL built request packet
+ *                     req_handle      -- request handle
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_fw_passthru(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle)
+{
+       struct tw_cli_ctlr_context              *ctlr;
+       struct tw_cli_req_context               *req;
+       union tw_cl_command_7k                  *cmd_7k;
+       struct tw_cl_command_9k                 *cmd_9k;
+       struct tw_cl_passthru_req_packet        *pt_req;
+       TW_UINT8                                opcode;
+       TW_UINT8                                sgl_offset;
+       TW_VOID                                 *sgl = TW_CL_NULL;
+       TW_INT32                                error;
+
+       tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+
+       if (ctlr->reset_in_progress) {
+               tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                       "Passthru request during reset: returning busy.");
+               return(TW_OSL_EBUSY);
+       }
+
+       if ((req = tw_cli_get_request(ctlr
+               )) == TW_CL_NULL) {
+               tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                       "Out of request context packets: returning busy");
+               return(TW_OSL_EBUSY);
+       }
+
+       req_handle->cl_req_ctxt = req;
+       req->req_handle = req_handle;
+       req->orig_req = req_pkt;
+       req->tw_cli_callback = tw_cli_complete_io;
+
+       req->flags |= (TW_CLI_REQ_FLAGS_EXTERNAL | TW_CLI_REQ_FLAGS_PASSTHRU);
+
+       pt_req = &(req_pkt->gen_req_pkt.pt_req);
+
+       tw_osl_memcpy(req->cmd_pkt, pt_req->cmd_pkt,
+               pt_req->cmd_pkt_length);
+       /* Build the cmd pkt. */
+       if ((opcode = GET_OPCODE(((TW_UINT8 *)
+               (pt_req->cmd_pkt))[sizeof(struct tw_cl_command_header)]))
+                       == TWA_FW_CMD_EXECUTE_SCSI) {
+               TW_UINT16       lun_l4, lun_h4;
+
+               tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(),
+                       "passthru: 9k cmd pkt");
+               req->flags |= TW_CLI_REQ_FLAGS_9K;
+               cmd_9k = &(req->cmd_pkt->command.cmd_pkt_9k);
+               lun_l4 = GET_LUN_L4(cmd_9k->lun_l4__req_id);
+               lun_h4 = GET_LUN_H4(cmd_9k->lun_h4__sgl_entries);
+               cmd_9k->lun_l4__req_id = TW_CL_SWAP16(
+                       BUILD_LUN_L4__REQ_ID(lun_l4, req->request_id));
+               if (pt_req->sgl_entries) {
+                       cmd_9k->lun_h4__sgl_entries =
+                               TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(lun_h4,
+                                       pt_req->sgl_entries));
+                       sgl = (TW_VOID *)(cmd_9k->sg_list);
+               }
+       } else {
+               tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(),
+                       "passthru: 7k cmd pkt");
+               cmd_7k = &(req->cmd_pkt->command.cmd_pkt_7k);
+               cmd_7k->generic.request_id =
+                       (TW_UINT8)(TW_CL_SWAP16(req->request_id));
+               if ((sgl_offset =
+                       GET_SGL_OFF(cmd_7k->generic.sgl_off__opcode))) {
+                       if (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)
+                               sgl = (((TW_UINT32 *)cmd_7k) + cmd_7k->generic.size);
+                       else
+                               sgl = (((TW_UINT32 *)cmd_7k) + sgl_offset);
+                       cmd_7k->generic.size += pt_req->sgl_entries *
+                               ((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2);
+               }
+       }
+
+       if (sgl)
+               tw_cli_fill_sg_list(ctlr, pt_req->sg_list,
+                       sgl, pt_req->sgl_entries);
+
+       if ((error = tw_cli_submit_cmd(req))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1100, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Failed to start passthru command",
+                       "error = %d", error);
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cl_ioctl
+ * Description:                Handler of CL supported ioctl cmds.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ *                     cmd     -- ioctl cmd
+ *                     buf     -- ptr to buffer in kernel memory, which is
+ *                                a copy of the input buffer in user-space
+ * Output:             buf     -- ptr to buffer in kernel memory, which will
+ *                                need to be copied to the output buffer in
+ *                                user-space
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_ioctl(struct tw_cl_ctlr_handle *ctlr_handle, u_long cmd, TW_VOID *buf)
+{
+       struct tw_cli_ctlr_context      *ctlr =
+               (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+       struct tw_cl_ioctl_packet       *user_buf =
+               (struct tw_cl_ioctl_packet *)buf;
+       struct tw_cl_event_packet       event_buf;
+       TW_INT32                        event_index;
+       TW_INT32                        start_index;
+       TW_INT32                        error = TW_OSL_ESUCCESS;
+
+       tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Serialize access to the AEN queue and the ioctl lock. */
+       tw_osl_get_lock(ctlr_handle, ctlr->gen_lock);
+
+       switch (cmd) {
+       case TW_CL_IOCTL_GET_FIRST_EVENT:
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get First Event");
+
+               if (ctlr->aen_q_wrapped) {
+                       if (ctlr->aen_q_overflow) {
+                               /*
+                                * The aen queue has wrapped, even before some
+                                * events have been retrieved.  Let the caller
+                                * know that he missed out on some AEN's.
+                                */
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_OVERFLOW;
+                               ctlr->aen_q_overflow = TW_CL_FALSE;
+                       } else
+                               user_buf->driver_pkt.status = 0;
+                       event_index = ctlr->aen_head;
+               } else {
+                       if (ctlr->aen_head == ctlr->aen_tail) {
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_NO_EVENTS;
+                               break;
+                       }
+                       user_buf->driver_pkt.status = 0;
+                       event_index = ctlr->aen_tail;   /* = 0 */
+               }
+               tw_osl_memcpy(user_buf->data_buf,
+                       &(ctlr->aen_queue[event_index]),
+                       sizeof(struct tw_cl_event_packet));
+
+               ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
+
+               break;
+
+
+       case TW_CL_IOCTL_GET_LAST_EVENT:
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get Last Event");
+
+               if (ctlr->aen_q_wrapped) {
+                       if (ctlr->aen_q_overflow) {
+                               /*
+                                * The aen queue has wrapped, even before some
+                                * events have been retrieved.  Let the caller
+                                * know that he missed out on some AEN's.
+                                */
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_OVERFLOW;
+                               ctlr->aen_q_overflow = TW_CL_FALSE;
+                       } else
+                               user_buf->driver_pkt.status = 0;
+               } else {
+                       if (ctlr->aen_head == ctlr->aen_tail) {
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_NO_EVENTS;
+                               break;
+                       }
+                       user_buf->driver_pkt.status = 0;
+               }
+               event_index = (ctlr->aen_head - 1 + ctlr->max_aens_supported) %
+                       ctlr->max_aens_supported;
+
+               tw_osl_memcpy(user_buf->data_buf,
+                       &(ctlr->aen_queue[event_index]),
+                       sizeof(struct tw_cl_event_packet));
+
+               ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
+
+               break;
+
+
+       case TW_CL_IOCTL_GET_NEXT_EVENT:
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get Next Event");
+
+               user_buf->driver_pkt.status = 0;
+               if (ctlr->aen_q_wrapped) {
+                       tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                               "Get Next Event: wrapped");
+                       if (ctlr->aen_q_overflow) {
+                               /*
+                                * The aen queue has wrapped, even before some
+                                * events have been retrieved.  Let the caller
+                                * know that he missed out on some AEN's.
+                                */
+                               tw_cli_dbg_printf(2, ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "Get Next Event: overflow");
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_OVERFLOW;
+                               ctlr->aen_q_overflow = TW_CL_FALSE;
+                       }
+                       start_index = ctlr->aen_head;
+               } else {
+                       if (ctlr->aen_head == ctlr->aen_tail) {
+                               tw_cli_dbg_printf(3, ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "Get Next Event: empty queue");
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_NO_EVENTS;
+                               break;
+                       }
+                       start_index = ctlr->aen_tail;   /* = 0 */
+               }
+               tw_osl_memcpy(&event_buf, user_buf->data_buf,
+                       sizeof(struct tw_cl_event_packet));
+
+               event_index = (start_index + event_buf.sequence_id -
+                       ctlr->aen_queue[start_index].sequence_id + 1) %
+                       ctlr->max_aens_supported;
+
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get Next Event: si = %x, ei = %x, ebsi = %x, "
+                       "sisi = %x, eisi = %x",
+                       start_index, event_index, event_buf.sequence_id,
+                       ctlr->aen_queue[start_index].sequence_id,
+                       ctlr->aen_queue[event_index].sequence_id);
+
+               if (! (ctlr->aen_queue[event_index].sequence_id >
+                       event_buf.sequence_id)) {
+                       /*
+                        * We don't have any event matching the criterion.  So,
+                        * we have to report TW_CL_ERROR_NO_EVENTS.  If we also
+                        * encountered an overflow condition above, we cannot
+                        * report both conditions during this call.  We choose
+                        * to report NO_EVENTS this time, and an overflow the
+                        * next time we are called.
+                        */
+                       if (user_buf->driver_pkt.status ==
+                               TW_CL_ERROR_AEN_OVERFLOW) {
+                               /*
+                                * Make a note so we report the overflow
+                                * next time.
+                                */
+                               ctlr->aen_q_overflow = TW_CL_TRUE;
+                       }
+                       user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
+                       break;
+               }
+               /* Copy the event -- even if there has been an overflow. */
+               tw_osl_memcpy(user_buf->data_buf,
+                       &(ctlr->aen_queue[event_index]),
+                       sizeof(struct tw_cl_event_packet));
+
+               ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
+
+               break;
+
+
+       case TW_CL_IOCTL_GET_PREVIOUS_EVENT:
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get Previous Event");
+
+               user_buf->driver_pkt.status = 0;
+               if (ctlr->aen_q_wrapped) {
+                       if (ctlr->aen_q_overflow) {
+                               /*
+                                * The aen queue has wrapped, even before some
+                                * events have been retrieved.  Let the caller
+                                * know that he missed out on some AEN's.
+                                */
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_OVERFLOW;
+                               ctlr->aen_q_overflow = TW_CL_FALSE;
+                       }
+                       start_index = ctlr->aen_head;
+               } else {
+                       if (ctlr->aen_head == ctlr->aen_tail) {
+                               user_buf->driver_pkt.status =
+                                       TW_CL_ERROR_AEN_NO_EVENTS;
+                               break;
+                       }
+                       start_index = ctlr->aen_tail;   /* = 0 */
+               }
+               tw_osl_memcpy(&event_buf, user_buf->data_buf,
+                       sizeof(struct tw_cl_event_packet));
+
+               event_index = (start_index + event_buf.sequence_id -
+                       ctlr->aen_queue[start_index].sequence_id - 1) %
+                       ctlr->max_aens_supported;
+
+               if (! (ctlr->aen_queue[event_index].sequence_id <
+                       event_buf.sequence_id)) {
+                       /*
+                        * We don't have any event matching the criterion.  So,
+                        * we have to report TW_CL_ERROR_NO_EVENTS.  If we also
+                        * encountered an overflow condition above, we cannot
+                        * report both conditions during this call.  We choose
+                        * to report NO_EVENTS this time, and an overflow the
+                        * next time we are called.
+                        */
+                       if (user_buf->driver_pkt.status ==
+                               TW_CL_ERROR_AEN_OVERFLOW) {
+                               /*
+                                * Make a note so we report the overflow
+                                * next time.
+                                */
+                               ctlr->aen_q_overflow = TW_CL_TRUE;
+                       }
+                       user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
+                       break;
+               }
+               /* Copy the event -- even if there has been an overflow. */
+               tw_osl_memcpy(user_buf->data_buf,
+                       &(ctlr->aen_queue[event_index]),
+                       sizeof(struct tw_cl_event_packet));
+
+               ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;
+
+               break;
+
+
+       case TW_CL_IOCTL_GET_LOCK:
+       {
+               struct tw_cl_lock_packet        lock_pkt;
+               TW_TIME                         cur_time;
+
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get ioctl lock");
+
+               cur_time = tw_osl_get_local_time();
+               tw_osl_memcpy(&lock_pkt, user_buf->data_buf,
+                       sizeof(struct tw_cl_lock_packet));
+
+               if ((ctlr->ioctl_lock.lock == TW_CLI_LOCK_FREE) ||
+                       (lock_pkt.force_flag) ||
+                       (cur_time >= ctlr->ioctl_lock.timeout)) {
+                       tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                               "GET_LOCK: Getting lock!");
+                       ctlr->ioctl_lock.lock = TW_CLI_LOCK_HELD;
+                       ctlr->ioctl_lock.timeout =
+                               cur_time + (lock_pkt.timeout_msec / 1000);
+                       lock_pkt.time_remaining_msec = lock_pkt.timeout_msec;
+                       user_buf->driver_pkt.status = 0;
+               } else {
+                       tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                               "GET_LOCK: Lock already held!");
+                       lock_pkt.time_remaining_msec = (TW_UINT32)(
+                               (ctlr->ioctl_lock.timeout - cur_time) * 1000);
+                       user_buf->driver_pkt.status =
+                               TW_CL_ERROR_IOCTL_LOCK_ALREADY_HELD;
+               }
+               tw_osl_memcpy(user_buf->data_buf, &lock_pkt,
+                       sizeof(struct tw_cl_lock_packet));
+               break;
+       }
+
+
+       case TW_CL_IOCTL_RELEASE_LOCK:
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Release ioctl lock");
+
+               if (ctlr->ioctl_lock.lock == TW_CLI_LOCK_FREE) {
+                       tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+                               "twa_ioctl: RELEASE_LOCK: Lock not held!");
+                       user_buf->driver_pkt.status =
+                               TW_CL_ERROR_IOCTL_LOCK_NOT_HELD;
+               } else {
+                       tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                               "RELEASE_LOCK: Releasing lock!");
+                       ctlr->ioctl_lock.lock = TW_CLI_LOCK_FREE;
+                       user_buf->driver_pkt.status = 0;
+               }
+               break;
+
+
+       case TW_CL_IOCTL_GET_COMPATIBILITY_INFO:
+       {
+               struct tw_cl_compatibility_packet       comp_pkt;
+
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Get compatibility info");
+
+               tw_osl_memcpy(comp_pkt.driver_version,
+                       TW_OSL_DRIVER_VERSION_STRING,
+                       sizeof(TW_OSL_DRIVER_VERSION_STRING));
+               comp_pkt.working_srl = ctlr->working_srl;
+               comp_pkt.working_branch = ctlr->working_branch;
+               comp_pkt.working_build = ctlr->working_build;
+               comp_pkt.driver_srl_high = TWA_CURRENT_FW_SRL;
+               comp_pkt.driver_branch_high =
+                       TWA_CURRENT_FW_BRANCH(ctlr->arch_id);
+               comp_pkt.driver_build_high =
+                       TWA_CURRENT_FW_BUILD(ctlr->arch_id);
+               comp_pkt.driver_srl_low = TWA_BASE_FW_SRL;
+               comp_pkt.driver_branch_low = TWA_BASE_FW_BRANCH;
+               comp_pkt.driver_build_low = TWA_BASE_FW_BUILD;
+               comp_pkt.fw_on_ctlr_srl = ctlr->fw_on_ctlr_srl;
+               comp_pkt.fw_on_ctlr_branch = ctlr->fw_on_ctlr_branch;
+               comp_pkt.fw_on_ctlr_build = ctlr->fw_on_ctlr_build;
+               user_buf->driver_pkt.status = 0;
+
+               /* Copy compatibility information to user space. */
+               tw_osl_memcpy(user_buf->data_buf, &comp_pkt,
+                       (sizeof(struct tw_cl_compatibility_packet) <
+                       user_buf->driver_pkt.buffer_length) ?
+                       sizeof(struct tw_cl_compatibility_packet) :
+                       user_buf->driver_pkt.buffer_length);
+               break;
+       }
+
+       default:
+               /* Unknown opcode. */
+               tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
+                       "Unknown ioctl cmd 0x%x", cmd);
+               error = TW_OSL_ENOTTY;
+       }
+
+       tw_osl_free_lock(ctlr_handle, ctlr->gen_lock);
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_get_param
+ * Description:                Get a firmware parameter.
+ *
+ * Input:              ctlr            -- ptr to per ctlr structure
+ *                     table_id        -- parameter table #
+ *                     param_id        -- index of the parameter in the table
+ *                     param_size      -- size of the parameter in bytes
+ *                     callback        -- ptr to function, if any, to be called
+ *                                     back on completion; TW_CL_NULL if no callback.
+ * Output:             param_data      -- param value
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_get_param(struct tw_cli_ctlr_context *ctlr, TW_INT32 table_id,
+       TW_INT32 param_id, TW_VOID *param_data, TW_INT32 param_size,
+       TW_VOID (* callback)(struct tw_cli_req_context *req))
+{
+       struct tw_cli_req_context       *req;
+       union tw_cl_command_7k          *cmd;
+       struct tw_cl_param_9k           *param = TW_CL_NULL;
+       TW_INT32                        error = TW_OSL_EBUSY;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Get a request packet. */
+       if ((req = tw_cli_get_request(ctlr
+               )) == TW_CL_NULL)
+               goto out;
+
+       /* Make sure this is the only CL internal request at this time. */
+       if (ctlr->internal_req_busy) {
+               error = TW_OSL_EBUSY;
+               goto out;
+       }
+       ctlr->internal_req_busy = TW_CL_TRUE;
+       req->data = ctlr->internal_req_data;
+       req->data_phys = ctlr->internal_req_data_phys;
+       req->length = TW_CLI_SECTOR_SIZE;
+       req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
+
+       /* Initialize memory to read data into. */
+       param = (struct tw_cl_param_9k *)(req->data);
+       tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);
+
+       /* Build the cmd pkt. */
+       cmd = &(req->cmd_pkt->command.cmd_pkt_7k);
+
+       req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
+
+       cmd->param.sgl_off__opcode =
+               BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_GET_PARAM);
+       cmd->param.request_id =
+               (TW_UINT8)(TW_CL_SWAP16(req->request_id));
+       cmd->param.host_id__unit = BUILD_HOST_ID__UNIT(0, 0);
+       cmd->param.param_count = TW_CL_SWAP16(1);
+
+       if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+               ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].address =
+                       TW_CL_SWAP64(req->data_phys);
+               ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].length =
+                       TW_CL_SWAP32(req->length);
+               cmd->param.size = 2 + 3;
+       } else {
+               ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].address =
+                       TW_CL_SWAP32(req->data_phys);
+               ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].length =
+                       TW_CL_SWAP32(req->length);
+               cmd->param.size = 2 + 2;
+       }
+
+       /* Specify which parameter we need. */
+       param->table_id = TW_CL_SWAP16(table_id | TWA_9K_PARAM_DESCRIPTOR);
+       param->parameter_id = (TW_UINT8)(param_id);
+       param->parameter_size_bytes = TW_CL_SWAP16(param_size);
+
+       /* Submit the command. */
+       if (callback == TW_CL_NULL) {
+               /* There's no call back; wait till the command completes. */
+               error = tw_cli_submit_and_poll_request(req,
+                               TW_CLI_REQUEST_TIMEOUT_PERIOD);
+               if (error == TW_OSL_ETIMEDOUT)
+                       /* Clean-up done by tw_cli_submit_and_poll_request. */
+                       return(error);
+               if (error)
+                       goto out;
+               if ((error = cmd->param.status)) {
+                       tw_cli_create_ctlr_event(ctlr,
+                               TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                               &(req->cmd_pkt->cmd_hdr));
+                       goto out;
+               }
+               tw_osl_memcpy(param_data, param->data, param_size);
+               ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       } else {
+               /* There's a call back.  Simply submit the command. */
+               req->tw_cli_callback = callback;
+               if ((error = tw_cli_submit_cmd(req)))
+                       goto out;
+       }
+       return(0);
+
+out:
+       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+               0x1101, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+               "get_param failed",
+               "error = %d", error);
+       if (param)
+               ctlr->internal_req_busy = TW_CL_FALSE;
+       if (req)
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       return(1);
+}
+
+
+
+/*
+ * Function name:      tw_cli_set_param
+ * Description:                Set a firmware parameter.
+ *
+ * Input:              ctlr            -- ptr to per ctlr structure
+ *                     table_id        -- parameter table #
+ *                     param_id        -- index of the parameter in the table
+ *                     param_size      -- size of the parameter in bytes
+ *                     callback        -- ptr to function, if any, to be called
+ *                                     back on completion; TW_CL_NULL if no callback.
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_set_param(struct tw_cli_ctlr_context *ctlr, TW_INT32 table_id,
+       TW_INT32 param_id, TW_INT32 param_size, TW_VOID *data,
+       TW_VOID (* callback)(struct tw_cli_req_context *req))
+{
+       struct tw_cli_req_context       *req;
+       union tw_cl_command_7k          *cmd;
+       struct tw_cl_param_9k           *param = TW_CL_NULL;
+       TW_INT32                        error = TW_OSL_EBUSY;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Get a request packet. */
+       if ((req = tw_cli_get_request(ctlr
+               )) == TW_CL_NULL)
+               goto out;
+
+       /* Make sure this is the only CL internal request at this time. */
+       if (ctlr->internal_req_busy) {
+               error = TW_OSL_EBUSY;
+               goto out;
+       }
+       ctlr->internal_req_busy = TW_CL_TRUE;
+       req->data = ctlr->internal_req_data;
+       req->data_phys = ctlr->internal_req_data_phys;
+       req->length = TW_CLI_SECTOR_SIZE;
+       req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
+
+       /* Initialize memory to send data using. */
+       param = (struct tw_cl_param_9k *)(req->data);
+       tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);
+
+       /* Build the cmd pkt. */
+       cmd = &(req->cmd_pkt->command.cmd_pkt_7k);
+
+       req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;
+
+       cmd->param.sgl_off__opcode =
+               BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_SET_PARAM);
+       cmd->param.request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id));
+       cmd->param.host_id__unit = BUILD_HOST_ID__UNIT(0, 0);
+       cmd->param.param_count = TW_CL_SWAP16(1);
+
+       if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+               ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].address =
+                       TW_CL_SWAP64(req->data_phys);
+               ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].length =
+                       TW_CL_SWAP32(req->length);
+               cmd->param.size = 2 + 3;
+       } else {
+               ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].address =
+                       TW_CL_SWAP32(req->data_phys);
+               ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].length =
+                       TW_CL_SWAP32(req->length);
+               cmd->param.size = 2 + 2;
+       }
+
+       /* Specify which parameter we want to set. */
+       param->table_id = TW_CL_SWAP16(table_id | TWA_9K_PARAM_DESCRIPTOR);
+       param->parameter_id = (TW_UINT8)(param_id);
+       param->parameter_size_bytes = TW_CL_SWAP16(param_size);
+       tw_osl_memcpy(param->data, data, param_size);
+
+       /* Submit the command. */
+       if (callback == TW_CL_NULL) {
+               /* There's no call back;  wait till the command completes. */
+               error = tw_cli_submit_and_poll_request(req,
+                       TW_CLI_REQUEST_TIMEOUT_PERIOD);
+               if (error == TW_OSL_ETIMEDOUT)
+                       /* Clean-up done by tw_cli_submit_and_poll_request. */
+                       return(error);
+               if (error)
+                       goto out;
+               if ((error = cmd->param.status)) {
+                       tw_cli_create_ctlr_event(ctlr,
+                               TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                               &(req->cmd_pkt->cmd_hdr));
+                       goto out;
+               }
+               ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       } else {
+               /* There's a call back.  Simply submit the command. */
+               req->tw_cli_callback = callback;
+               if ((error = tw_cli_submit_cmd(req)))
+                       goto out;
+       }
+       return(error);
+
+out:
+       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+               0x1102, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+               "set_param failed",
+               "error = %d", error);
+       if (param)
+               ctlr->internal_req_busy = TW_CL_FALSE;
+       if (req)
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_submit_and_poll_request
+ * Description:                Sends down a firmware cmd, and waits for the completion
+ *                     in a tight loop.
+ *
+ * Input:              req     -- ptr to request pkt
+ *                     timeout -- max # of seconds to wait before giving up
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_submit_and_poll_request(struct tw_cli_req_context *req,
+       TW_UINT32 timeout)
+{
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+       TW_TIME                         end_time;
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /*
+        * If the cmd queue is full, tw_cli_submit_cmd will queue this
+        * request in the pending queue, since this is an internal request.
+        */
+       if ((error = tw_cli_submit_cmd(req))) {
+               tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x1103, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Failed to start internal request",
+                       "error = %d", error);
+               return(error);
+       }
+
+       /*
+        * Poll for the response until the command gets completed, or there's
+        * a timeout.
+        */
+       end_time = tw_osl_get_local_time() + timeout;
+       do {
+               if ((error = req->error_code))
+                       /*
+                        * This will take care of completion due to a reset,
+                        * or a failure in tw_cli_submit_pending_queue.
+                        * The caller should do the clean-up.
+                        */
+                       return(error);
+
+               /* See if the command completed. */
+               tw_cli_process_resp_intr(ctlr);
+
+               if ((req->state != TW_CLI_REQ_STATE_BUSY) &&
+                       (req->state != TW_CLI_REQ_STATE_PENDING))
+                       return(req->state != TW_CLI_REQ_STATE_COMPLETE);
+       } while (tw_osl_get_local_time() <= end_time);
+
+       /* Time out! */
+       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+               0x1104, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+               "Internal request timed out",
+               "request = %p", req);
+
+       /*
+        * We will reset the controller only if the request has already been
+        * submitted, so as to not lose the request packet.  If a busy request
+        * timed out, the reset will take care of freeing resources.  If a
+        * pending request timed out, we will free resources for that request,
+        * right here, thereby avoiding a reset.  So, the caller is expected
+        * to NOT cleanup when TW_OSL_ETIMEDOUT is returned.
+        */
+
+       /*
+        * We have to make sure that this timed out request, if it were in the
+        * pending queue, doesn't get submitted while we are here, from
+        * tw_cli_submit_pending_queue.  There could be a race in that case.
+        * Need to revisit.
+        */
+       if (req->state != TW_CLI_REQ_STATE_PENDING)
+               tw_cl_reset_ctlr(ctlr->ctlr_handle);
+       else {
+               tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "Removing request from pending queue");
+               /*
+                * Request was never submitted.  Clean up.  Note that we did
+                * not do a reset.  So, we have to remove the request ourselves
+                * from the pending queue (as against tw_cli_drain_pendinq_queue
+                * taking care of it).
+                */
+               tw_cli_req_q_remove_item(req, TW_CLI_PENDING_Q);
+               if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) == TW_CL_NULL)
+                       TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+                               TWA_CONTROL_MASK_COMMAND_INTERRUPT);
+               if (req->data)
+                       ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+
+       return(TW_OSL_ETIMEDOUT);
+}
+
+
+
+/*
+ * Function name:      tw_cl_reset_ctlr
+ * Description:                Soft resets and then initializes the controller;
+ *                     drains any incomplete requests.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cl_reset_ctlr(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+       struct tw_cli_ctlr_context      *ctlr =
+               (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+       struct twa_softc                *sc = ctlr_handle->osl_ctlr_ctxt;
+       TW_INT32                        reset_attempt = 1;
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       ctlr->reset_in_progress = TW_CL_TRUE;
+       xpt_freeze_simq(sc->sim, 1);
+
+       tw_cli_disable_interrupts(ctlr);
+
+       /*
+        * Error back all requests in the complete, busy, and pending queues.
+        * If any request is already on its way to getting submitted, it's in
+        * none of these queues and so, will not be completed.  That request
+        * will continue its course and get submitted to the controller after
+        * the reset is done (and io_lock is released).
+        */
+       tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
+               "Draining all queues following reset");
+       tw_cli_drain_complete_queue(ctlr);
+       tw_cli_drain_busy_queue(ctlr);
+       tw_cli_drain_pending_queue(ctlr);
+       ctlr->internal_req_busy = TW_CL_FALSE;
+       ctlr->get_more_aens     = TW_CL_FALSE;
+
+       /* Soft reset the controller. */
+try_reset:
+       if ((error = tw_cli_soft_reset(ctlr))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                       0x1105, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Controller reset failed",
+                       "error = %d; attempt %d", error, reset_attempt++);
+               if (reset_attempt <= TW_CLI_MAX_RESET_ATTEMPTS)
+                       goto try_reset;
+               else
+                       goto out;
+       }
+
+       /* Re-establish logical connection with the controller. */
+       if ((error = tw_cli_init_connection(ctlr,
+                       (TW_UINT16)(ctlr->max_simult_reqs),
+                       0, 0, 0, 0, 0, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL,
+                       TW_CL_NULL, TW_CL_NULL))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                       0x1106, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Can't initialize connection after reset",
+                       "error = %d", error);
+               goto out;
+       }
+
+       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+               0x1107, 0x3, TW_CL_SEVERITY_INFO_STRING,
+               "Controller reset done!",
+               " ");
+
+out:
+       ctlr->reset_in_progress = TW_CL_FALSE;
+       xpt_release_simq(sc->sim, 1);
+
+       /*
+        * Enable interrupts, and also clear attention and response interrupts.
+        */
+       tw_cli_enable_interrupts(ctlr);
+
+       /* Request for a bus re-scan. */
+       if (!error)
+               tw_osl_scan_bus(ctlr_handle);
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_soft_reset
+ * Description:                Does the actual soft reset.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_soft_reset(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
+       TW_UINT32                       status_reg;
+       int                             found;
+       int                             loop_count;
+       TW_UINT32                       error;
+
+       tw_cli_dbg_printf(1, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+               0x1108, 0x3, TW_CL_SEVERITY_INFO_STRING,
+               "Resetting controller...",
+               " ");
+
+       /* Don't let any new commands get submitted to the controller. */
+       tw_osl_get_lock(ctlr_handle, ctlr->io_lock);
+
+       TW_CLI_SOFT_RESET(ctlr_handle);
+
+       if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_X) ||
+           (ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
+           (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) {
+               /*
+                * There's a hardware bug in the G133 ASIC, which can lead to
+                * PCI parity errors and hangs, if the host accesses any
+                * registers when the firmware is resetting the hardware, as
+                * part of a hard/soft reset.  The window of time when the
+                * problem can occur is about 10 ms.  Here, we will handshake
+                * with the firmware to find out when the firmware is pulling
+                * down the hardware reset pin, and wait for about 500 ms to
+                * make sure we don't access any hardware registers (for
+                * polling) during that window.
+                */
+               ctlr->reset_phase1_in_progress = TW_CL_TRUE;
+               loop_count = 0;
+               do {
+                       found = (tw_cli_find_response(ctlr, TWA_RESET_PHASE1_NOTIFICATION_RESPONSE) == TW_OSL_ESUCCESS);
+                       tw_osl_delay(10);
+                       loop_count++;
+                       error = 0x7888;
+               } while (!found && (loop_count < 6000000)); /* Loop for no more than 60 seconds */
+
+               if (!found) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                               0x1109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Missed firmware handshake after soft-reset",
+                               "error = %d", error);
+                       tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
+                       return(error);
+               }
+
+               tw_osl_delay(TWA_RESET_PHASE1_WAIT_TIME_MS * 1000);
+               ctlr->reset_phase1_in_progress = TW_CL_FALSE;
+       }
+
+       if ((error = tw_cli_poll_status(ctlr,
+                       TWA_STATUS_MICROCONTROLLER_READY |
+                       TWA_STATUS_ATTENTION_INTERRUPT,
+                       TW_CLI_RESET_TIMEOUT_PERIOD))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                       0x1109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Micro-ctlr not ready/No attn intr after reset",
+                       "error = %d", error);
+               tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
+               return(error);
+       }
+
+       TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+               TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT);
+
+       if ((error = tw_cli_drain_response_queue(ctlr))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x110A, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Can't drain response queue after reset",
+                       "error = %d", error);
+               tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
+               return(error);
+       }
+
+       tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
+
+       if ((error = tw_cli_drain_aen_queue(ctlr))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+                       0x110B, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Can't drain AEN queue after reset",
+                       "error = %d", error);
+               return(error);
+       }
+
+       if ((error = tw_cli_find_aen(ctlr, TWA_AEN_SOFT_RESET))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                       0x110C, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Reset not reported by controller",
+                       "error = %d", error);
+               return(error);
+       }
+
+       status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
+
+       if ((error = TW_CLI_STATUS_ERRORS(status_reg)) ||
+                       (error = tw_cli_check_ctlr_state(ctlr, status_reg))) {
+               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                       0x110D, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Controller errors detected after reset",
+                       "error = %d", error);
+               return(error);
+       }
+
+       return(TW_OSL_ESUCCESS);
+}
+
+
+
+/*
+ * Function name:      tw_cli_send_scsi_cmd
+ * Description:                Sends down a scsi cmd to fw.
+ *
+ * Input:              req     -- ptr to request pkt
+ *                     cmd     -- opcode of scsi cmd to send
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_send_scsi_cmd(struct tw_cli_req_context *req, TW_INT32 cmd)
+{
+       struct tw_cl_command_packet     *cmdpkt;
+       struct tw_cl_command_9k         *cmd9k;
+       struct tw_cli_ctlr_context      *ctlr;
+       TW_INT32                        error;
+
+       ctlr = req->ctlr;
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Make sure this is the only CL internal request at this time. */
+       if (ctlr->internal_req_busy)
+               return(TW_OSL_EBUSY);
+       ctlr->internal_req_busy = TW_CL_TRUE;
+       req->data = ctlr->internal_req_data;
+       req->data_phys = ctlr->internal_req_data_phys;
+       tw_osl_memzero(req->data, TW_CLI_SECTOR_SIZE);
+       req->length = TW_CLI_SECTOR_SIZE;
+
+       /* Build the cmd pkt. */
+       cmdpkt = req->cmd_pkt;
+
+       cmdpkt->cmd_hdr.header_desc.size_header = 128;
+
+       cmd9k = &(cmdpkt->command.cmd_pkt_9k);
+
+       cmd9k->res__opcode =
+               BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI);
+       cmd9k->unit = 0;
+       cmd9k->lun_l4__req_id = TW_CL_SWAP16(req->request_id);
+       cmd9k->status = 0;
+       cmd9k->sgl_offset = 16; /* offset from end of hdr = max cdb len */
+       cmd9k->lun_h4__sgl_entries = TW_CL_SWAP16(1);
+
+       if (req->ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+               ((struct tw_cl_sg_desc64 *)(cmd9k->sg_list))[0].address =
+                       TW_CL_SWAP64(req->data_phys);
+               ((struct tw_cl_sg_desc64 *)(cmd9k->sg_list))[0].length =
+                       TW_CL_SWAP32(req->length);
+       } else {
+               ((struct tw_cl_sg_desc32 *)(cmd9k->sg_list))[0].address =
+                       TW_CL_SWAP32(req->data_phys);
+               ((struct tw_cl_sg_desc32 *)(cmd9k->sg_list))[0].length =
+                       TW_CL_SWAP32(req->length);
+       }
+
+       cmd9k->cdb[0] = (TW_UINT8)cmd;
+       cmd9k->cdb[4] = 128;
+
+       if ((error = tw_cli_submit_cmd(req)))
+               if (error != TW_OSL_EBUSY) {
+                       tw_cli_dbg_printf(1, ctlr->ctlr_handle,
+                               tw_osl_cur_func(),
+                               "Failed to start SCSI command",
+                               "request = %p, error = %d", req, error);
+                       return(TW_OSL_EIO);
+               }
+       return(TW_OSL_ESUCCESS);
+}
+
+
+
+/*
+ * Function name:      tw_cli_get_aen
+ * Description:                Sends down a Request Sense cmd to fw to fetch an AEN.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_get_aen(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       if ((req = tw_cli_get_request(ctlr
+               )) == TW_CL_NULL)
+               return(TW_OSL_EBUSY);
+
+       req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
+       req->flags |= TW_CLI_REQ_FLAGS_9K;
+       req->tw_cli_callback = tw_cli_aen_callback;
+       if ((error = tw_cli_send_scsi_cmd(req, 0x03 /* REQUEST_SENSE */))) {
+               tw_cli_dbg_printf(1, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "Could not send SCSI command",
+                       "request = %p, error = %d", req, error);
+               if (req->data)
+                       ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_fill_sg_list
+ * Description:                Fills in the scatter/gather list.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ *                     sgl_src -- ptr to fill the sg list from
+ *                     sgl_dest-- ptr to sg list
+ *                     nsegments--# of segments
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_fill_sg_list(struct tw_cli_ctlr_context *ctlr, TW_VOID *sgl_src,
+       TW_VOID *sgl_dest, TW_INT32 num_sgl_entries)
+{
+       TW_INT32        i;
+
+       tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+               struct tw_cl_sg_desc64 *sgl_s =
+                       (struct tw_cl_sg_desc64 *)sgl_src;
+               struct tw_cl_sg_desc64 *sgl_d =
+                       (struct tw_cl_sg_desc64 *)sgl_dest;
+
+               tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "64 bit addresses");
+               for (i = 0; i < num_sgl_entries; i++) {
+                       sgl_d[i].address = TW_CL_SWAP64(sgl_s->address);
+                       sgl_d[i].length = TW_CL_SWAP32(sgl_s->length);
+                       sgl_s++;
+                       if (ctlr->flags & TW_CL_64BIT_SG_LENGTH)
+                               sgl_s = (struct tw_cl_sg_desc64 *)
+                                       (((TW_INT8 *)(sgl_s)) + 4);
+               }
+       } else {
+               struct tw_cl_sg_desc32 *sgl_s =
+                       (struct tw_cl_sg_desc32 *)sgl_src;
+               struct tw_cl_sg_desc32 *sgl_d =
+                       (struct tw_cl_sg_desc32 *)sgl_dest;
+
+               tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(),
+                       "32 bit addresses");
+               for (i = 0; i < num_sgl_entries; i++) {
+                       sgl_d[i].address = TW_CL_SWAP32(sgl_s[i].address);
+                       sgl_d[i].length = TW_CL_SWAP32(sgl_s[i].length);
+               }
+       }
+}
diff --git a/sys/dev/raid/twa/tw_cl_ioctl.h b/sys/dev/raid/twa/tw_cl_ioctl.h
new file mode 100644 (file)
index 0000000..4b2ee79
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_ioctl.h,v 1.3 2007/05/09 04:16:32 scottl Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ */
+
+
+
+#ifndef TW_CL_IOCTL_H
+
+#define TW_CL_IOCTL_H
+
+
+/*
+ * Macros and structures for Common Layer handled ioctls.
+ */
+
+
+#define TW_CL_AEN_NOT_RETRIEVED        0x1
+#define TW_CL_AEN_RETRIEVED    0x2
+
+#define TW_CL_ERROR_AEN_NO_EVENTS      0x1003  /* No more events */
+#define TW_CL_ERROR_AEN_OVERFLOW       0x1004  /* AEN overflow occurred */
+
+#define TW_CL_ERROR_IOCTL_LOCK_NOT_HELD                0x1001   /* Not locked */
+#define TW_CL_ERROR_IOCTL_LOCK_ALREADY_HELD    0x1002   /* Already locked */
+
+
+#pragma pack(1)
+
+/* Structure used to handle GET/RELEASE LOCK ioctls. */
+struct tw_cl_lock_packet {
+       TW_UINT32       timeout_msec;
+       TW_UINT32       time_remaining_msec;
+       TW_UINT32       force_flag;
+};
+
+
+/* Structure used to handle GET COMPATIBILITY INFO ioctl. */
+struct tw_cl_compatibility_packet {
+       TW_UINT8        driver_version[32];/* driver version */
+       TW_UINT16       working_srl;    /* driver & firmware negotiated srl */
+       TW_UINT16       working_branch; /* branch # of the firmware that the
+                                       driver is compatible with */
+       TW_UINT16       working_build;  /* build # of the firmware that the
+                                       driver is compatible with */
+       TW_UINT16       driver_srl_high;/* highest driver supported srl */
+       TW_UINT16       driver_branch_high;/* highest driver supported branch */
+       TW_UINT16       driver_build_high;/* highest driver supported build */
+       TW_UINT16       driver_srl_low;/* lowest driver supported srl */
+       TW_UINT16       driver_branch_low;/* lowest driver supported branch */
+       TW_UINT16       driver_build_low;/* lowest driver supported build */
+       TW_UINT16       fw_on_ctlr_srl; /* srl of running firmware */
+       TW_UINT16       fw_on_ctlr_branch;/* branch # of running firmware */
+       TW_UINT16       fw_on_ctlr_build;/* build # of running firmware */
+};
+
+
+/* Driver understandable part of the ioctl packet built by the API. */
+struct tw_cl_driver_packet {
+       TW_UINT32       control_code;
+       TW_UINT32       status;
+       TW_UINT32       unique_id;
+       TW_UINT32       sequence_id;
+       TW_UINT32       os_status;
+       TW_UINT32       buffer_length;
+};
+
+
+/* ioctl packet built by the API. */
+struct tw_cl_ioctl_packet {
+       struct tw_cl_driver_packet      driver_pkt;
+       TW_INT8                         padding[488];
+       struct tw_cl_command_packet     cmd_pkt;
+       TW_INT8                         data_buf[1];
+};
+
+#pragma pack()
+
+
+
+#endif /* TW_CL_IOCTL_H */
diff --git a/sys/dev/raid/twa/tw_cl_misc.c b/sys/dev/raid/twa/tw_cl_misc.c
new file mode 100644 (file)
index 0000000..7993430
--- /dev/null
@@ -0,0 +1,1038 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_misc.c,v 1.6 2010/06/17 19:48:03 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+/*
+ * Common Layer miscellaneous functions.
+ */
+
+
+#include "tw_osl_share.h"
+#include "tw_cl_share.h"
+#include "tw_cl_fwif.h"
+#include "tw_cl_ioctl.h"
+#include "tw_cl.h"
+#include "tw_cl_externs.h"
+#include "tw_osl_ioctl.h"
+
+
+
+/* AEN severity table. */
+TW_INT8        *tw_cli_severity_string_table[] = {
+       "None",
+       TW_CL_SEVERITY_ERROR_STRING,
+       TW_CL_SEVERITY_WARNING_STRING,
+       TW_CL_SEVERITY_INFO_STRING,
+       TW_CL_SEVERITY_DEBUG_STRING,
+       ""
+};
+
+
+
+/*
+ * Function name:      tw_cli_drain_complete_queue
+ * Description:                This function gets called during a controller reset.
+ *                     It errors back to the OS Layer, all those requests that
+ *                     are in the complete queue, at the time of the reset.
+ *                     Any CL internal requests will be simply freed.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_drain_complete_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+       struct tw_cl_req_packet         *req_pkt;
+
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Walk the busy queue. */
+       while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q))) {
+               if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
+                       /*
+                        * It's an internal request.  Set the appropriate
+                        * error and call the CL internal callback if there's
+                        * one.  If the request originator is polling for
+                        * completion, he should be checking req->error to
+                        * determine that the request did not go through.
+                        * The request originators are responsible for the
+                        * clean-up.
+                        */
+                       req->error_code = TW_CL_ERR_REQ_BUS_RESET;
+                       if (req->tw_cli_callback)
+                               req->tw_cli_callback(req);
+               } else {
+                       if ((req_pkt = req->orig_req)) {
+                               /* It's a SCSI request.  Complete it. */
+                               tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "Completing complete request %p "
+                                       "on reset",
+                                       req);
+                               req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+                               req_pkt->tw_osl_callback(req->req_handle);
+                       }
+                       tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+               }
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_drain_busy_queue
+ * Description:                This function gets called during a controller reset.
+ *                     It errors back to the OS Layer, all those requests that
+ *                     were pending with the firmware, at the time of the
+ *                     reset.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_drain_busy_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+       struct tw_cl_req_packet         *req_pkt;
+
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Walk the busy queue. */
+       while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_BUSY_Q))) {
+               if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
+                       /*
+                        * It's an internal request.  Set the appropriate
+                        * error and call the CL internal callback if there's
+                        * one.  If the request originator is polling for
+                        * completion, he should be checking req->error to
+                        * determine that the request did not go through.
+                        * The request originators are responsible for the
+                        * clean-up.
+                        */
+                       req->error_code = TW_CL_ERR_REQ_BUS_RESET;
+                       if (req->tw_cli_callback)
+                               req->tw_cli_callback(req);
+               } else {
+                       if ((req_pkt = req->orig_req)) {
+                               /* It's a SCSI request.  Complete it. */
+                               tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "Completing busy request %p on reset",
+                                       req);
+                               req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+                               req_pkt->tw_osl_callback(req->req_handle);
+                       }
+                       tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+               }
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_drain_pending_queue
+ * Description:                This function gets called during a controller reset.
+ *                     It errors back to the OS Layer, all those requests that
+ *                     were in the pending queue, at the time of the reset.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+
+TW_VOID
+tw_cli_drain_pending_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+       struct tw_cl_req_packet         *req_pkt;
+
+       tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /*
+        * Pull requests off the pending queue, and complete them.
+        */
+       while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q))) {
+               if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
+                       /*
+                        * It's an internal request.  Set the appropriate
+                        * error and call the CL internal callback if there's
+                        * one.  If the request originator is polling for
+                        * completion, he should be checking req->error to
+                        * determine that the request did not go through.
+                        * The request originators are responsible for the
+                        * clean-up.
+                        */
+                       req->error_code = TW_CL_ERR_REQ_BUS_RESET;
+                       if (req->tw_cli_callback)
+                               req->tw_cli_callback(req);
+               } else {
+                       if ((req_pkt = req->orig_req)) {
+                               /* It's an external request.  Complete it. */
+                               tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+                                       tw_osl_cur_func(),
+                                       "Completing pending request %p "
+                                       "on reset", req);
+                               req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+                               req_pkt->tw_osl_callback(req->req_handle);
+                       }
+                       tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+               }
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_drain_response_queue
+ * Description:                Drain the controller response queue.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_drain_response_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       TW_UINT32       resp;
+       TW_UINT32       status_reg;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       for (;;) {
+               status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+
+               if (tw_cli_check_ctlr_state(ctlr, status_reg))
+                       return(TW_OSL_EGENFAILURE);
+
+               if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
+                       return(TW_OSL_ESUCCESS); /* no more response queue entries */
+
+               resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_find_response
+ * Description:                Find a particular response in the ctlr response queue.
+ *
+ * Input:              ctlr    -- ptr to per ctlr structure
+ *                     req_id  -- request id of the response to look for
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_find_response(struct tw_cli_ctlr_context *ctlr, TW_INT32 req_id)
+{
+       TW_UINT32       resp;
+       TW_INT32        resp_id;
+       TW_UINT32       status_reg;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       for (;;) {
+               status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+
+               if (tw_cli_check_ctlr_state(ctlr, status_reg))
+                       return(TW_OSL_EGENFAILURE);
+
+               if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
+                       return(TW_OSL_ENOTTY); /* no more response queue entries */
+
+               if (ctlr->device_id == TW_CL_DEVICE_ID_9K) {
+                       resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
+                       resp_id = GET_RESP_ID(resp);
+               } else {
+                       resp = TW_CLI_READ_LARGE_RESPONSE_QUEUE(
+                               ctlr->ctlr_handle);
+                       resp_id = GET_LARGE_RESP_ID(resp);
+               }
+               if (resp_id == req_id)
+                       return(TW_OSL_ESUCCESS); /* found the req_id */
+       }
+}
+
+
+
+/*
+ * Function name:      tw_cli_drain_aen_queue
+ * Description:                Fetches all un-retrieved AEN's posted by fw.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_drain_aen_queue(struct tw_cli_ctlr_context *ctlr)
+{
+       struct tw_cli_req_context       *req;
+       struct tw_cl_command_header     *cmd_hdr;
+       TW_TIME                         end_time;
+       TW_UINT16                       aen_code;
+       TW_INT32                        error;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       for (;;) {
+               if ((req = tw_cli_get_request(ctlr
+                       )) == TW_CL_NULL) {
+                       error = TW_OSL_EBUSY;
+                       break;
+               }
+
+               req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
+               req->tw_cli_callback = TW_CL_NULL;
+               if ((error = tw_cli_send_scsi_cmd(req,
+                               0x03 /* REQUEST_SENSE */))) {
+                       tw_cli_dbg_printf(1, ctlr->ctlr_handle,
+                               tw_osl_cur_func(),
+                               "Cannot send command to fetch aen");
+                       break;
+               }
+
+               end_time = tw_osl_get_local_time() +
+                       TW_CLI_REQUEST_TIMEOUT_PERIOD;
+               do {
+                       if ((error = req->error_code))
+                               /*
+                                * This will take care of completion due to
+                                * a reset, or a failure in
+                                * tw_cli_submit_pending_queue.
+                                */
+                               goto out;
+
+                       tw_cli_process_resp_intr(req->ctlr);
+
+                       if ((req->state != TW_CLI_REQ_STATE_BUSY) &&
+                               (req->state != TW_CLI_REQ_STATE_PENDING))
+                               break;
+               } while (tw_osl_get_local_time() <= end_time);
+
+               if (req->state != TW_CLI_REQ_STATE_COMPLETE) {
+                       error = TW_OSL_ETIMEDOUT;
+                       break;
+               }
+
+               if ((error = req->cmd_pkt->command.cmd_pkt_9k.status)) {
+                       cmd_hdr = &req->cmd_pkt->cmd_hdr;
+                       tw_cli_create_ctlr_event(ctlr,
+                               TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                               cmd_hdr);
+                       break;
+               }
+
+               aen_code = tw_cli_manage_aen(ctlr, req);
+               if (aen_code == TWA_AEN_QUEUE_EMPTY)
+                       break;
+               if (aen_code == TWA_AEN_SYNC_TIME_WITH_HOST)
+                       continue;
+
+               ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+
+out:
+       if (req) {
+               if (req->data)
+                       ctlr->internal_req_busy = TW_CL_FALSE;
+               tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+       }
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_find_aen
+ * Description:                Reports whether a given AEN ever occurred.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ *                     aen_code-- AEN to look for
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_find_aen(struct tw_cli_ctlr_context *ctlr, TW_UINT16 aen_code)
+{
+       TW_UINT32       last_index;
+       TW_INT32        i;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       if (ctlr->aen_q_wrapped)
+               last_index = ctlr->aen_head;
+       else
+               last_index = 0;
+
+       i = ctlr->aen_head;
+       do {
+               i = (i + ctlr->max_aens_supported - 1) %
+                       ctlr->max_aens_supported;
+               if (ctlr->aen_queue[i].aen_code == aen_code)
+                       return(TW_OSL_ESUCCESS);
+       } while (i != last_index);
+
+       return(TW_OSL_EGENFAILURE);
+}
+
+
+
+/*
+ * Function name:      tw_cli_poll_status
+ * Description:                Poll for a given status to show up in the firmware
+ *                     status register.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ *                     status  -- status to look for
+ *                     timeout -- max # of seconds to wait before giving up
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_cli_poll_status(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status,
+       TW_UINT32 timeout)
+{
+       TW_TIME         end_time;
+       TW_UINT32       status_reg;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       end_time = tw_osl_get_local_time() + timeout;
+       do {
+               status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+               if ((status_reg & status) == status)
+                       /* got the required bit(s) */
+                       return(TW_OSL_ESUCCESS);
+
+               tw_osl_delay(1000);
+       } while (tw_osl_get_local_time() <= end_time);
+
+       return(TW_OSL_ETIMEDOUT);
+}
+
+
+
+/*
+ * Function name:      tw_cl_create_event
+ * Description:                Creates and queues ctlr/CL/OSL AEN's to be
+ *                     supplied to user-space tools on request.
+ *                     Also notifies OS Layer.
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ *                     queue_event-- TW_CL_TRUE --> queue event;
+ *                                   TW_CL_FALSE--> don't queue event
+ *                                                     (simply notify OSL)
+ *                     event_src  -- source of event
+ *                     event_code -- AEN/error code
+ *                     severity -- severity of event
+ *                     severity_str--Text description of severity
+ *                     event_desc -- standard string related to the event/error
+ *                     event_specific_desc -- format string for additional
+ *                                             info about the event
+ *                     ... -- additional arguments conforming to the format
+ *                             specified by event_specific_desc
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cl_create_event(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_UINT8 queue_event, TW_UINT8 event_src, TW_UINT16 event_code,
+       TW_UINT8 severity, TW_UINT8 *severity_str, TW_UINT8 *event_desc,
+       TW_UINT8 *event_specific_desc, ...)
+{
+       struct tw_cli_ctlr_context      *ctlr = ctlr_handle->cl_ctlr_ctxt;
+       struct tw_cl_event_packet       event_pkt;
+       struct tw_cl_event_packet       *event;
+       TW_UINT32                       aen_head;
+       va_list                         ap;
+
+       tw_cli_dbg_printf(8, ctlr_handle, tw_osl_cur_func(), "entered");
+
+       if ((ctlr) && (queue_event)) {
+               /* Protect access to ctlr->aen_head. */
+               tw_osl_get_lock(ctlr_handle, ctlr->gen_lock);
+
+               aen_head = ctlr->aen_head;
+               ctlr->aen_head = (aen_head + 1) % ctlr->max_aens_supported;
+
+               /* Queue the event. */
+               event = &(ctlr->aen_queue[aen_head]);
+               tw_osl_memzero(event->parameter_data,
+                       sizeof(event->parameter_data));
+
+               if (event->retrieved == TW_CL_AEN_NOT_RETRIEVED)
+                       ctlr->aen_q_overflow = TW_CL_TRUE;
+               event->sequence_id = ++(ctlr->aen_cur_seq_id);
+               if ((aen_head + 1) == ctlr->max_aens_supported) {
+                       tw_cli_dbg_printf(4, ctlr->ctlr_handle,
+                               tw_osl_cur_func(), "AEN queue wrapped");
+                       ctlr->aen_q_wrapped = TW_CL_TRUE;
+               }
+
+               /* Free access to ctlr->aen_head. */
+               tw_osl_free_lock(ctlr_handle, ctlr->gen_lock);
+       } else {
+               event = &event_pkt;
+               tw_osl_memzero(event, sizeof(struct tw_cl_event_packet));
+       }
+
+       event->event_src = event_src;
+       event->time_stamp_sec = (TW_UINT32)tw_osl_get_local_time();
+       event->aen_code = event_code;
+       event->severity = severity;
+       tw_osl_strcpy(event->severity_str, severity_str);
+       event->retrieved = TW_CL_AEN_NOT_RETRIEVED;
+
+       va_start(ap, event_specific_desc);
+       tw_osl_vsprintf(event->parameter_data, event_specific_desc, ap);
+       va_end(ap);
+
+       event->parameter_len =
+               (TW_UINT8)(tw_osl_strlen(event->parameter_data));
+       tw_osl_strcpy(event->parameter_data + event->parameter_len + 1,
+               event_desc);
+       event->parameter_len += (1 + tw_osl_strlen(event_desc));
+
+       tw_cli_dbg_printf(4, ctlr_handle, tw_osl_cur_func(),
+               "event = %x %x %x %x %x %x %x\n %s",
+               event->sequence_id,
+               event->time_stamp_sec,
+               event->aen_code,
+               event->severity,
+               event->retrieved,
+               event->repeat_count,
+               event->parameter_len,
+               event->parameter_data);
+
+       tw_osl_notify_event(ctlr_handle, event);
+}
+
+
+
+/*
+ * Function name:      tw_cli_get_request
+ * Description:                Gets a request pkt from the free queue.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ *                     req_pkt -- ptr to OSL built req_pkt, if there's one
+ * Output:             None
+ * Return value:       ptr to request pkt      -- success
+ *                     TW_CL_NULL              -- failure
+ */
+struct tw_cli_req_context *
+tw_cli_get_request(struct tw_cli_ctlr_context *ctlr
+       )
+{
+       struct tw_cli_req_context       *req;
+
+       tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       {
+               /* Get a free request packet. */
+               req = tw_cli_req_q_remove_head(ctlr, TW_CLI_FREE_Q);
+       }
+
+       /* Initialize some fields to their defaults. */
+       if (req) {
+               req->req_handle = TW_CL_NULL;
+               req->data = TW_CL_NULL;
+               req->length = 0;
+               req->data_phys = 0;
+               req->state = TW_CLI_REQ_STATE_INIT; /* req being initialized */
+               req->flags = 0;
+               req->error_code = 0;
+               req->orig_req = TW_CL_NULL;
+               req->tw_cli_callback = TW_CL_NULL;
+
+               /*
+                * Look at the status field in the command packet to see how
+                * it completed the last time it was used, and zero out only
+                * the portions that might have changed.  Note that we don't
+                * care to zero out the sglist.
+                */
+               if (req->cmd_pkt->command.cmd_pkt_9k.status)
+                       tw_osl_memzero(req->cmd_pkt,
+                               sizeof(struct tw_cl_command_header) +
+                               28 /* max bytes before sglist */);
+               else
+                       tw_osl_memzero(&(req->cmd_pkt->command),
+                               28 /* max bytes before sglist */);
+
+       }
+       return(req);
+}
+
+
+
+/*
+ * Function name:      tw_cli_dbg_printf
+ * Description:                Calls OSL print function if dbg_level is appropriate
+ *
+ * Input:              dbg_level -- Determines whether or not to print
+ *                     ctlr_handle -- controller handle
+ *                     cur_func -- text name of calling function
+ *                     fmt -- format string for the arguments to follow
+ *                     ... -- variable number of arguments, to be printed
+ *                             based on the fmt string
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_dbg_printf(TW_UINT8 dbg_level,
+       struct tw_cl_ctlr_handle *ctlr_handle, const TW_INT8 *cur_func,
+       TW_INT8 *fmt, ...)
+{
+#ifdef TW_OSL_DEBUG
+       TW_INT8 print_str[256];
+       va_list ap;
+
+       tw_osl_memzero(print_str, 256);
+       if (dbg_level <= TW_OSL_DEBUG_LEVEL_FOR_CL) {
+               tw_osl_sprintf(print_str, "%s: ", cur_func);
+
+               va_start(ap, fmt);
+               tw_osl_vsprintf(print_str + tw_osl_strlen(print_str), fmt, ap);
+               va_end(ap);
+
+               tw_osl_strcpy(print_str + tw_osl_strlen(print_str), "\n");
+               tw_osl_dbg_printf(ctlr_handle, print_str);
+       }
+#endif /* TW_OSL_DEBUG */
+}
+
+
+
+/*
+ * Function name:      tw_cli_notify_ctlr_info
+ * Description:                Notify OSL of controller info (fw/BIOS versions, etc.).
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cli_notify_ctlr_info(struct tw_cli_ctlr_context *ctlr)
+{
+       TW_INT8         fw_ver[16];
+       TW_INT8         bios_ver[16];
+       TW_INT8         ctlr_model[16];
+       TW_INT32        error[3];
+       TW_UINT8        num_ports = 0;
+
+       tw_cli_dbg_printf(5, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Get the port count. */
+       error[0] = tw_cli_get_param(ctlr, TWA_PARAM_CONTROLLER_TABLE,
+                       TWA_PARAM_CONTROLLER_PORT_COUNT, &num_ports,
+                       1, TW_CL_NULL);
+
+       /* Get the firmware and BIOS versions. */
+       error[0] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
+                       TWA_PARAM_VERSION_FW, fw_ver, 16, TW_CL_NULL);
+       error[1] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
+                       TWA_PARAM_VERSION_BIOS, bios_ver, 16, TW_CL_NULL);
+       error[2] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
+                       TWA_PARAM_CTLR_MODEL, ctlr_model, 16, TW_CL_NULL);
+
+       tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+               0x1300, 0x3, TW_CL_SEVERITY_INFO_STRING,
+               "Controller details:",
+               "Model %.16s, %d ports, Firmware %.16s, BIOS %.16s",
+               error[2]?(TW_INT8 *)TW_CL_NULL:ctlr_model,
+               num_ports,
+               error[0]?(TW_INT8 *)TW_CL_NULL:fw_ver,
+               error[1]?(TW_INT8 *)TW_CL_NULL:bios_ver);
+}
+
+
+
+/*
+ * Function name:      tw_cli_check_ctlr_state
+ * Description:                Makes sure that the fw status register reports a
+ *                     proper status.
+ *
+ * Input:              ctlr    -- ptr to CL internal ctlr context
+ *                     status_reg-- value in the status register
+ * Output:             None
+ * Return value:       0       -- no errors
+ *                     non-zero-- errors
+ */
+TW_INT32
+tw_cli_check_ctlr_state(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status_reg)
+{
+       struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
+       TW_INT32                        error = TW_OSL_ESUCCESS;
+
+       tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+       /* Check if the 'micro-controller ready' bit is not set. */
+       if (!(status_reg & TWA_STATUS_MICROCONTROLLER_READY)) {
+               TW_INT8 desc[200];
+
+               tw_osl_memzero(desc, 200);
+               if (!(ctlr->reset_phase1_in_progress)) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                               0x1301, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Missing expected status bit(s)",
+                               "status reg = 0x%x; Missing bits: %s",
+                               status_reg,
+                               tw_cli_describe_bits(
+                                       TWA_STATUS_MICROCONTROLLER_READY,
+                                       desc));
+                       error = TW_OSL_EGENFAILURE;
+               }
+       }
+
+       /* Check if any error bits are set. */
+       if ((status_reg & TWA_STATUS_UNEXPECTED_BITS) != 0) {
+               TW_INT8 desc[200];
+
+               tw_osl_memzero(desc, 200);
+
+               /* Skip queue error msgs during 9650SE/9690SA reset */
+               if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) &&
+                    (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) ||
+                   (!(ctlr->reset_in_progress)) ||
+                   ((status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) == 0))
+               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                       0x1302, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                       "Unexpected status bit(s)",
+                       "status reg = 0x%x Unexpected bits: %s",
+                       status_reg & TWA_STATUS_UNEXPECTED_BITS,
+                       tw_cli_describe_bits(status_reg &
+                               TWA_STATUS_UNEXPECTED_BITS, desc));
+
+               if (status_reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                               0x1303, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "PCI parity error: clearing... "
+                               "Re-seat/move/replace card",
+                               "status reg = 0x%x %s",
+                               status_reg,
+                               tw_cli_describe_bits(status_reg, desc));
+                       TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+                               TWA_CONTROL_CLEAR_PARITY_ERROR);
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+                       tw_osl_write_pci_config(ctlr->ctlr_handle,
+                               TW_CLI_PCI_CONFIG_STATUS_OFFSET,
+                               TWA_PCI_CONFIG_CLEAR_PARITY_ERROR, 2);
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+               }
+
+               if (status_reg & TWA_STATUS_PCI_ABORT_INTERRUPT) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                               0x1304, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "PCI abort: clearing... ",
+                               "status reg = 0x%x %s",
+                               status_reg,
+                               tw_cli_describe_bits(status_reg, desc));
+                       TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+                               TWA_CONTROL_CLEAR_PCI_ABORT);
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+                       tw_osl_write_pci_config(ctlr->ctlr_handle,
+                               TW_CLI_PCI_CONFIG_STATUS_OFFSET,
+                               TWA_PCI_CONFIG_CLEAR_PCI_ABORT, 2);
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+               }
+
+               if (status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) {
+                       /* Skip queue error msgs during 9650SE/9690SA reset */
+                       if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) &&
+                            (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) ||
+                           (!(ctlr->reset_in_progress)))
+                               tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                                                  TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                                                  0x1305, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                                                  "Controller queue error: clearing... ",
+                                                  "status reg = 0x%x %s",
+                                                  status_reg,
+                                                  tw_cli_describe_bits(status_reg, desc));
+                       TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+                               TWA_CONTROL_CLEAR_QUEUE_ERROR);
+               }
+
+               if (status_reg & TWA_STATUS_MICROCONTROLLER_ERROR) {
+                       tw_cl_create_event(ctlr_handle, TW_CL_TRUE,
+                               TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
+                               0x1307, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Micro-controller error! ",
+                               "status reg = 0x%x %s",
+                               status_reg,
+                               tw_cli_describe_bits(status_reg, desc));
+                       error = TW_OSL_EGENFAILURE;
+               }
+       }
+       return(error);
+}
+
+
+
+/*
+ * Function name:      tw_cli_describe_bits
+ * Description:                Given the value of the status register, returns a
+ *                     string describing the meaning of each set bit.
+ *
+ * Input:              reg -- status register value
+ * Output:             Pointer to a string describing each set bit
+ * Return value:       Pointer to the string describing each set bit
+ */
+TW_INT8        *
+tw_cli_describe_bits(TW_UINT32 reg, TW_INT8 *str)
+{
+       tw_osl_strcpy(str, "[");
+
+       if (reg & TWA_STATUS_COMMAND_QUEUE_EMPTY)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_EMPTY,");
+       if (reg & TWA_STATUS_MICROCONTROLLER_READY)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "MC_RDY,");
+       if (reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_Q_EMPTY,");
+       if (reg & TWA_STATUS_COMMAND_QUEUE_FULL)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_FULL,");
+       if (reg & TWA_STATUS_RESPONSE_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_INTR,");
+       if (reg & TWA_STATUS_COMMAND_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_INTR,");
+       if (reg & TWA_STATUS_ATTENTION_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "ATTN_INTR,");
+       if (reg & TWA_STATUS_HOST_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "HOST_INTR,");
+       if (reg & TWA_STATUS_PCI_ABORT_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_ABRT,");
+       if (reg & TWA_STATUS_MICROCONTROLLER_ERROR)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "MC_ERR,");
+       if (reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "Q_ERR,");
+       if (reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT)
+               tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_PERR");
+
+       tw_osl_strcpy(&str[tw_osl_strlen(str)], "]");
+       return(str);
+}
+
+
+
+#ifdef TW_OSL_DEBUG
+
+/*
+ * Function name:      tw_cl_print_ctlr_stats
+ * Description:                Prints the current status of the controller.
+ *
+ * Input:              ctlr_handle-- controller handle
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cl_print_ctlr_stats(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+       struct tw_cli_ctlr_context      *ctlr =
+               (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+       TW_UINT32                       status_reg;
+       TW_INT8                         desc[200];
+
+       tw_cli_dbg_printf(7, ctlr->ctlr_handle, "", "entered");
+
+       /* Print current controller details. */
+       tw_cli_dbg_printf(0, ctlr_handle, "", "cl_ctlr_ctxt = %p", ctlr);
+
+       tw_osl_memzero(desc, 200);
+       status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
+       tw_cli_dbg_printf(0, ctlr_handle, "", "status reg = 0x%x %s",
+               status_reg, tw_cli_describe_bits(status_reg, desc));
+
+       tw_cli_dbg_printf(0, ctlr_handle, "", "CLq type  current  max");
+       tw_cli_dbg_printf(0, ctlr_handle, "", "free      %04d     %04d",
+               ctlr->q_stats[TW_CLI_FREE_Q].cur_len,
+               ctlr->q_stats[TW_CLI_FREE_Q].max_len);
+       tw_cli_dbg_printf(0, ctlr_handle, "", "busy      %04d     %04d",
+               ctlr->q_stats[TW_CLI_BUSY_Q].cur_len,
+               ctlr->q_stats[TW_CLI_BUSY_Q].max_len);
+       tw_cli_dbg_printf(0, ctlr_handle, "", "pending   %04d     %04d",
+               ctlr->q_stats[TW_CLI_PENDING_Q].cur_len,
+               ctlr->q_stats[TW_CLI_PENDING_Q].max_len);
+       tw_cli_dbg_printf(0, ctlr_handle, "", "complete  %04d     %04d",
+               ctlr->q_stats[TW_CLI_COMPLETE_Q].cur_len,
+               ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len);
+       tw_cli_dbg_printf(0, ctlr_handle, "", "AEN queue head %d  tail %d",
+                       ctlr->aen_head, ctlr->aen_tail);
+}
+
+
+
+/*
+ * Function name:      tw_cl_reset_stats
+ * Description:                Resets CL maintained statistics for the controller.
+ *
+ * Input:              ctlr_handle-- controller handle
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cl_reset_stats(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+       struct tw_cli_ctlr_context      *ctlr =
+               (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+
+       tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(), "entered");
+       ctlr->q_stats[TW_CLI_FREE_Q].max_len = 0;
+       ctlr->q_stats[TW_CLI_BUSY_Q].max_len = 0;
+       ctlr->q_stats[TW_CLI_PENDING_Q].max_len = 0;
+       ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len = 0;
+}
+
+
+
+/*
+ * Function name:      tw_cli_print_req_info
+ * Description:                Prints CL internal details of a given request.
+ *
+ * Input:              req     -- ptr to CL internal request context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_cl_print_req_info(struct tw_cl_req_handle *req_handle)
+{
+       struct tw_cli_req_context       *req = req_handle->cl_req_ctxt;
+       struct tw_cli_ctlr_context      *ctlr = req->ctlr;
+       struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
+       struct tw_cl_command_packet     *cmd_pkt = req->cmd_pkt;
+       struct tw_cl_command_9k         *cmd9k;
+       union tw_cl_command_7k          *cmd7k;
+       TW_UINT8                        *cdb;
+       TW_VOID                         *sgl;
+       TW_UINT32                       sgl_entries;
+       TW_UINT32                       i;
+
+       tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+               "CL details for request:");
+       tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+               "req_handle = %p, ctlr = %p,\n"
+               "cmd_pkt = %p, cmd_pkt_phys = 0x%llx,\n"
+               "data = %p, length = 0x%x, data_phys = 0x%llx,\n"
+               "state = 0x%x, flags = 0x%x, error = 0x%x,\n"
+               "orig_req = %p, callback = %p, req_id = 0x%x,\n"
+               "next_req = %p, prev_req = %p",
+               req_handle, ctlr,
+               cmd_pkt, req->cmd_pkt_phys,
+               req->data, req->length, req->data_phys,
+               req->state, req->flags, req->error_code,
+               req->orig_req, req->tw_cli_callback, req->request_id,
+               req->link.next, req->link.prev);
+
+       if (req->flags & TW_CLI_REQ_FLAGS_9K) {
+               cmd9k = &(cmd_pkt->command.cmd_pkt_9k);
+               sgl = cmd9k->sg_list;
+               sgl_entries = TW_CL_SWAP16(
+                       GET_SGL_ENTRIES(cmd9k->lun_h4__sgl_entries));
+               tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+                       "9K cmd: opcode = 0x%x, unit = 0x%x, req_id = 0x%x,\n"
+                       "status = 0x%x, sgl_offset = 0x%x, sgl_entries = 0x%x",
+                       GET_OPCODE(cmd9k->res__opcode),
+                       cmd9k->unit,
+                       TW_CL_SWAP16(GET_REQ_ID(cmd9k->lun_l4__req_id)),
+                       cmd9k->status,
+                       cmd9k->sgl_offset,
+                       sgl_entries);
+
+               cdb = (TW_UINT8 *)(cmd9k->cdb);
+               tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+                       "CDB: %x %x %x %x %x %x %x %x"
+                       "%x %x %x %x %x %x %x %x",
+                       cdb[0], cdb[1], cdb[2], cdb[3],
+                       cdb[4], cdb[5], cdb[6], cdb[7],
+                       cdb[8], cdb[9], cdb[10], cdb[11],
+                       cdb[12], cdb[13], cdb[14], cdb[15]);
+       } else {
+               cmd7k = &(cmd_pkt->command.cmd_pkt_7k);
+               sgl = cmd7k->param.sgl;
+               sgl_entries = (cmd7k->generic.size -
+                       GET_SGL_OFF(cmd7k->generic.sgl_off__opcode)) /
+                       ((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2);
+               tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+                       "7K cmd: opcode = 0x%x, sgl_offset = 0x%x,\n"
+                       "size = 0x%x, req_id = 0x%x, unit = 0x%x,\n"
+                       "status = 0x%x, flags = 0x%x, count = 0x%x",
+                       GET_OPCODE(cmd7k->generic.sgl_off__opcode),
+                       GET_SGL_OFF(cmd7k->generic.sgl_off__opcode),
+                       cmd7k->generic.size,
+                       TW_CL_SWAP16(cmd7k->generic.request_id),
+                       GET_UNIT(cmd7k->generic.host_id__unit),
+                       cmd7k->generic.status,
+                       cmd7k->generic.flags,
+                       TW_CL_SWAP16(cmd7k->generic.count));
+       }
+
+       tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), "SG entries:");
+
+       if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
+               struct tw_cl_sg_desc64 *sgl64 = (struct tw_cl_sg_desc64 *)sgl;
+
+               for (i = 0; i < sgl_entries; i++) {
+                       tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+                               "0x%llx  0x%x",
+                               sgl64[i].address, sgl64[i].length);
+               }
+       } else {
+               struct tw_cl_sg_desc32 *sgl32 = (struct tw_cl_sg_desc32 *)sgl;
+
+               for (i = 0; i < sgl_entries; i++) {
+                       tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
+                               "0x%x  0x%x",
+                               sgl32[i].address, sgl32[i].length);
+               }
+       }
+}
+
+#endif /* TW_OSL_DEBUG */
diff --git a/sys/dev/raid/twa/tw_cl_share.h b/sys/dev/raid/twa/tw_cl_share.h
new file mode 100644 (file)
index 0000000..ec8326d
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_cl_share.h,v 1.7 2010/07/09 17:38:15 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+
+#ifndef TW_CL_SHARE_H
+
+#define TW_CL_SHARE_H
+
+
+/*
+ * Macros, structures and functions shared between OSL and CL,
+ * and defined by CL.
+ */
+
+#define TW_CL_NULL                     ((TW_VOID *)0)
+#define TW_CL_TRUE                     1
+#define TW_CL_FALSE                    0
+
+#define TW_CL_VENDOR_ID                        0x13C1  /* 3ware vendor id */
+#define TW_CL_DEVICE_ID_9K             0x1002  /* 9000 PCI series device id */
+#define TW_CL_DEVICE_ID_9K_X           0x1003  /* 9000 PCI-X series device id */
+#define TW_CL_DEVICE_ID_9K_E           0x1004  /* 9000 PCIe series device id */
+#define TW_CL_DEVICE_ID_9K_SA          0x1005  /* 9000 PCIe SAS series device id */
+
+#define TW_CL_BAR_TYPE_IO              1       /* I/O base address */
+#define TW_CL_BAR_TYPE_MEM             2       /* memory base address */
+#define TW_CL_BAR_TYPE_SBUF            3       /* SBUF base address */
+
+#ifdef TW_OSL_ENCLOSURE_SUPPORT
+#define TW_CL_MAX_NUM_UNITS            65      /* max # of units we support
+                                               -- enclosure target id is 64 */
+#else /* TW_OSL_ENCLOSURE_SUPPORT */
+#define TW_CL_MAX_NUM_UNITS            32      /* max # of units we support */
+#endif /* TW_OSL_ENCLOSURE_SUPPORT */
+
+#define TW_CL_MAX_NUM_LUNS             16      /* max # of LUN's we support */
+#define TW_CL_MAX_IO_SIZE              0x20000 /* 128K */
+
+/*
+ * Though we can support 256 simultaneous requests, we advertise as capable
+ * of supporting only 255, since we want to keep one CL internal request
+ * context packet always available for internal requests.
+ */
+#define TW_CL_MAX_SIMULTANEOUS_REQUESTS        256     /* max simult reqs supported */
+
+#define TW_CL_MAX_32BIT_SG_ELEMENTS    109     /* max 32-bit sg elements */
+#define TW_CL_MAX_64BIT_SG_ELEMENTS    72      /* max 64-bit sg elements */
+
+
+/* Possible values of ctlr->flags */
+#define TW_CL_64BIT_ADDRESSES  (1<<0) /* 64 bit cmdpkt & SG addresses */
+#define TW_CL_64BIT_SG_LENGTH  (1<<1) /* 64 bit SG length */
+#define TW_CL_START_CTLR_ONLY  (1<<2) /* Start ctlr only */
+#define TW_CL_STOP_CTLR_ONLY   (1<<3) /* Stop ctlr only */
+#define TW_CL_DEFERRED_INTR_USED (1<<5) /* OS Layer uses deferred intr */
+
+/* Possible error values from the Common Layer. */
+#define TW_CL_ERR_REQ_SUCCESS                  0
+#define TW_CL_ERR_REQ_GENERAL_FAILURE          (1<<0)
+#define TW_CL_ERR_REQ_INVALID_TARGET           (1<<1)
+#define TW_CL_ERR_REQ_INVALID_LUN              (1<<2)
+#define TW_CL_ERR_REQ_SCSI_ERROR               (1<<3)
+#define TW_CL_ERR_REQ_AUTO_SENSE_VALID         (1<<4)
+#define TW_CL_ERR_REQ_BUS_RESET                        (1<<5)
+#define TW_CL_ERR_REQ_UNABLE_TO_SUBMIT_COMMAND (1<<6)
+
+
+/* Possible values of req_pkt->flags */
+#define TW_CL_REQ_RETRY_ON_BUSY                (1<<0)
+#define TW_CL_REQ_CALLBACK_FOR_SGLIST  (1<<1)
+
+
+#define TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR  3
+#define TW_CL_MESSAGE_SOURCE_CONTROLLER_EVENT  4
+#define TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR        21
+#define TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT        22
+#define TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER    5
+#define TW_CL_MESSAGE_SOURCE_FREEBSD_OS                8
+#define TW_CL_MESSAGE_SOURCE_WINDOWS_DRIVER    7
+#define TW_CL_MESSAGE_SOURCE_WINDOWS_OS                10
+
+#define TW_CL_SEVERITY_ERROR           0x1
+#define TW_CL_SEVERITY_WARNING         0x2
+#define TW_CL_SEVERITY_INFO            0x3
+#define TW_CL_SEVERITY_DEBUG           0x4
+
+#define TW_CL_SEVERITY_ERROR_STRING    "ERROR"
+#define TW_CL_SEVERITY_WARNING_STRING  "WARNING"
+#define TW_CL_SEVERITY_INFO_STRING     "INFO"
+#define TW_CL_SEVERITY_DEBUG_STRING    "DEBUG"
+
+
+
+/*
+ * Structure, a pointer to which is used as the controller handle in
+ * communications between the OS Layer and the Common Layer.
+ */
+struct tw_cl_ctlr_handle {
+       TW_VOID *osl_ctlr_ctxt; /* OSL's ctlr context */
+       TW_VOID *cl_ctlr_ctxt;  /* CL's ctlr context */
+};
+
+
+/*
+ * Structure, a pointer to which is used as the request handle in
+ * communications between the OS Layer and the Common Layer.
+ */
+struct tw_cl_req_handle {
+       TW_VOID *osl_req_ctxt;  /* OSL's request context */
+       TW_VOID *cl_req_ctxt;   /* CL's request context */
+       TW_UINT8 is_io;         /* Only freeze/release simq for IOs */
+};
+
+
+/* Structure used to describe SCSI requests to CL. */
+struct tw_cl_scsi_req_packet {
+       TW_UINT32       unit;           /* unit # to send cmd to */
+       TW_UINT32       lun;            /* LUN to send cmd to */
+       TW_UINT8        *cdb;           /* ptr to SCSI cdb */
+       TW_UINT32       cdb_len;        /* # of valid cdb bytes */
+       TW_UINT32       sense_len;      /* # of bytes of valid sense info */
+       TW_UINT8        *sense_data;    /* ptr to sense data, if any */
+       TW_UINT32       scsi_status;    /* SCSI status returned by fw */
+       TW_UINT32       sgl_entries;    /* # of SG descriptors */
+       TW_UINT8        *sg_list;       /* ptr to SG list */
+};
+
+
+/* Structure used to describe pass through command packets to CL. */
+struct tw_cl_passthru_req_packet {
+       TW_UINT8        *cmd_pkt;       /* ptr to passthru cmd pkt */
+       TW_UINT32       cmd_pkt_length; /* size of cmd pkt */
+       TW_UINT32       sgl_entries;    /* # of SG descriptors */
+       TW_UINT8        *sg_list;       /* ptr to SG list */
+};
+
+
+/* Request packet submitted to the Common Layer, by the OS Layer. */
+struct tw_cl_req_packet {
+       TW_UINT32       cmd;            /* Common Layer cmd */
+       TW_UINT32       flags;          /* flags describing request */
+       TW_UINT32       status;         /* Common Layer returned status */
+       TW_VOID         (*tw_osl_callback)(struct tw_cl_req_handle *req_handle);
+                       /* OSL routine to be called by CL on req completion */
+       TW_VOID         (*tw_osl_sgl_callback)(
+                       struct tw_cl_req_handle *req_handle, TW_VOID *sg_list,
+                       TW_UINT32 *num_sgl_entries);
+                       /* OSL callback to get SG list. */
+
+       union {
+               struct tw_cl_scsi_req_packet            scsi_req; /* SCSI req */
+               struct tw_cl_passthru_req_packet        pt_req;/*Passthru req*/
+       } gen_req_pkt;
+};
+
+
+#pragma pack(1)
+/*
+ * Packet that describes an AEN/error generated by the controller,
+ * Common Layer, or even the OS Layer.
+ */
+struct tw_cl_event_packet {
+       TW_UINT32       sequence_id;
+       TW_UINT32       time_stamp_sec;
+       TW_UINT16       aen_code;
+       TW_UINT8        severity;
+       TW_UINT8        retrieved;
+       TW_UINT8        repeat_count;
+       TW_UINT8        parameter_len;
+       TW_UINT8        parameter_data[98];
+       TW_UINT32       event_src;
+       TW_UINT8        severity_str[20];
+};
+#pragma pack()
+
+
+/* Structure to link 2 adjacent elements in a list. */
+struct tw_cl_link {
+       struct tw_cl_link       *next;
+       struct tw_cl_link       *prev;
+};
+
+
+#pragma pack(1)
+/* Scatter/Gather list entry with 32 bit addresses. */
+struct tw_cl_sg_desc32 {
+       TW_UINT32       address;
+       TW_UINT32       length;
+};
+
+
+/* Scatter/Gather list entry with 64 bit addresses. */
+struct tw_cl_sg_desc64 {
+       TW_UINT64       address;
+       TW_UINT32       length;
+};
+
+#pragma pack()
+
+
+/* Byte swap functions.  Valid only if running on big endian platforms. */
+#ifdef TW_OSL_BIG_ENDIAN
+
+#define TW_CL_SWAP16_WITH_CAST(x)                                      \
+       ((x << 8) | (x >> 8))
+
+
+#define TW_CL_SWAP32_WITH_CAST(x)                                      \
+       ((x << 24) | ((x << 8) & (0xFF0000)) |                          \
+       ((x >> 8) & (0xFF00)) | (x >> 24))
+
+
+#define TW_CL_SWAP64_WITH_CAST(x)                                      \
+       ((((TW_UINT64)(TW_CL_SWAP32(((TW_UINT32 *)(&(x)))[1]))) << 32) |\
+       ((TW_UINT32)(TW_CL_SWAP32(((TW_UINT32 *)(&(x)))[0]))))
+
+
+#else /* TW_OSL_BIG_ENDIAN */
+
+#define TW_CL_SWAP16_WITH_CAST(x)      x
+#define TW_CL_SWAP32_WITH_CAST(x)      x
+#define TW_CL_SWAP64_WITH_CAST(x)      x
+
+#endif /* TW_OSL_BIG_ENDIAN */
+
+#define TW_CL_SWAP16(x)                TW_CL_SWAP16_WITH_CAST((TW_UINT16)(x))
+#define TW_CL_SWAP32(x)                TW_CL_SWAP32_WITH_CAST((TW_UINT32)(x))
+#define TW_CL_SWAP64(x)                TW_CL_SWAP64_WITH_CAST((TW_UINT64)(x))
+
+
+/* Queue manipulation functions. */
+
+/* Initialize a queue. */
+#define TW_CL_Q_INIT(head)     do {            \
+       (head)->prev = (head)->next = head;     \
+} while (0)
+
+
+/* Insert an item at the head of the queue. */
+#define TW_CL_Q_INSERT_HEAD(head, item)        do {    \
+       (item)->next = (head)->next;            \
+       (item)->prev = head;                    \
+       (head)->next->prev = item;              \
+       (head)->next = item;                    \
+} while (0)
+
+
+/* Insert an item at the tail of the queue. */
+#define        TW_CL_Q_INSERT_TAIL(head, item) do {    \
+       (item)->next = head;                    \
+       (item)->prev = (head)->prev;            \
+       (head)->prev->next = item;              \
+       (head)->prev = item;                    \
+} while (0)
+
+
+/* Remove an item from the head of the queue. */
+#define TW_CL_Q_REMOVE_ITEM(head, item)        do {    \
+       (item)->prev->next = (item)->next;      \
+       (item)->next->prev = (item)->prev;      \
+} while (0)
+
+
+/* Retrieve the item at the head of the queue. */
+#define TW_CL_Q_FIRST_ITEM(head)               \
+       (((head)->next != head) ? ((head)->next) : TW_CL_NULL)
+
+
+/* Retrieve the item at the tail of the queue. */
+#define TW_CL_Q_LAST_ITEM(head)                        \
+       (((head)->prev != head) ? ((head)->prev) : TW_CL_NULL)
+
+
+/* Retrieve the item next to a given item in the queue. */
+#define TW_CL_Q_NEXT_ITEM(head, item)          \
+       (((item)->next != head) ? ((item)->next) : TW_CL_NULL)
+
+
+/* Retrieve the item previous to a given item in the queue. */
+#define TW_CL_Q_PREV_ITEM(head, item)          \
+       (((item)->prev != head) ? ((item)->prev) : TW_CL_NULL)
+
+
+/* Determine the offset of a field from the head of the structure it is in. */
+#define        TW_CL_STRUCT_OFFSET(struct_type, field) \
+       (TW_INT8 *)(&((struct_type *)0)->field)
+
+
+/*
+ * Determine the address of the head of a structure, given the address of a
+ * field within it.
+ */
+#define TW_CL_STRUCT_HEAD(addr, struct_type, field)    \
+       (struct_type *)((TW_INT8 *)addr -               \
+       TW_CL_STRUCT_OFFSET(struct_type, field))
+
+
+
+#ifndef TW_BUILDING_API
+
+#include "tw_osl_inline.h"
+
+
+
+/*
+ * The following are extern declarations of OS Layer defined functions called
+ * by the Common Layer.  If any function has been defined as a macro in
+ * tw_osl_share.h, we will not make the extern declaration here.
+ */
+
+#ifndef tw_osl_breakpoint
+/* Allows setting breakpoints in the CL code for debugging purposes. */
+extern TW_VOID tw_osl_breakpoint(TW_VOID);
+#endif
+
+
+#ifndef tw_osl_ctlr_busy
+/* Called when CL is too busy to accept new requests. */
+extern TW_VOID tw_osl_ctlr_busy(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_req_handle *req_handle);
+#endif
+
+
+#ifndef tw_osl_cur_func
+/* Text name of current function. */
+extern TW_INT8 *tw_osl_cur_func(TW_VOID);
+#endif
+
+
+#ifdef TW_OSL_DEBUG
+#ifndef tw_osl_dbg_printf
+/* Print to syslog/event log/debug console, as applicable. */
+extern TW_INT32 tw_osl_dbg_printf(struct tw_cl_ctlr_handle *ctlr_handle,
+       const TW_INT8 *fmt, ...);
+#endif
+#endif /* TW_OSL_DEBUG */
+
+
+#ifndef tw_osl_delay
+/* Cause a delay of usecs micro-seconds. */
+extern TW_VOID tw_osl_delay(TW_INT32 usecs);
+#endif
+
+
+#ifndef tw_osl_destroy_lock
+/* Create/initialize a lock for CL's use. */
+extern TW_VOID tw_osl_destroy_lock(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_LOCK_HANDLE *lock);
+#endif
+
+
+#ifndef tw_osl_free_lock
+/* Free a previously held lock. */
+extern TW_VOID tw_osl_free_lock(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_LOCK_HANDLE *lock);
+#endif
+
+
+#ifndef tw_osl_get_local_time
+/* Get local time. */
+extern TW_TIME tw_osl_get_local_time(TW_VOID);
+#endif
+
+
+#ifndef tw_osl_get_lock
+/* Acquire a lock. */
+extern TW_VOID tw_osl_get_lock(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_LOCK_HANDLE *lock);
+#endif
+
+
+#ifndef tw_osl_init_lock
+/* Create/initialize a lock for CL's use. */
+extern TW_VOID tw_osl_init_lock(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_INT8 *lock_name, TW_LOCK_HANDLE *lock);
+#endif
+
+
+#ifndef tw_osl_memcpy
+/* Copy 'size' bytes from 'src' to 'dest'. */
+extern TW_VOID tw_osl_memcpy(TW_VOID *src, TW_VOID *dest, TW_INT32 size);
+#endif
+
+
+#ifndef tw_osl_memzero
+/* Zero 'size' bytes starting at 'addr'. */
+extern TW_VOID tw_osl_memzero(TW_VOID *addr, TW_INT32 size);
+#endif
+
+
+#ifndef tw_osl_notify_event
+/* Notify OSL of a controller/CL (or even OSL) event. */
+extern TW_VOID tw_osl_notify_event(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_event_packet *event);
+#endif
+
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+#ifndef tw_osl_read_pci_config
+/* Read 'size' bytes from 'offset' in the PCI config space. */
+extern TW_UINT32 tw_osl_read_pci_config(
+       struct tw_cl_ctlr_handle *ctlr_handle, TW_INT32 offset, TW_INT32 size);
+#endif
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+
+#ifndef tw_osl_read_reg
+/* Read 'size' bytes at 'offset' from base address of this controller. */
+extern TW_UINT32 tw_osl_read_reg(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_INT32 offset, TW_INT32 size);
+#endif
+
+
+#ifndef tw_osl_scan_bus
+/* Request OSL for a bus scan. */
+extern TW_VOID tw_osl_scan_bus(struct tw_cl_ctlr_handle *ctlr_handle);
+#endif
+
+
+#ifdef TW_OSL_CAN_SLEEP
+#ifndef tw_osl_sleep
+/* Sleep for 'timeout' ms or until woken up (by tw_osl_wakeup). */
+extern TW_INT32        tw_osl_sleep(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_SLEEP_HANDLE *sleep_handle, TW_INT32 timeout);
+#endif
+#endif /* TW_OSL_CAN_SLEEP */
+
+
+#ifndef tw_osl_sprintf
+/* Standard sprintf. */
+extern TW_INT32        tw_osl_sprintf(TW_INT8 *dest, const TW_INT8 *fmt, ...);
+#endif
+
+
+#ifndef tw_osl_strcpy
+/* Copy string 'src' to 'dest'. */
+extern TW_INT8 *tw_osl_strcpy(TW_INT8 *dest, TW_INT8 *src);
+#endif
+
+
+#ifndef tw_osl_strlen
+/* Return length of string pointed at by 'str'. */
+extern TW_INT32        tw_osl_strlen(TW_VOID *str);
+#endif
+
+#ifndef tw_osl_vsprintf
+/* Standard vsprintf. */
+extern TW_INT32        tw_osl_vsprintf(TW_INT8 *dest, const TW_INT8 *fmt, va_list ap);
+#endif
+
+
+#ifdef TW_OSL_CAN_SLEEP
+#ifndef tw_osl_wakeup
+/* Wake up a thread sleeping by a call to tw_osl_sleep. */
+extern TW_VOID tw_osl_wakeup(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_SLEEP_HANDLE *sleep_handle);
+#endif
+#endif /* TW_OSL_CAN_SLEEP */
+
+
+#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
+#ifndef tw_osl_write_pci_config
+/* Write 'value' of 'size' bytes at 'offset' in the PCI config space. */
+extern TW_VOID tw_osl_write_pci_config(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_INT32 offset, TW_INT32 value, TW_INT32 size);
+#endif
+#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */
+
+
+#ifndef tw_osl_write_reg
+/*
+ * Write 'value' of 'size' (max 4) bytes at 'offset' from base address of
+ * this controller.
+ */
+extern TW_VOID tw_osl_write_reg(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_INT32 offset, TW_INT32 value, TW_INT32 size);
+#endif
+
+
+
+/* Functions in the Common Layer */
+
+/* Creates and queues AEN's.  Also notifies OS Layer. */
+extern TW_VOID tw_cl_create_event(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_UINT8 queue_event, TW_UINT8 event_src, TW_UINT16 event_code,
+       TW_UINT8 severity, TW_UINT8 *severity_str, TW_UINT8 *event_desc,
+       TW_UINT8 *event_specific_desc, ...);
+
+/* Indicates whether a ctlr is supported by CL. */
+extern TW_INT32        tw_cl_ctlr_supported(TW_INT32 vendor_id, TW_INT32 device_id);
+
+
+/* Submit a firmware cmd packet. */
+extern TW_INT32        tw_cl_fw_passthru(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle);
+
+
+/* Find out how much memory CL needs. */
+extern TW_INT32        tw_cl_get_mem_requirements(
+       struct tw_cl_ctlr_handle *ctlr_handle, TW_UINT32 flags,
+       TW_INT32 device_id, TW_INT32 max_simult_reqs, TW_INT32 max_aens,
+       TW_UINT32 *alignment, TW_UINT32 *sg_size_factor,
+       TW_UINT32 *non_dma_mem_size, TW_UINT32 *dma_mem_size
+       );
+
+
+/* Return PCI BAR info. */
+extern TW_INT32 tw_cl_get_pci_bar_info(TW_INT32 device_id, TW_INT32 bar_type,
+       TW_INT32 *bar_num, TW_INT32 *bar0_offset, TW_INT32 *bar_size);
+
+
+/* Initialize Common Layer for a given controller. */
+extern TW_INT32        tw_cl_init_ctlr(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_UINT32 flags, TW_INT32 device_id, TW_INT32 max_simult_reqs,
+       TW_INT32 max_aens, TW_VOID *non_dma_mem, TW_VOID *dma_mem,
+       TW_UINT64 dma_mem_phys
+       );
+
+
+/* CL's interrupt handler. */
+extern TW_INT32        tw_cl_interrupt(struct tw_cl_ctlr_handle *ctlr_handle);
+
+
+/* CL's ioctl handler. */
+extern TW_INT32        tw_cl_ioctl(struct tw_cl_ctlr_handle *ctlr_handle,
+       u_long cmd, TW_VOID *buf);
+
+
+#ifdef TW_OSL_DEBUG
+/* Print CL's state/statistics for a controller. */
+extern TW_VOID tw_cl_print_ctlr_stats(struct tw_cl_ctlr_handle *ctlr_handle);
+
+/* Prints CL internal details of a given request. */
+extern TW_VOID tw_cl_print_req_info(struct tw_cl_req_handle *req_handle);
+#endif /* TW_OSL_DEBUG */
+
+
+/* Soft reset controller. */
+extern TW_INT32        tw_cl_reset_ctlr(struct tw_cl_ctlr_handle *ctlr_handle);
+
+
+#ifdef TW_OSL_DEBUG
+/* Reset CL's statistics for a controller. */
+extern TW_VOID tw_cl_reset_stats(struct tw_cl_ctlr_handle *ctlr_handle);
+#endif /* TW_OSL_DEBUG */
+
+
+/* Stop a controller. */
+extern TW_INT32        tw_cl_shutdown_ctlr(struct tw_cl_ctlr_handle *ctlr_handle,
+       TW_UINT32 flags);
+
+
+/* Submit a SCSI I/O request. */
+extern TW_INT32        tw_cl_start_io(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle);
+
+
+#endif /* TW_BUILDING_API */
+
+#endif /* TW_CL_SHARE_H */
diff --git a/sys/dev/raid/twa/tw_osl.h b/sys/dev/raid/twa/tw_osl.h
new file mode 100644 (file)
index 0000000..edc5580
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_osl.h,v 1.8 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+
+#ifndef TW_OSL_H
+
+#define TW_OSL_H
+
+
+/*
+ * OS Layer internal macros, structures and functions.
+ */
+
+
+#define TW_OSLI_DEVICE_NAME            "3ware 9000 series Storage Controller"
+
+#define TW_OSLI_MALLOC_CLASS           M_TWA
+#define TW_OSLI_MAX_NUM_REQUESTS       TW_CL_MAX_SIMULTANEOUS_REQUESTS
+/* Reserve two command packets.  One for ioctls and one for AENs */
+#define TW_OSLI_MAX_NUM_IOS            (TW_OSLI_MAX_NUM_REQUESTS - 2)
+#define TW_OSLI_MAX_NUM_AENS           0x100
+
+#ifdef PAE
+#define        TW_OSLI_DMA_BOUNDARY            (1u << 31)
+#else
+#define        TW_OSLI_DMA_BOUNDARY            ((bus_size_t)((uint64_t)1 << 32))
+#endif
+
+/* Possible values of req->state. */
+#define TW_OSLI_REQ_STATE_INIT         0x0     /* being initialized */
+#define TW_OSLI_REQ_STATE_BUSY         0x1     /* submitted to CL */
+#define TW_OSLI_REQ_STATE_PENDING      0x2     /* in pending queue */
+#define TW_OSLI_REQ_STATE_COMPLETE     0x3     /* completed by CL */
+
+/* Possible values of req->flags. */
+#define TW_OSLI_REQ_FLAGS_DATA_IN      (1<<0)  /* read request */
+#define TW_OSLI_REQ_FLAGS_DATA_OUT     (1<<1)  /* write request */
+#define TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED (1<<2)/* data in ccb is misaligned,
+                                       have to copy to/from private buffer */
+#define TW_OSLI_REQ_FLAGS_MAPPED       (1<<3)  /* request has been mapped */
+#define TW_OSLI_REQ_FLAGS_IN_PROGRESS  (1<<4)  /* bus_dmamap_load returned
+                                               EINPROGRESS */
+#define TW_OSLI_REQ_FLAGS_PASSTHRU     (1<<5)  /* pass through request */
+#define TW_OSLI_REQ_FLAGS_SLEEPING     (1<<6)  /* owner sleeping on this cmd */
+
+
+#ifdef TW_OSL_DEBUG
+struct tw_osli_q_stats {
+       TW_UINT32       cur_len;        /* current # of items in q */
+       TW_UINT32       max_len;        /* max value reached by q_length */
+};
+#endif /* TW_OSL_DEBUG */
+
+
+/* Queues of OSL internal request context packets. */
+#define TW_OSLI_FREE_Q         0       /* free q */
+#define TW_OSLI_BUSY_Q         1       /* q of reqs submitted to CL */
+#define TW_OSLI_Q_COUNT                2       /* total number of queues */
+
+/* Driver's request packet. */
+struct tw_osli_req_context {
+       struct tw_cl_req_handle req_handle;/* tag to track req b/w OSL & CL */
+       struct lock             ioctl_wake_timeout_lock_handle;/* non-spin lock used to detect ioctl timeout */
+       struct lock             *ioctl_wake_timeout_lock;/* ptr to above lock */
+       struct twa_softc        *ctlr;  /* ptr to OSL's controller context */
+       TW_VOID                 *data;  /* ptr to data being passed to CL */
+       TW_UINT32               length; /* length of buf being passed to CL */
+
+       /*
+        * ptr to, and length of data passed to us from above, in case a buffer
+        * copy was done due to non-compliance to alignment requirements
+        */
+       TW_VOID                 *real_data;
+       TW_UINT32               real_length;
+
+       TW_UINT32               state;  /* request state */
+       TW_UINT32               flags;  /* request flags */
+
+       /* error encountered before request submission to CL */
+       TW_UINT32               error_code;
+
+       /* ptr to orig req for use during callback */
+       TW_VOID                 *orig_req;
+
+       struct tw_cl_link       link;   /* to link this request in a list */
+       bus_dmamap_t            dma_map;/* DMA map for data */
+       struct tw_cl_req_packet req_pkt;/* req pkt understood by CL */
+};
+
+
+/* Per-controller structure. */
+struct twa_softc {
+       struct tw_cl_ctlr_handle        ctlr_handle;
+       struct tw_osli_req_context      *req_ctx_buf;
+
+       /* Controller state. */
+       TW_UINT8                open;
+       TW_UINT32               flags;
+
+       TW_INT32                device_id;
+       TW_UINT32               alignment;
+       TW_UINT32               sg_size_factor;
+
+       TW_VOID                 *non_dma_mem;
+       TW_VOID                 *dma_mem;
+       TW_UINT64               dma_mem_phys;
+
+       /* Request queues and arrays. */
+       struct tw_cl_link       req_q_head[TW_OSLI_Q_COUNT];
+
+       struct task             deferred_intr_callback;/* taskqueue function */
+       struct spinlock         io_lock_handle;/* general purpose lock */
+       struct spinlock         *io_lock;/* ptr to general purpose lock */
+       struct spinlock         q_lock_handle;  /* queue manipulation lock */
+       struct spinlock         *q_lock;/* ptr to queue manipulation lock */
+       struct lock             sim_lock_handle;/* sim lock shared with cam */
+       struct lock             *sim_lock;/* ptr to sim lock */
+
+#ifdef TW_OSL_DEBUG
+       struct tw_osli_q_stats  q_stats[TW_OSLI_Q_COUNT];/* queue statistics */
+#endif /* TW_OSL_DEBUG */
+
+       device_t                bus_dev;        /* bus device */
+       struct cdev             *ctrl_dev;      /* control device */
+       struct resource         *reg_res;       /* register interface window */
+       TW_INT32                reg_res_id;     /* register resource id */
+       bus_space_handle_t      bus_handle;     /* bus space handle */
+       bus_space_tag_t         bus_tag;        /* bus space tag */
+       bus_dma_tag_t           parent_tag;     /* parent DMA tag */
+       bus_dma_tag_t           cmd_tag; /* DMA tag for CL's DMA'able mem */
+       bus_dma_tag_t           dma_tag; /* data buffer DMA tag */
+       bus_dma_tag_t           ioctl_tag; /* ioctl data buffer DMA tag */
+       bus_dmamap_t            cmd_map; /* DMA map for CL's DMA'able mem */
+       bus_dmamap_t            ioctl_map; /* DMA map for ioctl data buffers */
+       struct resource         *irq_res;       /* interrupt resource */
+       TW_INT32                irq_res_id;     /* register resource id */
+       TW_VOID                 *intr_handle;   /* interrupt handle */
+
+       struct sysctl_ctx_list  sysctl_ctxt;    /* sysctl context */
+       struct sysctl_oid       *sysctl_tree;   /* sysctl oid */
+
+       struct cam_sim          *sim;   /* sim for this controller */
+       struct cam_path         *path;  /* peripheral, path, tgt, lun
+                                       associated with this controller */
+};
+
+
+
+/*
+ * Queue primitives.
+ */
+
+#ifdef TW_OSL_DEBUG
+
+#define TW_OSLI_Q_INIT(sc, q_type)     do {                            \
+       (sc)->q_stats[q_type].cur_len = 0;                              \
+       (sc)->q_stats[q_type].max_len = 0;                              \
+} while(0)
+
+
+#define TW_OSLI_Q_INSERT(sc, q_type)   do {                            \
+       struct tw_osli_q_stats *q_stats = &((sc)->q_stats[q_type]);     \
+                                                                       \
+       if (++(q_stats->cur_len) > q_stats->max_len)                    \
+               q_stats->max_len = q_stats->cur_len;                    \
+} while(0)
+
+
+#define TW_OSLI_Q_REMOVE(sc, q_type)                                   \
+       (sc)->q_stats[q_type].cur_len--
+
+
+#else /* TW_OSL_DEBUG */
+
+#define TW_OSLI_Q_INIT(sc, q_index)
+#define TW_OSLI_Q_INSERT(sc, q_index)
+#define TW_OSLI_Q_REMOVE(sc, q_index)
+
+#endif /* TW_OSL_DEBUG */
+
+
+
+/* Initialize a queue of requests. */
+static __inline        TW_VOID
+tw_osli_req_q_init(struct twa_softc *sc, TW_UINT8 q_type)
+{
+       TW_CL_Q_INIT(&(sc->req_q_head[q_type]));
+       TW_OSLI_Q_INIT(sc, q_type);
+}
+
+
+
+/* Insert the given request at the head of the given queue (q_type). */
+static __inline        TW_VOID
+tw_osli_req_q_insert_head(struct tw_osli_req_context *req, TW_UINT8 q_type)
+{
+       spin_lock_wr(req->ctlr->q_lock);
+       TW_CL_Q_INSERT_HEAD(&(req->ctlr->req_q_head[q_type]), &(req->link));
+       TW_OSLI_Q_INSERT(req->ctlr, q_type);
+       spin_unlock_wr(req->ctlr->q_lock);
+}
+
+
+
+/* Insert the given request at the tail of the given queue (q_type). */
+static __inline        TW_VOID
+tw_osli_req_q_insert_tail(struct tw_osli_req_context *req, TW_UINT8 q_type)
+{
+       spin_lock_wr(req->ctlr->q_lock);
+       TW_CL_Q_INSERT_TAIL(&(req->ctlr->req_q_head[q_type]), &(req->link));
+       TW_OSLI_Q_INSERT(req->ctlr, q_type);
+       spin_unlock_wr(req->ctlr->q_lock);
+}
+
+
+
+/* Remove and return the request at the head of the given queue (q_type). */
+static __inline struct tw_osli_req_context *
+tw_osli_req_q_remove_head(struct twa_softc *sc, TW_UINT8 q_type)
+{
+       struct tw_osli_req_context      *req = NULL;
+       struct tw_cl_link               *link;
+
+       spin_lock_wr(sc->q_lock);
+       if ((link = TW_CL_Q_FIRST_ITEM(&(sc->req_q_head[q_type]))) !=
+               TW_CL_NULL) {
+               req = TW_CL_STRUCT_HEAD(link,
+                       struct tw_osli_req_context, link);
+               TW_CL_Q_REMOVE_ITEM(&(sc->req_q_head[q_type]), &(req->link));
+               TW_OSLI_Q_REMOVE(sc, q_type);
+       }
+       spin_unlock_wr(sc->q_lock);
+       return(req);
+}
+
+
+
+/* Remove the given request from the given queue (q_type). */
+static __inline TW_VOID
+tw_osli_req_q_remove_item(struct tw_osli_req_context *req, TW_UINT8 q_type)
+{
+       spin_lock_wr(req->ctlr->q_lock);
+       TW_CL_Q_REMOVE_ITEM(&(req->ctlr->req_q_head[q_type]), &(req->link));
+       TW_OSLI_Q_REMOVE(req->ctlr, q_type);
+       spin_unlock_wr(req->ctlr->q_lock);
+}
+
+
+
+#ifdef TW_OSL_DEBUG
+
+extern TW_INT32        TW_DEBUG_LEVEL_FOR_OSL;
+
+#define tw_osli_dbg_dprintf(dbg_level, sc, fmt, args...)               \
+       if (dbg_level <= TW_DEBUG_LEVEL_FOR_OSL)                        \
+               device_printf(sc->bus_dev, "%s: " fmt "\n",             \
+                       __func__, ##args)
+
+
+#define tw_osli_dbg_printf(dbg_level, fmt, args...)                    \
+       if (dbg_level <= TW_DEBUG_LEVEL_FOR_OSL)                        \
+               kprintf("%s: " fmt "\n", __func__, ##args)
+
+#else /* TW_OSL_DEBUG */
+
+#define tw_osli_dbg_dprintf(dbg_level, sc, fmt, args...)
+#define tw_osli_dbg_printf(dbg_level, fmt, args...)
+
+#endif /* TW_OSL_DEBUG */
+
+
+/* For regular printing. */
+#define twa_printf(sc, fmt, args...)                                   \
+       device_printf(((struct twa_softc *)(sc))->bus_dev, fmt, ##args)
+
+/* For printing in the "consistent error reporting" format. */
+#define tw_osli_printf(sc, err_specific_desc, args...)                 \
+       device_printf((sc)->bus_dev,                                    \
+               "%s: (0x%02X: 0x%04X): %s: " err_specific_desc "\n", ##args)
+
+
+
+#endif /* TW_OSL_H */
diff --git a/sys/dev/raid/twa/tw_osl_cam.c b/sys/dev/raid/twa/tw_osl_cam.c
new file mode 100644 (file)
index 0000000..a5bffa6
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_osl_cam.c,v 1.14 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+/*
+ * FreeBSD CAM related functions.
+ */
+
+
+#include <dev/raid/twa/tw_osl_includes.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_sim.h>
+#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_debug.h>
+#include <bus/cam/cam_periph.h>
+#include <bus/cam/cam_xpt_periph.h>
+
+#include <bus/cam/scsi/scsi_all.h>
+#include <bus/cam/scsi/scsi_message.h>
+
+static TW_VOID twa_action(struct cam_sim *sim, union ccb *ccb);
+static TW_VOID twa_poll(struct cam_sim *sim);
+static TW_VOID twa_timeout(TW_VOID *arg);
+static TW_VOID twa_bus_scan_cb(struct cam_periph *periph, union ccb *ccb);
+
+static TW_INT32        tw_osli_execute_scsi(struct tw_osli_req_context *req,
+       union ccb *ccb);
+
+
+
+/*
+ * Function name:      tw_osli_cam_attach
+ * Description:                Attaches the driver to CAM.
+ *
+ * Input:              sc      -- ptr to OSL internal ctlr context
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_osli_cam_attach(struct twa_softc *sc)
+{
+       struct cam_devq         *devq;
+
+       tw_osli_dbg_dprintf(3, sc, "entered");
+
+       /*
+        * Create the device queue for our SIM.
+        */
+       if ((devq = cam_simq_alloc(TW_OSLI_MAX_NUM_REQUESTS)) == NULL) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2100,
+                       "Failed to create SIM device queue",
+                       ENOMEM);
+               return(ENOMEM);
+       }
+
+       /*
+        * Create a SIM entry.  Though we can support TW_OSLI_MAX_NUM_REQUESTS
+        * simultaneous requests, we claim to be able to handle only
+        * TW_OSLI_MAX_NUM_IOS (two less), so that we always have a request
+        * packet available to service ioctls and AENs.
+        */
+       tw_osli_dbg_dprintf(3, sc, "Calling cam_sim_alloc");
+       sc->sim = cam_sim_alloc(twa_action, twa_poll, "twa", sc,
+                       device_get_unit(sc->bus_dev), sc->sim_lock,
+                       TW_OSLI_MAX_NUM_IOS, 1, devq);
+       if (sc->sim == NULL) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2101,
+                       "Failed to create a SIM entry",
+                       ENOMEM);
+               return(ENOMEM);
+       }
+
+       /*
+        * Register the bus.
+        */
+       tw_osli_dbg_dprintf(3, sc, "Calling xpt_bus_register");
+       lockmgr(sc->sim_lock, LK_EXCLUSIVE);
+       if (xpt_bus_register(sc->sim, 0) != CAM_SUCCESS) {
+               cam_sim_free(sc->sim);
+               sc->sim = NULL; /* so cam_detach will not try to free it */
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2102,
+                       "Failed to register the bus",
+                       ENXIO);
+               lockmgr(sc->sim_lock, LK_RELEASE);
+               return(ENXIO);
+       }
+
+       tw_osli_dbg_dprintf(3, sc, "Calling xpt_create_path");
+       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);
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2103,
+                       "Failed to create path",
+                       ENXIO);
+               lockmgr(sc->sim_lock, LK_RELEASE);
+               return(ENXIO);
+       }
+       lockmgr(sc->sim_lock, LK_RELEASE);
+
+       tw_osli_dbg_dprintf(3, sc, "exiting");
+       return(0);
+}
+
+
+
+/*
+ * Function name:      tw_osli_cam_detach
+ * Description:                Detaches the driver from CAM.
+ *
+ * Input:              sc      -- ptr to OSL internal ctlr context
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_osli_cam_detach(struct twa_softc *sc)
+{
+       tw_osli_dbg_dprintf(3, sc, "entered");
+
+       lockmgr(sc->sim_lock, LK_EXCLUSIVE);
+
+       if (sc->path)
+               xpt_free_path(sc->path);
+       if (sc->sim) {
+               xpt_bus_deregister(cam_sim_path(sc->sim));
+               /* Passing TRUE to cam_sim_free will free the devq as well. */
+               cam_sim_free(sc->sim);
+       }
+       /* It's ok have 1 hold count while destroying the mutex */
+       lockuninit(sc->sim_lock);
+}
+
+
+
+/*
+ * Function name:      tw_osli_execute_scsi
+ * Description:                Build a fw cmd, based on a CAM style ccb, and
+ *                     send it down.
+ *
+ * Input:              req     -- ptr to OSL internal request context
+ *                     ccb     -- ptr to CAM style ccb
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_osli_execute_scsi(struct tw_osli_req_context *req, union ccb *ccb)
+{
+       struct twa_softc                *sc = req->ctlr;
+       struct tw_cl_req_packet         *req_pkt;
+       struct tw_cl_scsi_req_packet    *scsi_req;
+       struct ccb_hdr                  *ccb_h = &(ccb->ccb_h);
+       struct ccb_scsiio               *csio = &(ccb->csio);
+       TW_INT32                        error;
+
+       tw_osli_dbg_dprintf(10, sc, "SCSI I/O request 0x%x",
+               csio->cdb_io.cdb_bytes[0]);
+
+       if (ccb_h->target_id >= TW_CL_MAX_NUM_UNITS) {
+               tw_osli_dbg_dprintf(3, sc, "Invalid target. PTL = %x %x %x",
+                       ccb_h->path_id, ccb_h->target_id, ccb_h->target_lun);
+               ccb_h->status |= CAM_TID_INVALID;
+               xpt_done(ccb);
+               return(1);
+       }
+       if (ccb_h->target_lun >= TW_CL_MAX_NUM_LUNS) {
+               tw_osli_dbg_dprintf(3, sc, "Invalid lun. PTL = %x %x %x",
+                       ccb_h->path_id, ccb_h->target_id, ccb_h->target_lun);
+               ccb_h->status |= CAM_LUN_INVALID;
+               xpt_done(ccb);
+               return(1);
+       }
+
+       if(ccb_h->flags & CAM_CDB_PHYS) {
+               tw_osli_printf(sc, "",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2105,
+                       "Physical CDB address!");
+               ccb_h->status = CAM_REQ_INVALID;
+               xpt_done(ccb);
+               return(1);
+       }
+
+       /*
+        * 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;
+
+       if((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+               if(ccb_h->flags & CAM_DIR_IN)
+                       req->flags |= TW_OSLI_REQ_FLAGS_DATA_IN;
+               else
+                       req->flags |= TW_OSLI_REQ_FLAGS_DATA_OUT;
+       }
+
+       /* Build the CL understood request packet for SCSI cmds. */
+       req_pkt = &req->req_pkt;
+       req_pkt->status = 0;
+       req_pkt->tw_osl_callback = tw_osl_complete_io;
+       scsi_req = &(req_pkt->gen_req_pkt.scsi_req);
+       scsi_req->unit = ccb_h->target_id;
+       scsi_req->lun = ccb_h->target_lun;
+       scsi_req->sense_len = 0;
+       scsi_req->sense_data = (TW_UINT8 *)(&csio->sense_data);
+       scsi_req->scsi_status = 0;
+       if(ccb_h->flags & CAM_CDB_POINTER)
+               scsi_req->cdb = csio->cdb_io.cdb_ptr;
+       else
+               scsi_req->cdb = csio->cdb_io.cdb_bytes;
+       scsi_req->cdb_len = csio->cdb_len;
+
+       if (!(ccb_h->flags & CAM_DATA_PHYS)) {
+               /* Virtual data addresses.  Need to convert them... */
+               tw_osli_dbg_dprintf(3, sc,
+                       "XPT_SCSI_IO: Single virtual address!");
+               if (!(ccb_h->flags & CAM_SCATTER_VALID)) {
+                       if (csio->dxfer_len > TW_CL_MAX_IO_SIZE) {
+                               tw_osli_printf(sc, "size = %d",
+                                       TW_CL_SEVERITY_ERROR_STRING,
+                                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                                       0x2106,
+                                       "I/O size too big",
+                                       csio->dxfer_len);
+                               ccb_h->status = CAM_REQ_TOO_BIG;
+                               xpt_done(ccb);
+                               return(1);
+                       }
+
+                       if ((req->length = csio->dxfer_len)) {
+                               req->data = csio->data_ptr;
+                               scsi_req->sgl_entries = 1;
+                       }
+               } else {
+                       tw_osli_printf(sc, "",
+                               TW_CL_SEVERITY_ERROR_STRING,
+                               TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                               0x2107,
+                               "XPT_SCSI_IO: Got SGList");
+                       ccb_h->status = CAM_REQ_INVALID;
+                       xpt_done(ccb);
+                       return(1);
+               }
+       } else {
+               /* Data addresses are physical. */
+               tw_osli_printf(sc, "",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2108,
+                       "XPT_SCSI_IO: Physical data addresses");
+               ccb_h->status = CAM_REQ_INVALID;
+               ccb_h->status &= ~CAM_SIM_QUEUED;
+               xpt_done(ccb);
+               return(1);
+       }
+
+       callout_reset(&ccb->ccb_h.timeout_ch,
+               (ccb_h->timeout * hz) / 1000, twa_timeout, req);
+       /*
+        * twa_map_load_data_callback will fill in the SGL,
+        * and submit the I/O.
+        */
+       error = tw_osli_map_request(req);
+       return(error);
+}
+
+
+
+/*
+ * Function name:      twa_action
+ * Description:                Driver entry point for CAM's use.
+ *
+ * Input:              sim     -- sim corresponding to the ctlr
+ *                     ccb     -- ptr to CAM request
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+twa_action(struct cam_sim *sim, union ccb *ccb)
+{
+       struct twa_softc        *sc = (struct twa_softc *)cam_sim_softc(sim);
+       struct ccb_hdr          *ccb_h = &(ccb->ccb_h);
+
+       switch (ccb_h->func_code) {
+       case XPT_SCSI_IO:       /* SCSI I/O */
+       {
+               struct tw_osli_req_context      *req;
+
+               req = tw_osli_get_request(sc);
+               if (req == NULL) {
+                       tw_osli_dbg_dprintf(2, sc, "Cannot get request pkt.");
+                       /*
+                        * Freeze the simq to maintain ccb ordering.  The next
+                        * ccb that gets completed will unfreeze the simq.
+                        */
+                       ccb_h->status |= CAM_REQUEUE_REQ;
+                       xpt_done(ccb);
+                       break;
+               }
+               req->req_handle.osl_req_ctxt = req;
+               req->req_handle.is_io = TW_CL_TRUE;
+               req->orig_req = ccb;
+               if (tw_osli_execute_scsi(req, ccb))
+                       tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q);
+               break;
+       }
+
+       case XPT_ABORT:
+               tw_osli_dbg_dprintf(2, sc, "Abort request.");
+               ccb_h->status = CAM_UA_ABORT;
+               xpt_done(ccb);
+               break;
+
+       case XPT_RESET_BUS:
+               tw_cl_create_event(&(sc->ctlr_handle), TW_CL_TRUE,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2108, 0x3, TW_CL_SEVERITY_INFO_STRING,
+                       "Received Reset Bus request from CAM",
+                       " ");
+
+               lockmgr(sc->sim_lock, LK_RELEASE);
+               if (tw_cl_reset_ctlr(&sc->ctlr_handle)) {
+                       tw_cl_create_event(&(sc->ctlr_handle), TW_CL_TRUE,
+                               TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                               0x2109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+                               "Failed to reset bus",
+                               " ");
+                       ccb_h->status = CAM_REQ_CMP_ERR;
+               }
+               else
+                       ccb_h->status = CAM_REQ_CMP;
+
+               lockmgr(sc->sim_lock, LK_EXCLUSIVE);
+               xpt_done(ccb);
+               break;
+
+       case XPT_SET_TRAN_SETTINGS:
+               tw_osli_dbg_dprintf(3, sc, "XPT_SET_TRAN_SETTINGS");
+
+               /*
+                * This command is not supported, since it's very specific
+                * to SCSI, and we are doing ATA.
+                */
+               ccb_h->status = CAM_FUNC_NOTAVAIL;
+               xpt_done(ccb);
+               break;
+
+       case XPT_GET_TRAN_SETTINGS:
+       {
+               struct ccb_trans_settings       *cts = &ccb->cts;
+               struct ccb_trans_settings_scsi *scsi =
+                   &cts->proto_specific.scsi;
+               struct ccb_trans_settings_spi *spi =
+                   &cts->xport_specific.spi;
+
+               cts->protocol = PROTO_SCSI;
+               cts->protocol_version = SCSI_REV_2;
+               cts->transport = XPORT_SPI;
+               cts->transport_version = 2;
+
+               spi->valid = CTS_SPI_VALID_DISC;
+               spi->flags = CTS_SPI_FLAGS_DISC_ENB;
+               scsi->valid = CTS_SCSI_VALID_TQ;
+               scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
+               tw_osli_dbg_dprintf(3, sc, "XPT_GET_TRAN_SETTINGS");
+               ccb_h->status = CAM_REQ_CMP;
+               xpt_done(ccb);
+               break;
+       }
+
+       case XPT_CALC_GEOMETRY:
+               tw_osli_dbg_dprintf(3, sc, "XPT_CALC_GEOMETRY");
+               cam_calc_geometry(&ccb->ccg, 1/* extended */);
+               xpt_done(ccb);
+               break;
+
+       case XPT_PATH_INQ:    /* Path inquiry -- get twa properties */
+       {
+               struct ccb_pathinq      *path_inq = &ccb->cpi;
+
+               tw_osli_dbg_dprintf(3, sc, "XPT_PATH_INQ request");
+
+               path_inq->version_num = 1;
+               path_inq->hba_inquiry = 0;
+               path_inq->target_sprt = 0;
+               path_inq->hba_misc = 0;
+               path_inq->hba_eng_cnt = 0;
+               path_inq->max_target = TW_CL_MAX_NUM_UNITS;
+               path_inq->max_lun = TW_CL_MAX_NUM_LUNS - 1;
+               path_inq->unit_number = cam_sim_unit(sim);
+               path_inq->bus_id = cam_sim_bus(sim);
+               path_inq->initiator_id = TW_CL_MAX_NUM_UNITS;
+               path_inq->base_transfer_speed = 100000;
+               strncpy(path_inq->sim_vid, "FreeBSD", SIM_IDLEN);
+               strncpy(path_inq->hba_vid, "3ware", HBA_IDLEN);
+               strncpy(path_inq->dev_name, cam_sim_name(sim), DEV_IDLEN);
+                path_inq->transport = XPORT_SPI;
+                path_inq->transport_version = 2;
+                path_inq->protocol = PROTO_SCSI;
+                path_inq->protocol_version = SCSI_REV_2;
+               ccb_h->status = CAM_REQ_CMP;
+               xpt_done(ccb);
+               break;
+       }
+
+       default:
+               tw_osli_dbg_dprintf(3, sc, "func_code = %x", ccb_h->func_code);
+               ccb_h->status = CAM_REQ_INVALID;
+               xpt_done(ccb);
+               break;
+       }
+}
+
+
+
+/*
+ * Function name:      twa_poll
+ * Description:                Driver entry point called when interrupts are not
+ *                     available.
+ *
+ * Input:              sim     -- sim corresponding to the controller
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+twa_poll(struct cam_sim *sim)
+{
+       struct twa_softc *sc = (struct twa_softc *)(cam_sim_softc(sim));
+
+       tw_osli_dbg_dprintf(3, sc, "entering; sc = %p", sc);
+       tw_cl_interrupt(&(sc->ctlr_handle));
+       tw_osli_dbg_dprintf(3, sc, "exiting; sc = %p", sc);
+}
+
+
+
+/*
+ * Function name:      twa_timeout
+ * Description:                Driver entry point for being alerted on a request
+ *                     timing out.
+ *
+ * Input:              arg     -- ptr to timed out request
+ * Output:             None
+ * Return value:       None
+ */
+static TW_VOID
+twa_timeout(TW_VOID *arg)
+{
+       struct tw_osli_req_context      *req =
+               (struct tw_osli_req_context *)arg;
+
+       tw_cl_create_event(&(req->ctlr->ctlr_handle), TW_CL_TRUE,
+               TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+               0x210B, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+               "Request timed out!",
+               "request = %p", req);
+       tw_cl_reset_ctlr(&(req->ctlr->ctlr_handle));
+}
+
+
+
+/*
+ * Function name:      tw_osli_request_bus_scan
+ * Description:                Requests CAM for a scan of the bus.
+ *
+ * Input:              sc      -- ptr to per ctlr structure
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+TW_INT32
+tw_osli_request_bus_scan(struct twa_softc *sc)
+{
+       union ccb       *ccb;
+
+       tw_osli_dbg_dprintf(3, sc, "entering");
+
+       /* If we get here before sc->sim is initialized, return an error. */
+       if (!(sc->sim))
+               return(ENXIO);
+       if ((ccb = xpt_alloc_ccb()) == NULL)
+               return(ENOMEM);
+       lockmgr(sc->sim_lock, LK_EXCLUSIVE);
+       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);
+               lockmgr(sc->sim_lock, LK_RELEASE);
+               return(EIO);
+       }
+
+       xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, 5/*priority (low)*/);
+       ccb->ccb_h.func_code = XPT_SCAN_BUS;
+       ccb->ccb_h.cbfcnp = twa_bus_scan_cb;
+       ccb->crcn.flags = CAM_FLAG_NONE;
+       xpt_action(ccb);
+
+       lockmgr(sc->sim_lock, LK_RELEASE);
+       return(0);
+}
+
+
+
+/*
+ * Function name:      twa_bus_scan_cb
+ * Description:                Callback from CAM on a bus scan request.
+ *
+ * Input:              periph  -- we don't use this
+ *                     ccb     -- bus scan request ccb that we sent to CAM
+ * Output:             None
+ * Return value:       None
+ */
+static TW_VOID
+twa_bus_scan_cb(struct cam_periph *periph, union ccb *ccb)
+{
+       tw_osli_dbg_printf(3, "entering");
+
+       if (ccb->ccb_h.status != CAM_REQ_CMP)
+               kprintf("cam_scan_callback: failure status = %x\n",
+                       ccb->ccb_h.status);
+       else
+               tw_osli_dbg_printf(3, "success");
+
+       xpt_free_path(ccb->ccb_h.path);
+       kfree(ccb, M_TEMP);
+}
+
+
+
+/*
+ * Function name:      tw_osli_disallow_new_requests
+ * Description:                Calls the appropriate CAM function, so as to freeze
+ *                     the flow of new requests from CAM to this controller.
+ *
+ * Input:              sc      -- ptr to OSL internal ctlr context
+ *                     req_handle -- ptr to request handle sent by OSL.
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_osli_disallow_new_requests(struct twa_softc *sc,
+       struct tw_cl_req_handle *req_handle)
+{
+       /* Only freeze/release the simq for IOs */
+       if (req_handle->is_io) {
+               struct tw_osli_req_context      *req = req_handle->osl_req_ctxt;
+               union ccb                       *ccb = (union ccb *)(req->orig_req);
+
+               xpt_freeze_simq(sc->sim, 1);
+               ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+       }
+}
+
+
+
+/*
+ * Function name:      tw_osl_ctlr_busy
+ * Description:                CL calls this function on cmd queue full or otherwise,
+ *                     when it is too busy to accept new requests.
+ *
+ * Input:              ctlr_handle     -- ptr to controller handle
+ *                     req_handle      -- ptr to request handle sent by OSL.
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_osl_ctlr_busy(struct tw_cl_ctlr_handle *ctlr_handle,
+       struct tw_cl_req_handle *req_handle)
+{
+       tw_osli_disallow_new_requests(ctlr_handle->osl_ctlr_ctxt, req_handle);
+}
+
+
+
+/*
+ * Function name:      tw_osl_scan_bus
+ * Description:                CL calls this function to request for a bus scan.
+ *
+ * Input:              ctlr_handle     -- ptr to controller handle
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_osl_scan_bus(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+       struct twa_softc        *sc = ctlr_handle->osl_ctlr_ctxt;
+       TW_INT32                error;
+
+       if ((error = tw_osli_request_bus_scan(sc)))
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2109,
+                       "Bus scan request to CAM failed",
+                       error);
+}
+
+
+
+/*
+ * Function name:      tw_osl_complete_io
+ * Description:                Called to complete CAM scsi requests.
+ *
+ * Input:              req_handle      -- ptr to request handle
+ * Output:             None
+ * Return value:       None
+ */
+TW_VOID
+tw_osl_complete_io(struct tw_cl_req_handle *req_handle)
+{
+       struct tw_osli_req_context      *req = req_handle->osl_req_ctxt;
+       struct tw_cl_req_packet         *req_pkt =
+               (struct tw_cl_req_packet *)(&req->req_pkt);
+       struct tw_cl_scsi_req_packet    *scsi_req;
+       struct twa_softc                *sc = req->ctlr;
+       union ccb                       *ccb = (union ccb *)(req->orig_req);
+
+       tw_osli_dbg_dprintf(10, sc, "entering");
+
+       if (req->state != TW_OSLI_REQ_STATE_BUSY)
+               tw_osli_printf(sc, "request = %p, status = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x210A,
+                       "Unposted command completed!!",
+                       req, req->state);
+
+       /*
+        * Remove request from the busy queue.  Just mark it complete.
+        * There's no need to move it into the complete queue as we are
+        * going to be done with it right now.
+        */
+       req->state = TW_OSLI_REQ_STATE_COMPLETE;
+       tw_osli_req_q_remove_item(req, TW_OSLI_BUSY_Q);
+
+       tw_osli_unmap_request(req);
+
+       callout_stop(&ccb->ccb_h.timeout_ch);
+       if (req->error_code) {
+               /* This request never got submitted to the firmware. */
+               if (req->error_code == EBUSY) {
+                       /*
+                        * Cmd queue is full, or the Common Layer is out of
+                        * resources.  The simq will already have been frozen.
+                        * When this ccb gets completed will unfreeze the simq.
+                        */
+                       ccb->ccb_h.status |= CAM_REQUEUE_REQ;
+               }
+               else if (req->error_code == EFBIG)
+                       ccb->ccb_h.status = CAM_REQ_TOO_BIG;
+               else
+                       ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+       } else {
+               scsi_req = &(req_pkt->gen_req_pkt.scsi_req);
+               if (req_pkt->status == TW_CL_ERR_REQ_SUCCESS)
+                       ccb->ccb_h.status = CAM_REQ_CMP;
+               else {
+                       if (req_pkt->status & TW_CL_ERR_REQ_INVALID_TARGET)
+                               ccb->ccb_h.status |= CAM_TID_INVALID;
+                       else if (req_pkt->status & TW_CL_ERR_REQ_INVALID_LUN)
+                               ccb->ccb_h.status |= CAM_LUN_INVALID;
+                       else if (req_pkt->status & TW_CL_ERR_REQ_SCSI_ERROR)
+                               ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
+                       else if (req_pkt->status & TW_CL_ERR_REQ_BUS_RESET)
+                               ccb->ccb_h.status |= CAM_SCSI_BUS_RESET;
+                       /*
+                        * If none of the above errors occurred, simply
+                        * mark completion error.
+                        */
+                       if (ccb->ccb_h.status == 0)
+                               ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+
+                       if (req_pkt->status & TW_CL_ERR_REQ_AUTO_SENSE_VALID) {
+                               ccb->csio.sense_len = scsi_req->sense_len;
+                               ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+                       }
+               }
+
+               ccb->csio.scsi_status = scsi_req->scsi_status;
+       }
+
+       ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+       lockmgr(sc->sim_lock, LK_EXCLUSIVE);
+       xpt_done(ccb);
+       lockmgr(sc->sim_lock, LK_RELEASE);
+       if (! req->error_code)
+                /* twa_action will free the request otherwise */
+               tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q);
+}
diff --git a/sys/dev/raid/twa/tw_osl_externs.h b/sys/dev/raid/twa/tw_osl_externs.h
new file mode 100644 (file)
index 0000000..6a4962b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_osl_externs.h,v 1.3 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ */
+
+
+
+#ifndef TW_OSL_EXTERNS_H
+
+#define TW_OSL_EXTERNS_H
+
+
+/*
+ * Data structures and functions global to the OS Layer.
+ */
+
+
+/* External data structures. */
+
+extern int     mp_ncpus;
+
+
+
+/* Functions in tw_osl_freebsd.c */
+
+/* Build a firmware passthru cmd pkt, and submit it to CL. */
+extern TW_INT32        tw_osli_fw_passthru(struct twa_softc *sc, TW_INT8 *buf);
+
+/* Get an OSL internal request context packet. */
+extern struct tw_osli_req_context *tw_osli_get_request(struct twa_softc *sc);
+
+/* Map data to DMA'able memory. */
+extern TW_INT32        tw_osli_map_request(struct tw_osli_req_context *req);
+
+/* Undo mapping. */
+extern TW_VOID tw_osli_unmap_request(struct tw_osli_req_context *req);
+
+
+
+/* Functions in tw_osl_cam.c */
+
+/* Attach to CAM. */
+extern TW_INT32        tw_osli_cam_attach(struct twa_softc *sc);
+
+/* Detach from CAM. */
+extern TW_VOID tw_osli_cam_detach(struct twa_softc *sc);
+
+/* Request CAM for a bus scan. */
+extern TW_INT32        tw_osli_request_bus_scan(struct twa_softc *sc);
+
+/* Freeze ccb flow from CAM. */
+extern TW_VOID tw_osli_disallow_new_requests(struct twa_softc *sc,
+       struct tw_cl_req_handle *req_handle);
+
+/* OSL's completion routine for SCSI I/O's. */
+extern TW_VOID tw_osl_complete_io(struct tw_cl_req_handle *req_handle);
+
+/* OSL's completion routine for passthru requests. */
+extern TW_VOID tw_osl_complete_passthru(struct tw_cl_req_handle *req_handle);
+
+
+
+#endif /* TW_OSL_EXTERNS_H */
diff --git a/sys/dev/raid/twa/tw_osl_freebsd.c b/sys/dev/raid/twa/tw_osl_freebsd.c
new file mode 100644 (file)
index 0000000..6e19c70
--- /dev/null
@@ -0,0 +1,1624 @@
+/*
+ * Copyright (c) 2004-07 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap.
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $FreeBSD: src/sys/dev/twa/tw_osl_freebsd.c,v 1.15 2010/06/09 21:40:38 delphij Exp $
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ * Modifications by: Adam Radford
+ * Modifications by: Manjunath Ranganathaiah
+ */
+
+
+/*
+ * FreeBSD specific functions not related to CAM, and other
+ * miscellaneous functions.
+ */
+
+
+#include <dev/raid/twa/tw_osl_includes.h>
+#include <dev/raid/twa/tw_cl_fwif.h>
+#include <dev/raid/twa/tw_cl_ioctl.h>
+#include <dev/raid/twa/tw_osl_ioctl.h>
+
+#ifdef TW_OSL_DEBUG
+TW_INT32       TW_DEBUG_LEVEL_FOR_OSL = TW_OSL_DEBUG;
+TW_INT32       TW_OSL_DEBUG_LEVEL_FOR_CL = TW_OSL_DEBUG;
+#endif /* TW_OSL_DEBUG */
+
+MALLOC_DEFINE(TW_OSLI_MALLOC_CLASS, "twa_commands", "twa commands");
+
+
+static d_open_t                twa_open;
+static d_close_t               twa_close;
+static d_ioctl_t               twa_ioctl;
+
+static struct dev_ops twa_ops = {
+       { "twa", 0, 0 },
+       .d_open =       twa_open,
+       .d_close =      twa_close,
+       .d_ioctl =      twa_ioctl,
+};
+
+static devclass_t      twa_devclass;
+
+
+/*
+ * Function name:      twa_open
+ * Description:                Called when the controller is opened.
+ *                     Simply marks the controller as open.
+ *
+ * Input:              dev     -- control device corresponding to the ctlr
+ *                     flags   -- mode of open
+ *                     fmt     -- device type (character/block etc.)
+ *                     proc    -- current process
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+static TW_INT32
+twa_open(struct dev_open_args *ap)
+{
+       cdev_t                  dev = ap->a_head.a_dev;
+       struct twa_softc        *sc = (struct twa_softc *)(dev->si_drv1);
+
+       tw_osli_dbg_dprintf(5, sc, "entered");
+       sc->open = TW_CL_TRUE;
+       return(0);
+}
+
+
+
+/*
+ * Function name:      twa_close
+ * Description:                Called when the controller is closed.
+ *                     Simply marks the controller as not open.
+ *
+ * Input:              dev     -- control device corresponding to the ctlr
+ *                     flags   -- mode of corresponding open
+ *                     fmt     -- device type (character/block etc.)
+ *                     proc    -- current process
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+static TW_INT32
+twa_close(struct dev_close_args *ap)
+{
+       cdev_t                  dev = ap->a_head.a_dev;
+       struct twa_softc        *sc = (struct twa_softc *)(dev->si_drv1);
+
+       tw_osli_dbg_dprintf(5, sc, "entered");
+       sc->open = TW_CL_FALSE;
+       return(0);
+}
+
+
+
+/*
+ * Function name:      twa_ioctl
+ * Description:                Called when an ioctl is posted to the controller.
+ *                     Handles any OS Layer specific cmds, passes the rest
+ *                     on to the Common Layer.
+ *
+ * Input:              dev     -- control device corresponding to the ctlr
+ *                     cmd     -- ioctl cmd
+ *                     buf     -- ptr to buffer in kernel memory, which is
+ *                                a copy of the input buffer in user-space
+ *                     flags   -- mode of corresponding open
+ *                     proc    -- current process
+ * Output:             buf     -- ptr to buffer in kernel memory, which will
+ *                                be copied to the output buffer in user-space
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+static TW_INT32
+twa_ioctl(struct dev_ioctl_args *ap)
+{
+       cdev_t                  dev = ap->a_head.a_dev;
+       u_long                  cmd = ap->a_cmd;
+       caddr_t                 buf = ap->a_data;
+       struct twa_softc        *sc = (struct twa_softc *)(dev->si_drv1);
+       TW_INT32                error;
+
+       tw_osli_dbg_dprintf(5, sc, "entered");
+
+       switch (cmd) {
+       case TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH:
+               tw_osli_dbg_dprintf(6, sc, "ioctl: fw_passthru");
+               error = tw_osli_fw_passthru(sc, (TW_INT8 *)buf);
+               break;
+
+       case TW_OSL_IOCTL_SCAN_BUS:
+               /* Request CAM for a bus scan. */
+               tw_osli_dbg_dprintf(6, sc, "ioctl: scan bus");
+               error = tw_osli_request_bus_scan(sc);
+               break;
+
+       default:
+               tw_osli_dbg_dprintf(6, sc, "ioctl: 0x%lx", cmd);
+               error = tw_cl_ioctl(&sc->ctlr_handle, cmd, buf);
+               break;
+       }
+       return(error);
+}
+
+
+
+static TW_INT32        twa_probe(device_t dev);
+static TW_INT32        twa_attach(device_t dev);
+static TW_INT32        twa_detach(device_t dev);
+static TW_INT32        twa_shutdown(device_t dev);
+#if 0 /* XXX swildner */
+static TW_VOID twa_busdma_lock(TW_VOID *lock_arg, bus_dma_lock_op_t op);
+#endif
+static TW_VOID twa_pci_intr(TW_VOID *arg);
+
+static TW_INT32        tw_osli_alloc_mem(struct twa_softc *sc);
+static TW_VOID tw_osli_free_resources(struct twa_softc *sc);
+
+static TW_VOID twa_map_load_data_callback(TW_VOID *arg,
+       bus_dma_segment_t *segs, TW_INT32 nsegments, TW_INT32 error);
+static TW_VOID twa_map_load_callback(TW_VOID *arg,
+       bus_dma_segment_t *segs, TW_INT32 nsegments, TW_INT32 error);
+
+
+static device_method_t twa_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         twa_probe),
+       DEVMETHOD(device_attach,        twa_attach),
+       DEVMETHOD(device_detach,        twa_detach),
+       DEVMETHOD(device_shutdown,      twa_shutdown),
+
+       DEVMETHOD(bus_print_child,      bus_generic_print_child),
+       DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+       {0, 0}
+};
+
+static driver_t        twa_pci_driver = {
+       "twa",
+       twa_methods,
+       sizeof(struct twa_softc)
+};
+
+DRIVER_MODULE(twa, pci, twa_pci_driver, twa_devclass, 0, 0);
+MODULE_DEPEND(twa, cam, 1, 1, 1);
+MODULE_DEPEND(twa, pci, 1, 1, 1);
+
+
+/*
+ * Function name:      twa_probe
+ * Description:                Called at driver load time.  Claims 9000 ctlrs.
+ *
+ * Input:              dev     -- bus device corresponding to the ctlr
+ * Output:             None
+ * Return value:       <= 0    -- success
+ *                     > 0     -- failure
+ */
+static TW_INT32
+twa_probe(device_t dev)
+{
+       static TW_UINT8 first_ctlr = 1;
+
+       tw_osli_dbg_printf(3, "entered");
+
+       if (tw_cl_ctlr_supported(pci_get_vendor(dev), pci_get_device(dev))) {
+               device_set_desc(dev, TW_OSLI_DEVICE_NAME);
+               /* Print the driver version only once. */
+               if (first_ctlr) {
+                       kprintf("3ware device driver for 9000 series storage "
+                               "controllers, version: %s\n",
+                               TW_OSL_DRIVER_VERSION_STRING);
+                       first_ctlr = 0;
+               }
+               return(0);
+       }
+       return(ENXIO);
+}
+
+
+
+/*
+ * Function name:      twa_attach
+ * Description:                Allocates pci resources; updates sc; adds a node to the
+ *                     sysctl tree to expose the driver version; makes calls
+ *                     (to the Common Layer) to initialize ctlr, and to
+ *                     attach to CAM.
+ *
+ * Input:              dev     -- bus device corresponding to the ctlr
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+static TW_INT32
+twa_attach(device_t dev)
+{
+       struct twa_softc        *sc = device_get_softc(dev);
+       TW_UINT32               command;
+       TW_INT32                bar_num;
+       TW_INT32                bar0_offset;
+       TW_INT32                bar_size;
+       TW_INT32                error;
+
+       tw_osli_dbg_dprintf(3, sc, "entered");
+
+       sc->ctlr_handle.osl_ctlr_ctxt = sc;
+
+       /* Initialize the softc structure. */
+       sc->bus_dev = dev;
+       sc->device_id = pci_get_device(dev);
+
+       /* Initialize the mutexes right here. */
+       sc->io_lock = &(sc->io_lock_handle);
+       spin_init(sc->io_lock);
+       sc->q_lock = &(sc->q_lock_handle);
+       spin_init(sc->q_lock);
+       sc->sim_lock = &(sc->sim_lock_handle);
+       lockinit(sc->sim_lock, "tw_osl_sim_lock", 0, LK_CANRECURSE);
+
+       sysctl_ctx_init(&sc->sysctl_ctxt);
+       sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctxt,
+               SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
+               device_get_nameunit(dev), CTLFLAG_RD, 0, "");
+       if (sc->sysctl_tree == NULL) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2000,
+                       "Cannot add sysctl tree node",
+                       ENXIO);
+               return(ENXIO);
+       }
+       SYSCTL_ADD_STRING(&sc->sysctl_ctxt, SYSCTL_CHILDREN(sc->sysctl_tree),
+               OID_AUTO, "driver_version", CTLFLAG_RD,
+               TW_OSL_DRIVER_VERSION_STRING, 0, "TWA driver version");
+
+       /* Make sure we are going to be able to talk to this board. */
+       command = pci_read_config(dev, PCIR_COMMAND, 2);
+       if ((command & PCIM_CMD_PORTEN) == 0) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2001,
+                       "Register window not available",
+                       ENXIO);
+               tw_osli_free_resources(sc);
+               return(ENXIO);
+       }
+
+       /* Force the busmaster enable bit on, in case the BIOS forgot. */
+       command |= PCIM_CMD_BUSMASTEREN;
+       pci_write_config(dev, PCIR_COMMAND, command, 2);
+
+       /* Allocate the PCI register window. */
+       if ((error = tw_cl_get_pci_bar_info(sc->device_id, TW_CL_BAR_TYPE_MEM,
+               &bar_num, &bar0_offset, &bar_size))) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x201F,
+                       "Can't get PCI BAR info",
+                       error);
+               tw_osli_free_resources(sc);
+               return(error);
+       }
+       sc->reg_res_id = PCIR_BARS + bar0_offset;
+       if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+                               &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE))
+                               == NULL) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2002,
+                       "Can't allocate register window",
+                       ENXIO);
+               tw_osli_free_resources(sc);
+               return(ENXIO);
+       }
+       sc->bus_tag = rman_get_bustag(sc->reg_res);
+       sc->bus_handle = rman_get_bushandle(sc->reg_res);
+
+       /* Allocate and register our interrupt. */
+       sc->irq_res_id = 0;
+       if ((sc->irq_res = bus_alloc_resource(sc->bus_dev, SYS_RES_IRQ,
+                               &(sc->irq_res_id), 0, ~0, 1,
+                               RF_SHAREABLE | RF_ACTIVE)) == NULL) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2003,
+                       "Can't allocate interrupt",
+                       ENXIO);
+               tw_osli_free_resources(sc);
+               return(ENXIO);
+       }
+       if ((error = bus_setup_intr(sc->bus_dev, sc->irq_res,
+                       0,
+                       twa_pci_intr,
+                       sc, &sc->intr_handle, NULL))) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2004,
+                       "Can't set up interrupt",
+                       error);
+               tw_osli_free_resources(sc);
+               return(error);
+       }
+
+       if ((error = tw_osli_alloc_mem(sc))) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2005,
+                       "Memory allocation failure",
+                       error);
+               tw_osli_free_resources(sc);
+               return(error);
+       }
+
+       /* Initialize the Common Layer for this controller. */
+       if ((error = tw_cl_init_ctlr(&sc->ctlr_handle, sc->flags, sc->device_id,
+                       TW_OSLI_MAX_NUM_REQUESTS, TW_OSLI_MAX_NUM_AENS,
+                       sc->non_dma_mem, sc->dma_mem,
+                       sc->dma_mem_phys
+                       ))) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2006,
+                       "Failed to initialize Common Layer/controller",
+                       error);
+               tw_osli_free_resources(sc);
+               return(error);
+       }
+
+       /* Create the control device. */
+       sc->ctrl_dev = make_dev(&twa_ops, device_get_unit(sc->bus_dev),
+                       UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR,
+                       "twa%d", device_get_unit(sc->bus_dev));
+       sc->ctrl_dev->si_drv1 = sc;
+
+       if ((error = tw_osli_cam_attach(sc))) {
+               tw_osli_free_resources(sc);
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2007,
+                       "Failed to initialize CAM",
+                       error);
+               return(error);
+       }
+
+       return(0);
+}
+
+
+
+/*
+ * Function name:      tw_osli_alloc_mem
+ * Description:                Allocates memory needed both by CL and OSL.
+ *
+ * Input:              sc      -- OSL internal controller context
+ * Output:             None
+ * Return value:       0       -- success
+ *                     non-zero-- failure
+ */
+static TW_INT32
+tw_osli_alloc_mem(struct twa_softc *sc)
+{
+       struct tw_osli_req_context      *req;
+       TW_UINT32                       max_sg_elements;
+       TW_UINT32                       non_dma_mem_size;
+       TW_UINT32                       dma_mem_size;
+       TW_INT32                        error;
+       TW_INT32                        i;
+
+       tw_osli_dbg_dprintf(3, sc, "entered");
+
+       sc->flags |= (sizeof(bus_addr_t) == 8) ? TW_CL_64BIT_ADDRESSES : 0;
+       sc->flags |= (sizeof(bus_size_t) == 8) ? TW_CL_64BIT_SG_LENGTH : 0;
+
+       max_sg_elements = (sizeof(bus_addr_t) == 8) ?
+               TW_CL_MAX_64BIT_SG_ELEMENTS : TW_CL_MAX_32BIT_SG_ELEMENTS;
+
+       if ((error = tw_cl_get_mem_requirements(&sc->ctlr_handle, sc->flags,
+                       sc->device_id, TW_OSLI_MAX_NUM_REQUESTS,  TW_OSLI_MAX_NUM_AENS,
+                       &(sc->alignment), &(sc->sg_size_factor),
+                       &non_dma_mem_size, &dma_mem_size
+                       ))) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2008,
+                       "Can't get Common Layer's memory requirements",
+                       error);
+               return(error);
+       }
+
+       if ((sc->non_dma_mem = kmalloc(non_dma_mem_size, TW_OSLI_MALLOC_CLASS,
+                               M_WAITOK)) == NULL) {
+               tw_osli_printf(sc, "error = %d",
+                       TW_CL_SEVERITY_ERROR_STRING,
+                       TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER,
+                       0x2009,
+                       "Can't allocate non-dma memory",
+                       ENOMEM);
+               return(ENOMEM);
+       }
+
+       /* Create the parent dma tag. */
+       if (bus_dma_tag_create(NULL,                    /* parent */
+                               sc->alignment,          /* alignment */
+                               TW_OSLI_DMA_BOUNDARY,   /* boundary */
+                               BUS_SPACE_MAXADDR,      /* lowaddr */
+                               BUS_SPACE_MAXADDR,      /* highaddr */
+                               NULL