diff options
author | delphij <delphij@FreeBSD.org> | 2011-10-04 21:40:25 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2011-10-04 21:40:25 +0000 |
commit | 9da6f154d20f9979b10969ef3d1775c66118abf1 (patch) | |
tree | 8bf58a4bedd5b6037fb04497b9166be360ce5c7b | |
parent | 66a96c11c836a64c8381f3046d1b055c9bd67456 (diff) | |
download | FreeBSD-src-9da6f154d20f9979b10969ef3d1775c66118abf1.zip FreeBSD-src-9da6f154d20f9979b10969ef3d1775c66118abf1.tar.gz |
Add the 9750 SATA+SAS 6Gb/s RAID controller card driver, tws(4). Many
thanks for their contiued support to FreeBSD.
This is version 10.80.00.003 from codeset 10.2.1 [1]
Obtained from: LSI http://kb.lsi.com/Download16574.aspx [1]
-rw-r--r-- | share/man/man4/Makefile | 1 | ||||
-rw-r--r-- | share/man/man4/tws.4 | 118 | ||||
-rw-r--r-- | sys/amd64/conf/GENERIC | 1 | ||||
-rw-r--r-- | sys/conf/files | 5 | ||||
-rw-r--r-- | sys/dev/tws/tws.c | 905 | ||||
-rw-r--r-- | sys/dev/tws/tws.h | 265 | ||||
-rw-r--r-- | sys/dev/tws/tws_cam.c | 1342 | ||||
-rw-r--r-- | sys/dev/tws/tws_hdm.c | 535 | ||||
-rw-r--r-- | sys/dev/tws/tws_hdm.h | 420 | ||||
-rw-r--r-- | sys/dev/tws/tws_services.c | 401 | ||||
-rw-r--r-- | sys/dev/tws/tws_services.h | 141 | ||||
-rw-r--r-- | sys/dev/tws/tws_user.c | 379 | ||||
-rw-r--r-- | sys/dev/tws/tws_user.h | 156 | ||||
-rw-r--r-- | sys/i386/conf/GENERIC | 1 | ||||
-rw-r--r-- | sys/modules/Makefile | 1 | ||||
-rw-r--r-- | sys/modules/tws/Makefile | 10 |
16 files changed, 4681 insertions, 0 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 091eb28..b38a01c 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -447,6 +447,7 @@ MAN= aac.4 \ tun.4 \ twa.4 \ twe.4 \ + tws.4 \ tx.4 \ txp.4 \ u3g.4 \ diff --git a/share/man/man4/tws.4 b/share/man/man4/tws.4 new file mode 100644 index 0000000..9ce154e --- /dev/null +++ b/share/man/man4/tws.4 @@ -0,0 +1,118 @@ +.\" +.\"Copyright (c) 2010, 2011 iXsystems, Inc. +.\"All rights reserved. +.\" written by: Xin LI <delphij@FreeBSD.org> +.\" +.\"Redistribution and use in source and binary forms, with or without +.\"modification, are permitted provided that the following conditions +.\"are met: +.\"1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\"2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\"THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\"ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\"LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\"OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\"SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 4, 2011 +.Dt TWS 4 +.Os +.Sh NAME +.Nm tws +.Nd 3ware 9750 SATA+SAS 6Gb/s RAID controller card driver +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device scbus" +.Cd "device tws" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +tws_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for LSI's 3ware 9750 SATA+SAS 6Gb/s RAID controller cards. +.Pp +These controllers feature the LSISAS2108 6Gb/s SAS RAID-on-Chip (ROC) +and are available in 4- and 8-port configurations, supports RAID levels +0, 1, 5, 6, 10, 50 and single disk, with 96 SATA and/or SAS hard drives and SSDs. +.Pp +For further hardware information, see +.Pa http://www.lsi.com/. +.Sh HARDWARE +The +.Nm +driver supports the following SATA/SAS RAID controller: +.Pp +.Bl -bullet -compact +.It +LSI's 3ware SAS 9750 series +.El +.Sh LOADER TUNABLES +Tunables can be set at the +.Xr loader 8 +prompt before booting the kernel or stored in +.Xr loader.conf 5 . +.Bl -tag -width "hw.tws.use_32bit_sgls" +.It Va hw.tws.cam_depth +The maximium queued CAM SIM requests for one controller. +The default value is 256. +.It Va hw.tws.enable_msi +This tunable enables MSI support on the controller if set to a non-zero value. +The default value is 0. +.It Va hw.tws.queue_depth +The maximium queued requests for one controller. +.It Va hw.tws.use_32bit_sgls +Limit the driver to use only 32-bit SG elements regardless whether the operating +system is running in 64-bit mode. +The default value is 0. +.El +.Sh FILES +.Bl -tag -width ".Pa /dev/tws?" -compact +.It Pa /dev/da? +array/logical disk interface +.It Pa /dev/tws? +management interface +.El +.Sh DIAGNOSTICS +Whenever the driver encounters a command failure, it prints out an error code in +the format: +.Qq Li "ERROR: (<error source>: <error code>):" , +followed by a text description of the error. +There are other error messages and warnings that the +driver prints out, depending on the kinds of errors that it encounters. +If the driver is compiled with +.Dv TWS_DEBUG +defined, it prints out a whole bunch of debug +messages. +.Sh SEE ALSO +.Xr da 4 , +.Xr scsi 4 +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Manjunath Ranganathaiah +for LSI and this manual page was written by +.An Xin LI Aq delphij@FreeBSD.org +for iXsystems, Inc. diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index db87d0b..325f034 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -151,6 +151,7 @@ device mlx # Mylex DAC960 family #XXX pointer/int warnings #device pst # Promise Supertrak SX6000 device twe # 3ware ATA RAID +device tws # LSI 3ware 9750 SATA+SAS 6Gb/s RAID controller # atkbdc0 controls both the keyboard and the PS/2 mouse device atkbdc # AT keyboard controller diff --git a/sys/conf/files b/sys/conf/files index 5c5d92d..0633223 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1833,6 +1833,11 @@ dev/twa/tw_osl_freebsd.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twe/twe.c optional twe dev/twe/twe_freebsd.c optional twe +dev/tws/tws.c optional tws +dev/tws/tws_cam.c optional tws +dev/tws/tws_hdm.c optional tws +dev/tws/tws_services.c optional tws +dev/tws/tws_user.c optional tws dev/tx/if_tx.c optional tx dev/txp/if_txp.c optional txp dev/uart/uart_bus_acpi.c optional uart acpi diff --git a/sys/dev/tws/tws.c b/sys/dev/tws/tws.c new file mode 100644 index 0000000..57a463e --- /dev/null +++ b/sys/dev/tws/tws.c @@ -0,0 +1,905 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ +*/ + + +#include <dev/tws/tws.h> +#include <dev/tws/tws_services.h> +#include <dev/tws/tws_hdm.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> + +MALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver"); +int tws_queue_depth = TWS_MAX_REQS; +int tws_enable_msi = 0; +int tws_enable_msix = 0; + + + +/* externs */ +extern int tws_cam_attach(struct tws_softc *sc); +extern void tws_cam_detach(struct tws_softc *sc); +extern int tws_init_ctlr(struct tws_softc *sc); +extern boolean tws_ctlr_ready(struct tws_softc *sc); +extern void tws_turn_off_interrupts(struct tws_softc *sc); +extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ); +extern struct tws_request *tws_q_remove_request(struct tws_softc *sc, + struct tws_request *req, u_int8_t q_type ); +extern struct tws_request *tws_q_remove_head(struct tws_softc *sc, + u_int8_t q_type ); +extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id); +extern boolean tws_ctlr_reset(struct tws_softc *sc); +extern void tws_intr(void *arg); +extern int tws_use_32bit_sgls; + + +struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type); +int tws_init_connect(struct tws_softc *sc, u_int16_t mc); +void tws_send_event(struct tws_softc *sc, u_int8_t event); +uint8_t tws_get_state(struct tws_softc *sc); +void tws_release_request(struct tws_request *req); + + + +/* Function prototypes */ +static d_open_t tws_open; +static d_close_t tws_close; +static d_read_t tws_read; +static d_write_t tws_write; +extern d_ioctl_t tws_ioctl; + +static int tws_init(struct tws_softc *sc); +static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, + int nseg, int error); + +static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size); +static int tws_init_aen_q(struct tws_softc *sc); +static int tws_init_trace_q(struct tws_softc *sc); +static int tws_setup_irq(struct tws_softc *sc); +int tws_setup_intr(struct tws_softc *sc, int irqs); +int tws_teardown_intr(struct tws_softc *sc); + + +/* Character device entry points */ + +static struct cdevsw tws_cdevsw = { + .d_version = D_VERSION, + .d_open = tws_open, + .d_close = tws_close, + .d_read = tws_read, + .d_write = tws_write, + .d_ioctl = tws_ioctl, + .d_name = "tws", +}; + +/* + * In the cdevsw routines, we find our softc by using the si_drv1 member + * of struct cdev. We set this variable to point to our softc in our + * attach routine when we create the /dev entry. + */ + +int +tws_open(struct cdev *dev, int oflags, int devtype, d_thread_t *td) +{ + struct tws_softc *sc = dev->si_drv1; + + if ( sc ) + TWS_TRACE_DEBUG(sc, "entry", dev, oflags); + return (0); +} + +int +tws_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td) +{ + struct tws_softc *sc = dev->si_drv1; + + if ( sc ) + TWS_TRACE_DEBUG(sc, "entry", dev, fflag); + return (0); +} + +int +tws_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct tws_softc *sc = dev->si_drv1; + + if ( sc ) + TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); + return (0); +} + +int +tws_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct tws_softc *sc = dev->si_drv1; + + if ( sc ) + TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); + return (0); +} + +/* PCI Support Functions */ + +/* + * Compare the device ID of this device against the IDs that this driver + * supports. If there is a match, set the description and return success. + */ +static int +tws_probe(device_t dev) +{ + static u_int8_t first_ctlr = 1; + + if ((pci_get_vendor(dev) == TWS_VENDOR_ID) && + (pci_get_device(dev) == TWS_DEVICE_ID)) { + device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller"); + if (first_ctlr) { + printf("LSI 3ware device driver for SAS/SATA storage " + "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING); + first_ctlr = 0; + } + + return(0); + } + return (ENXIO); +} + +/* Attach function is only called if the probe is successful. */ + +static int +tws_attach(device_t dev) +{ + struct tws_softc *sc = device_get_softc(dev); + u_int32_t cmd, bar; + int error=0,i; + + /* no tracing yet */ + /* Look up our softc and initialize its fields. */ + sc->tws_dev = dev; + sc->device_id = pci_get_device(dev); + sc->subvendor_id = pci_get_subvendor(dev); + sc->subdevice_id = pci_get_subdevice(dev); + + /* Intialize mutexes */ + mtx_init( &sc->q_lock, "tws_q_lock", NULL, MTX_DEF); + mtx_init( &sc->sim_lock, "tws_sim_lock", NULL, MTX_DEF); + mtx_init( &sc->gen_lock, "tws_gen_lock", NULL, MTX_DEF); + mtx_init( &sc->io_lock, "tws_io_lock", NULL, MTX_DEF); + + if ( tws_init_trace_q(sc) == FAILURE ) + printf("trace init failure\n"); + /* send init event */ + mtx_lock(&sc->gen_lock); + tws_send_event(sc, TWS_INIT_START); + mtx_unlock(&sc->gen_lock); + + +#if _BYTE_ORDER == _BIG_ENDIAN + TWS_TRACE(sc, "BIG endian", 0, 0); +#endif + /* sysctl context setup */ + sysctl_ctx_init(&sc->tws_clist); + sc->tws_oidp = SYSCTL_ADD_NODE(&sc->tws_clist, + SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, + device_get_nameunit(dev), + CTLFLAG_RD, 0, ""); + if ( sc->tws_oidp == NULL ) { + tws_log(sc, SYSCTL_TREE_NODE_ADD); + goto attach_fail_1; + } + SYSCTL_ADD_STRING(&sc->tws_clist, SYSCTL_CHILDREN(sc->tws_oidp), + OID_AUTO, "driver_version", CTLFLAG_RD, + TWS_DRIVER_VERSION_STRING, 0, "TWS driver version"); + + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + if ( (cmd & PCIM_CMD_PORTEN) == 0) { + tws_log(sc, PCI_COMMAND_READ); + goto attach_fail_1; + } + /* Force the busmaster enable bit on. */ + cmd |= PCIM_CMD_BUSMASTEREN; + pci_write_config(dev, PCIR_COMMAND, cmd, 2); + + bar = pci_read_config(dev, TWS_PCI_BAR0, 4); + TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0); + bar = pci_read_config(dev, TWS_PCI_BAR1, 4); + bar = bar & ~TWS_BIT2; + TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0); + + /* MFA base address is BAR2 register used for + * push mode. Firmware will evatualy move to + * pull mode during witch this needs to change + */ +#ifndef TWS_PULL_MODE_ENABLE + sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4); + sc->mfa_base = sc->mfa_base & ~TWS_BIT2; + TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0); +#endif + + /* allocate MMIO register space */ + sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */ + if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, + &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE)) + == NULL) { + tws_log(sc, ALLOC_MEMORY_RES); + goto attach_fail_1; + } + sc->bus_tag = rman_get_bustag(sc->reg_res); + sc->bus_handle = rman_get_bushandle(sc->reg_res); + +#ifndef TWS_PULL_MODE_ENABLE + /* Allocate bus space for inbound mfa */ + sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */ + if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY, + &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE)) + == NULL) { + tws_log(sc, ALLOC_MEMORY_RES); + goto attach_fail_2; + } + sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res); + sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res); +#endif + + /* Allocate and register our interrupt. */ + sc->intr_type = TWS_INTx; /* default */ + + if ( tws_enable_msi ) + sc->intr_type = TWS_MSI; + if ( tws_setup_irq(sc) == FAILURE ) { + tws_log(sc, ALLOC_MEMORY_RES); + goto attach_fail_3; + } + + /* + * Create a /dev entry for this device. The kernel will assign us + * a major number automatically. We use the unit number of this + * device as the minor number and name the character device + * "tws<unit>". + */ + sc->tws_cdev = make_dev(&tws_cdevsw, device_get_unit(dev), + UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u", + device_get_unit(dev)); + sc->tws_cdev->si_drv1 = sc; + + if ( tws_init(sc) == FAILURE ) { + tws_log(sc, TWS_INIT_FAILURE); + goto attach_fail_4; + } + if ( tws_init_ctlr(sc) == FAILURE ) { + tws_log(sc, TWS_CTLR_INIT_FAILURE); + goto attach_fail_4; + } + if ((error = tws_cam_attach(sc))) { + tws_log(sc, TWS_CAM_ATTACH); + goto attach_fail_4; + } + /* send init complete event */ + mtx_lock(&sc->gen_lock); + tws_send_event(sc, TWS_INIT_COMPLETE); + mtx_unlock(&sc->gen_lock); + + TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id); + return(0); + +attach_fail_4: + tws_teardown_intr(sc); + destroy_dev(sc->tws_cdev); +attach_fail_3: + for(i=0;i<sc->irqs;i++) { + if ( sc->irq_res[i] ){ + if (bus_release_resource(sc->tws_dev, + SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i])) + TWS_TRACE(sc, "bus irq res", 0, 0); + } + } +#ifndef TWS_PULL_MODE_ENABLE +attach_fail_2: +#endif + if ( sc->mfa_res ){ + if (bus_release_resource(sc->tws_dev, + SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) + TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id); + } + if ( sc->reg_res ){ + if (bus_release_resource(sc->tws_dev, + SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) + TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id); + } +attach_fail_1: + mtx_destroy(&sc->q_lock); + mtx_destroy(&sc->sim_lock); + mtx_destroy(&sc->gen_lock); + mtx_destroy(&sc->io_lock); + sysctl_ctx_free(&sc->tws_clist); + return (ENXIO); +} + +/* Detach device. */ + +static int +tws_detach(device_t dev) +{ + struct tws_softc *sc = device_get_softc(dev); + int i; + u_int32_t reg; + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + + mtx_lock(&sc->gen_lock); + tws_send_event(sc, TWS_UNINIT_START); + mtx_unlock(&sc->gen_lock); + + /* needs to disable interrupt before detaching from cam */ + tws_turn_off_interrupts(sc); + /* clear door bell */ + tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); + reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4); + TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0); + sc->obfl_q_overrun = false; + tws_init_connect(sc, 1); + + /* Teardown the state in our softc created in our attach routine. */ + /* Disconnect the interrupt handler. */ + tws_teardown_intr(sc); + + /* Release irq resource */ + for(i=0;i<sc->irqs;i++) { + if ( sc->irq_res[i] ){ + if (bus_release_resource(sc->tws_dev, + SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i])) + TWS_TRACE(sc, "bus release irq resource", + i, sc->irq_res_id[i]); + } + } + if ( sc->intr_type == TWS_MSI ) { + pci_release_msi(sc->tws_dev); + } + + tws_cam_detach(sc); + + /* Release memory resource */ + if ( sc->mfa_res ){ + if (bus_release_resource(sc->tws_dev, + SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) + TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id); + } + if ( sc->reg_res ){ + if (bus_release_resource(sc->tws_dev, + SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) + TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id); + } + + free(sc->reqs, M_TWS); + free(sc->sense_bufs, M_TWS); + free(sc->scan_ccb, M_TWS); + free(sc->aen_q.q, M_TWS); + free(sc->trace_q.q, M_TWS); + mtx_destroy(&sc->q_lock); + mtx_destroy(&sc->sim_lock); + mtx_destroy(&sc->gen_lock); + mtx_destroy(&sc->io_lock); + destroy_dev(sc->tws_cdev); + sysctl_ctx_free(&sc->tws_clist); + return (0); +} + +int +tws_setup_intr(struct tws_softc *sc, int irqs) +{ + int i, error; + + for(i=0;i<irqs;i++) { + if (!(sc->intr_handle[i])) { + if ((error = bus_setup_intr(sc->tws_dev, sc->irq_res[i], + INTR_TYPE_CAM | INTR_MPSAFE, +#if (__FreeBSD_version >= 700000) + NULL, +#endif + tws_intr, sc, &sc->intr_handle[i]))) { + tws_log(sc, SETUP_INTR_RES); + return(FAILURE); + } + } + } + return(SUCCESS); + +} + + +int +tws_teardown_intr(struct tws_softc *sc) +{ + int i, error; + + for(i=0;i<sc->irqs;i++) { + if (sc->intr_handle[i]) { + error = bus_teardown_intr(sc->tws_dev, + sc->irq_res[i], sc->intr_handle[i]); + sc->intr_handle[i] = NULL; + } + } + return(SUCCESS); +} + + +static int +tws_setup_irq(struct tws_softc *sc) +{ + int messages; + u_int16_t cmd; + + cmd = pci_read_config(sc->tws_dev, PCIR_COMMAND, 2); + switch(sc->intr_type) { + case TWS_INTx : + cmd = cmd & ~0x0400; + pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); + sc->irqs = 1; + sc->irq_res_id[0] = 0; + sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, + &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE); + if ( ! sc->irq_res[0] ) + return(FAILURE); + if ( tws_setup_intr(sc, sc->irqs) == FAILURE ) + return(FAILURE); + device_printf(sc->tws_dev, "Using legacy INTx\n"); + break; + case TWS_MSI : + cmd = cmd | 0x0400; + pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); + sc->irqs = 1; + sc->irq_res_id[0] = 1; + messages = 1; + if (pci_alloc_msi(sc->tws_dev, &messages) != 0 ) { + TWS_TRACE(sc, "pci alloc msi fail", 0, messages); + return(FAILURE); + } + sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, + &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE); + + if ( !sc->irq_res[0] ) + return(FAILURE); + if ( tws_setup_intr(sc, sc->irqs) == FAILURE ) + return(FAILURE); + device_printf(sc->tws_dev, "Using MSI\n"); + break; + + } + + return(SUCCESS); +} + +static int +tws_init(struct tws_softc *sc) +{ + + u_int32_t max_sg_elements; + u_int32_t dma_mem_size; + int error; + u_int32_t reg; + + sc->seq_id = 0; + if ( tws_queue_depth > TWS_MAX_REQS ) + tws_queue_depth = TWS_MAX_REQS; + if (tws_queue_depth < TWS_RESERVED_REQS+1) + tws_queue_depth = TWS_RESERVED_REQS+1; + sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false; + max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ? + TWS_MAX_64BIT_SG_ELEMENTS : + TWS_MAX_32BIT_SG_ELEMENTS; + dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) + + (TWS_SECTOR_SIZE) ; + if ( bus_dma_tag_create(NULL, /* parent */ + TWS_ALIGNMENT, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE, /* maxsize */ + max_sg_elements, /* numsegs */ + BUS_SPACE_MAXSIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->parent_tag /* tag */ + )) { + TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements, + sc->is64bit); + return(ENOMEM); + } + /* In bound message frame requires 16byte alignment. + * Outbound MF's can live with 4byte alignment - for now just + * use 16 for both. + */ + if ( bus_dma_tag_create(sc->parent_tag, /* parent */ + TWS_IN_MF_ALIGNMENT, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + dma_mem_size, /* maxsize */ + 1, /* numsegs */ + BUS_SPACE_MAXSIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sc->cmd_tag /* tag */ + )) { + TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); + return(ENOMEM); + } + + if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem, + BUS_DMA_NOWAIT, &sc->cmd_map)) { + TWS_TRACE_DEBUG(sc, "DMA mem alloc fail", max_sg_elements, sc->is64bit); + return(ENOMEM); + } + + /* if bus_dmamem_alloc succeeds then bus_dmamap_load will succeed */ + sc->dma_mem_phys=0; + error = bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem, + dma_mem_size, tws_dmamap_cmds_load_cbfn, + &sc->dma_mem_phys, 0); + + /* + * Create a dma tag for data buffers; size will be the maximum + * possible I/O size (128kB). + */ + if (bus_dma_tag_create(sc->parent_tag, /* parent */ + TWS_ALIGNMENT, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + TWS_MAX_IO_SIZE, /* maxsize */ + max_sg_elements, /* nsegments */ + TWS_MAX_IO_SIZE, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + busdma_lock_mutex, /* lockfunc */ + &sc->io_lock, /* lockfuncarg */ + &sc->data_tag /* tag */)) { + TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); + return(ENOMEM); + } + + sc->reqs = malloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS, + M_WAITOK | M_ZERO); + if ( sc->reqs == NULL ) { + TWS_TRACE_DEBUG(sc, "malloc failed", 0, sc->is64bit); + return(ENOMEM); + } + sc->sense_bufs = malloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS, + M_WAITOK | M_ZERO); + if ( sc->sense_bufs == NULL ) { + TWS_TRACE_DEBUG(sc, "sense malloc failed", 0, sc->is64bit); + return(ENOMEM); + } + sc->scan_ccb = malloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO); + if ( sc->scan_ccb == NULL ) { + TWS_TRACE_DEBUG(sc, "ccb malloc failed", 0, sc->is64bit); + return(ENOMEM); + } + + if ( !tws_ctlr_ready(sc) ) + if( !tws_ctlr_reset(sc) ) + return(FAILURE); + + bzero(&sc->stats, sizeof(struct tws_stats)); + tws_init_qs(sc); + tws_turn_off_interrupts(sc); + + /* + * enable pull mode by setting bit1 . + * setting bit0 to 1 will enable interrupt coalesing + * will revisit. + */ + +#ifdef TWS_PULL_MODE_ENABLE + + reg = tws_read_reg(sc, TWS_I2O0_CTL, 4); + TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL); + tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4); + +#endif + + TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL); + if ( tws_init_reqs(sc, dma_mem_size) == FAILURE ) + return(FAILURE); + if ( tws_init_aen_q(sc) == FAILURE ) + return(FAILURE); + + return(SUCCESS); + +} + +static int +tws_init_aen_q(struct tws_softc *sc) +{ + sc->aen_q.head=0; + sc->aen_q.tail=0; + sc->aen_q.depth=256; + sc->aen_q.overflow=0; + sc->aen_q.q = malloc(sizeof(struct tws_event_packet)*sc->aen_q.depth, + M_TWS, M_WAITOK | M_ZERO); + if ( ! sc->aen_q.q ) + return(FAILURE); + return(SUCCESS); +} + +static int +tws_init_trace_q(struct tws_softc *sc) +{ + sc->trace_q.head=0; + sc->trace_q.tail=0; + sc->trace_q.depth=256; + sc->trace_q.overflow=0; + sc->trace_q.q = malloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth, + M_TWS, M_WAITOK | M_ZERO); + if ( ! sc->trace_q.q ) + return(FAILURE); + return(SUCCESS); +} + +static int +tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size) +{ + + struct tws_command_packet *cmd_buf; + cmd_buf = (struct tws_command_packet *)sc->dma_mem; + int i; + + bzero(cmd_buf, dma_mem_size); + TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0); + mtx_lock(&sc->q_lock); + for ( i=0; i< tws_queue_depth; i++) + { + if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) { + /* log a ENOMEM failure msg here */ + return(FAILURE); + } + sc->reqs[i].cmd_pkt = &cmd_buf[i]; + + sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ; + sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys + + (i * sizeof(struct tws_command_packet)); + + sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys + + sizeof(struct tws_command_header) + + (i * sizeof(struct tws_command_packet)); + sc->reqs[i].request_id = i; + sc->reqs[i].sc = sc; + + sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128; + + sc->reqs[i].state = TWS_REQ_STATE_FREE; + if ( i >= TWS_RESERVED_REQS ) + tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q); + } + mtx_unlock(&sc->q_lock); + return(SUCCESS); +} + +static void +tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, + int nseg, int error) +{ + + /* printf("command load done \n"); */ + + *((bus_addr_t *)arg) = segs[0].ds_addr; +} + +void +tws_send_event(struct tws_softc *sc, u_int8_t event) +{ + mtx_assert(&sc->gen_lock, MA_OWNED); + TWS_TRACE_DEBUG(sc, "received event ", 0, event); + switch (event) { + + case TWS_INIT_START: + sc->tws_state = TWS_INIT; + break; + + case TWS_INIT_COMPLETE: + if (sc->tws_state != TWS_INIT) { + device_printf(sc->tws_dev, "invalid state transition %d => TWS_ONLINE\n", sc->tws_state); + } else { + sc->tws_state = TWS_ONLINE; + } + break; + + case TWS_RESET_START: + /* We can transition to reset state from any state except reset*/ + if (sc->tws_state != TWS_RESET) { + sc->tws_prev_state = sc->tws_state; + sc->tws_state = TWS_RESET; + } + break; + + case TWS_RESET_COMPLETE: + if (sc->tws_state != TWS_RESET) { + device_printf(sc->tws_dev, "invalid state transition %d => %d (previous state)\n", sc->tws_state, sc->tws_prev_state); + } else { + sc->tws_state = sc->tws_prev_state; + } + break; + + case TWS_SCAN_FAILURE: + if (sc->tws_state != TWS_ONLINE) { + device_printf(sc->tws_dev, "invalid state transition %d => TWS_OFFLINE\n", sc->tws_state); + } else { + sc->tws_state = TWS_OFFLINE; + } + break; + + case TWS_UNINIT_START: + if ((sc->tws_state != TWS_ONLINE) && (sc->tws_state != TWS_OFFLINE)) { + device_printf(sc->tws_dev, "invalid state transition %d => TWS_UNINIT\n", sc->tws_state); + } else { + sc->tws_state = TWS_UNINIT; + } + break; + } + +} + +uint8_t +tws_get_state(struct tws_softc *sc) +{ + + return((u_int8_t)sc->tws_state); + +} + +/* Called during system shutdown after sync. */ + +static int +tws_shutdown(device_t dev) +{ + + struct tws_softc *sc = device_get_softc(dev); + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + + tws_turn_off_interrupts(sc); + tws_init_connect(sc, 1); + + return (0); +} + +/* + * Device suspend routine. + */ +static int +tws_suspend(device_t dev) +{ + struct tws_softc *sc = device_get_softc(dev); + + if ( sc ) + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + return (0); +} + +/* + * Device resume routine. + */ +static int +tws_resume(device_t dev) +{ + + struct tws_softc *sc = device_get_softc(dev); + + if ( sc ) + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + return (0); +} + + +struct tws_request * +tws_get_request(struct tws_softc *sc, u_int16_t type) +{ + struct mtx *my_mutex = ((type == TWS_REQ_TYPE_SCSI_IO) ? &sc->q_lock : &sc->gen_lock); + struct tws_request *r = NULL; + + mtx_lock(my_mutex); + + if (type == TWS_REQ_TYPE_SCSI_IO) { + r = tws_q_remove_head(sc, TWS_FREE_Q); + } else { + if ( sc->reqs[type].state == TWS_REQ_STATE_FREE ) { + r = &sc->reqs[type]; + } + } + + if ( r ) { + bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache)); + r->data = NULL; + r->length = 0; + r->type = type; + r->flags = TWS_DIR_UNKNOWN; + r->error_code = TWS_REQ_RET_INVALID; + r->cb = NULL; + r->ccb_ptr = NULL; + r->thandle.callout = NULL; + r->next = r->prev = NULL; + + r->state = ((type == TWS_REQ_TYPE_SCSI_IO) ? TWS_REQ_STATE_TRAN : TWS_REQ_STATE_BUSY); + } + + mtx_unlock(my_mutex); + + return(r); +} + +void +tws_release_request(struct tws_request *req) +{ + + struct tws_softc *sc = req->sc; + + TWS_TRACE_DEBUG(sc, "entry", sc, 0); + mtx_lock(&sc->q_lock); + tws_q_insert_tail(sc, req, TWS_FREE_Q); + mtx_unlock(&sc->q_lock); +} + +static device_method_t tws_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tws_probe), + DEVMETHOD(device_attach, tws_attach), + DEVMETHOD(device_detach, tws_detach), + DEVMETHOD(device_shutdown, tws_shutdown), + DEVMETHOD(device_suspend, tws_suspend), + DEVMETHOD(device_resume, tws_resume), + + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + { 0, 0 } +}; + +static driver_t tws_driver = { + "tws", + tws_methods, + sizeof(struct tws_softc) +}; + + +static devclass_t tws_devclass; + +/* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */ +DRIVER_MODULE(tws, pci, tws_driver, tws_devclass, 0, 0); +MODULE_DEPEND(tws, cam, 1, 1, 1); +MODULE_DEPEND(tws, pci, 1, 1, 1); + +TUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth); +TUNABLE_INT("hw.tws.enable_msi", &tws_enable_msi); diff --git a/sys/dev/tws/tws.h b/sys/dev/tws/tws.h new file mode 100644 index 0000000..cf9f727 --- /dev/null +++ b/sys/dev/tws/tws.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> /* defines used in kernel.h */ +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/errno.h> +#include <sys/kernel.h> /* types used in module initialization */ +#include <sys/conf.h> /* cdevsw struct */ +#include <sys/uio.h> /* uio struct */ +#include <sys/malloc.h> +#include <sys/bus.h> /* structs, prototypes for pci bus stuff */ + + +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/pci/pcivar.h> /* For pci_get macros! */ +#include <dev/pci/pcireg.h> + +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/stat.h> + + +#define TWS_PULL_MODE_ENABLE 1 + +MALLOC_DECLARE(M_TWS); +/* externs */ +extern int tws_queue_depth; + + +#define TWS_DRIVER_VERSION_STRING "10.80.00.003" +#define TWS_MAX_NUM_UNITS 65 +#define TWS_MAX_NUM_LUNS 16 +#define TWS_MAX_IRQS 2 +#define TWS_SCSI_INITIATOR_ID 66 +#define TWS_MAX_IO_SIZE 0x20000 /* 128kB */ +#define TWS_SECTOR_SIZE 0x200 +#define TWS_POLL_TIMEOUT 60 +#define TWS_IO_TIMEOUT 60 +#define TWS_IOCTL_TIMEOUT 60 +#define TWS_RESET_TIMEOUT 60 + +#define TWS_PCI_BAR0 0x10 +#define TWS_PCI_BAR1 0x14 +#define TWS_PCI_BAR2 0x1C + +#define TWS_VENDOR_ID 0x13C1 +#define TWS_DEVICE_ID 0x1010 + +#define TWS_INVALID_REQID 0xFFFF + +/* bus tag related */ +#define TWS_ALIGNMENT 4 +#define TWS_IN_MF_ALIGNMENT 16 +#define TWS_OUT_MF_ALIGNMENT 4 + +#define TWS_MAX_32BIT_SG_ELEMENTS 93 /* max 32-bit sg elements */ +#define TWS_MAX_64BIT_SG_ELEMENTS 46 /* max 64-bit sg elements */ + +#define TWS_MAX_QS 4 +#define TWS_MAX_REQS 256 +#define TWS_RESERVED_REQS 4 + +/* Request states */ +#define TWS_REQ_STATE_FREE 0 +#define TWS_REQ_STATE_BUSY 1 +#define TWS_REQ_STATE_TRAN 2 +#define TWS_REQ_STATE_COMPLETE 3 + +/* Request types */ +#define TWS_REQ_TYPE_INTERNAL_CMD 0x0 +#define TWS_REQ_TYPE_AEN_FETCH 0x1 +#define TWS_REQ_TYPE_PASSTHRU 0x2 +#define TWS_REQ_TYPE_GETSET_PARAM 0x3 +#define TWS_REQ_TYPE_SCSI_IO 0x4 + +/* Driver states */ + +enum tws_states { + TWS_INIT=50, + TWS_UNINIT, + TWS_OFFLINE, + TWS_ONLINE, + TWS_RESET, +}; + +/* events */ + +enum tws_events { + TWS_INIT_START=100, + TWS_INIT_COMPLETE, + TWS_UNINIT_START, + TWS_RESET_START, + TWS_RESET_COMPLETE, + TWS_SCAN_FAILURE, +}; + +enum tws_req_flags { + TWS_DIR_UNKNOWN = 0x1, + TWS_DIR_IN = 0x2, + TWS_DIR_OUT = 0x4, + TWS_DIR_NONE = 0x8, +}; + +enum tws_intrs { + TWS_INTx, + TWS_MSI, + TWS_MSIX, +}; + +struct tws_msix_info { + int tbl_res_id; + bus_space_tag_t tbl_tag; + bus_space_handle_t tbl_handle; + struct resource *tbl_res; +}; + +struct tws_ioctl_lock { + u_int32_t lock; + time_t timeout; +}; + + +#define TWS_TRACE_FNAME_LEN 10 +#define TWS_TRACE_FUNC_LEN 15 +#define TWS_TRACE_DESC_LEN 10 +struct tws_trace_rec { + struct timespec ts; + char fname[TWS_TRACE_FNAME_LEN]; + char func[TWS_TRACE_FUNC_LEN]; + int linenum; + char desc[TWS_TRACE_DESC_LEN]; + u_int64_t val1; + u_int64_t val2; +}; + +struct tws_circular_q { + volatile int16_t head; + volatile int16_t tail; + u_int16_t depth; + u_int8_t overflow; + void * q; +}; + + + +struct tws_stats { + u_int64_t reqs_in; + u_int64_t reqs_out; + u_int64_t reqs_errored; + u_int64_t spurios_intrs; + u_int64_t num_intrs; + u_int64_t num_aens; + u_int64_t ioctls; + u_int64_t scsi_ios; +}; + +struct tws_init_connect_info { + u_int16_t working_srl; + u_int16_t working_branch; + u_int16_t working_build; + u_int16_t fw_on_ctlr_srl; + u_int16_t fw_on_ctlr_branch; + u_int16_t fw_on_ctlr_build; + +}; + + +/* ------------ boolean types ------------------- */ + +typedef enum _boolean { false, true } boolean; +enum err { SUCCESS, FAILURE }; + +/* ----------- per instance data ---------------- */ + +/* The softc holds our per-instance data. */ +struct tws_softc { + device_t tws_dev; /* bus device */ + struct cdev *tws_cdev; /* controller device */ + u_int32_t device_id; /* device id */ + u_int32_t subvendor_id; /* device id */ + u_int32_t subdevice_id; /* device id */ + u_int8_t tws_state; /* driver state */ + u_int8_t tws_prev_state; /* driver prev state */ + struct sysctl_ctx_list tws_clist; /* sysctl context */ + struct sysctl_oid *tws_oidp; /* sysctl context */ + struct resource *reg_res; /* register interface window */ + struct resource *mfa_res; /* mfa interface window */ + int reg_res_id; /* register resource id */ + int mfa_res_id; /* register resource id */ + bus_space_handle_t bus_handle; /* bus space handle */ + bus_space_handle_t bus_mfa_handle; /* bus space handle */ + bus_space_tag_t bus_tag; /* bus space tag */ + bus_space_tag_t bus_mfa_tag; /* bus space tag for mfa's */ + u_int64_t mfa_base; /* mfa base address */ + struct resource *irq_res[TWS_MAX_IRQS];/* interrupt resource */ + int irq_res_id[TWS_MAX_IRQS]; /* intr resource id */ + void *intr_handle[TWS_MAX_IRQS]; /* interrupt handle */ + int irqs; /* intrs used */ + struct tws_msix_info msix; /* msix info */ + struct cam_sim *sim; /* sim for this controller */ + struct cam_path *path; /* Ctlr path to CAM */ + struct mtx q_lock; /* queue lock */ + struct mtx sim_lock; /* sim lock */ + struct mtx gen_lock; /* general driver lock */ + struct mtx io_lock; /* IO lock */ + struct tws_ioctl_lock ioctl_lock; /* ioctl lock */ + u_int32_t seq_id; /* Sequence id */ + int chan; /* wait channel */ + struct tws_circular_q aen_q; /* aen q */ + struct tws_circular_q trace_q; /* trace q */ + struct tws_stats stats; /* I/O stats */ + struct tws_init_connect_info cinfo; /* compatibility info */ + boolean is64bit; /* True - 64bit else 32bit */ + u_int8_t intr_type; /* Interrupt type used */ + bus_dma_tag_t parent_tag; /* parent DMA tag */ + bus_dma_tag_t cmd_tag; /* command DMA tag */ + bus_dmamap_t cmd_map; /* command map */ + void *dma_mem; /* pointer to dmable memory */ + u_int64_t dma_mem_phys; /* phy addr */ + bus_dma_tag_t data_tag; /* data DMA tag */ + struct tws_request *reqs; /* pointer to requests */ + struct tws_sense *sense_bufs; /* pointer to sense buffers */ + boolean obfl_q_overrun; /* OBFL overrun flag */ + union ccb *scan_ccb; /* pointer to a ccb */ + struct tws_request *q_head[TWS_MAX_QS]; /* head pointers to q's */ + struct tws_request *q_tail[TWS_MAX_QS]; /* tail pointers to q's */ +}; diff --git a/sys/dev/tws/tws_cam.c b/sys/dev/tws/tws_cam.c new file mode 100644 index 0000000..df78b99 --- /dev/null +++ b/sys/dev/tws/tws_cam.c @@ -0,0 +1,1342 @@ +/* + * Copyright (c) 2010 LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah <manjunath.ranganathaiah@lsi.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <dev/tws/tws.h> +#include <dev/tws/tws_services.h> +#include <dev/tws/tws_hdm.h> +#include <dev/tws/tws_user.h> +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> +#include <cam/cam_periph.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +static int tws_cam_depth=(TWS_MAX_REQS - TWS_RESERVED_REQS); +static char tws_sev_str[5][8]={"","ERROR","WARNING","INFO","DEBUG"}; + +static void tws_action(struct cam_sim *sim, union ccb *ccb); +static void tws_poll(struct cam_sim *sim); +static void tws_scsi_complete(struct tws_request *req); + + + +void tws_unmap_request(struct tws_softc *sc, struct tws_request *req); +int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req); +int tws_bus_scan(struct tws_softc *sc); +int tws_cam_attach(struct tws_softc *sc); +void tws_cam_detach(struct tws_softc *sc); +void tws_reset(void *arg); + +static void tws_reset_cb(void *arg); +static void tws_reinit(void *arg); +static int32_t tws_execute_scsi(struct tws_softc *sc, union ccb *ccb); +static void tws_freeze_simq(struct tws_softc *sc, struct tws_request *req); +static void tws_dmamap_data_load_cbfn(void *arg, bus_dma_segment_t *segs, + int nseg, int error); +static void tws_fill_sg_list(struct tws_softc *sc, void *sgl_src, + void *sgl_dest, u_int16_t num_sgl_entries); +static void tws_err_complete(struct tws_softc *sc, u_int64_t mfa); +static void tws_scsi_err_complete(struct tws_request *req, + struct tws_command_header *hdr); +static void tws_passthru_err_complete(struct tws_request *req, + struct tws_command_header *hdr); + + +void tws_timeout(void *arg); +static void tws_intr_attn_aen(struct tws_softc *sc); +static void tws_intr_attn_error(struct tws_softc *sc); +static void tws_intr_resp(struct tws_softc *sc); +void tws_intr(void *arg); +void tws_cmd_complete(struct tws_request *req); +void tws_aen_complete(struct tws_request *req); +int tws_send_scsi_cmd(struct tws_softc *sc, int cmd); +void tws_getset_param_complete(struct tws_request *req); +int tws_set_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, + u_int32_t param_size, void *data); +int tws_get_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, + u_int32_t param_size, void *data); + + +extern struct tws_request *tws_get_request(struct tws_softc *sc, + u_int16_t type); +extern void *tws_release_request(struct tws_request *req); +extern int tws_submit_command(struct tws_softc *sc, struct tws_request *req); +extern boolean tws_get_response(struct tws_softc *sc, + u_int16_t *req_id, u_int64_t *mfa); +extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ); +extern struct tws_request * tws_q_remove_request(struct tws_softc *sc, + struct tws_request *req, u_int8_t q_type ); +extern void tws_send_event(struct tws_softc *sc, u_int8_t event); + +extern struct tws_sense * +tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa); + +extern void tws_fetch_aen(void *arg); +extern void tws_disable_db_intr(struct tws_softc *sc); +extern void tws_enable_db_intr(struct tws_softc *sc); +extern void tws_passthru_complete(struct tws_request *req); +extern void tws_aen_synctime_with_host(struct tws_softc *sc); +extern void tws_circular_aenq_insert(struct tws_softc *sc, + struct tws_circular_q *cq, struct tws_event_packet *aen); +extern int tws_use_32bit_sgls; +extern boolean tws_ctlr_reset(struct tws_softc *sc); +extern struct tws_request * tws_q_remove_tail(struct tws_softc *sc, + u_int8_t q_type ); +extern void tws_turn_off_interrupts(struct tws_softc *sc); +extern void tws_turn_on_interrupts(struct tws_softc *sc); +extern int tws_init_connect(struct tws_softc *sc, u_int16_t mc); +extern void tws_init_obfl_q(struct tws_softc *sc); +extern uint8_t tws_get_state(struct tws_softc *sc); +extern void tws_assert_soft_reset(struct tws_softc *sc); +extern boolean tws_ctlr_ready(struct tws_softc *sc); +extern u_int16_t tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa); +extern int tws_setup_intr(struct tws_softc *sc, int irqs); +extern int tws_teardown_intr(struct tws_softc *sc); + + + +int +tws_cam_attach(struct tws_softc *sc) +{ + struct cam_devq *devq; + + TWS_TRACE_DEBUG(sc, "entry", 0, sc); + /* Create a device queue for sim */ + + /* + * if the user sets cam depth to less than 1 + * cam may get confused + */ + if ( tws_cam_depth < 1 ) + tws_cam_depth = 1; + if ( tws_cam_depth > (tws_queue_depth - TWS_RESERVED_REQS) ) + tws_cam_depth = tws_queue_depth - TWS_RESERVED_REQS; + + TWS_TRACE_DEBUG(sc, "depths,ctlr,cam", tws_queue_depth, tws_cam_depth); + + if ((devq = cam_simq_alloc(tws_cam_depth)) == NULL) { + tws_log(sc, CAM_SIMQ_ALLOC); + return(ENOMEM); + } + + /* + * Create a SIM entry. Though we can support tws_cam_depth + * simultaneous requests, we claim to be able to handle only + * (tws_cam_depth), so that we always have reserved requests + * packet available to service ioctls and internal commands. + */ + sc->sim = cam_sim_alloc(tws_action, tws_poll, "tws", sc, + device_get_unit(sc->tws_dev), +#if (__FreeBSD_version >= 700000) + &sc->sim_lock, +#endif + tws_cam_depth, 1, devq); + /* 1, 1, devq); */ + if (sc->sim == NULL) { + cam_simq_free(devq); + tws_log(sc, CAM_SIM_ALLOC); + } + /* Register the bus. */ + mtx_lock(&sc->sim_lock); + if (xpt_bus_register(sc->sim, +#if (__FreeBSD_version >= 700000) + sc->tws_dev, +#endif + 0) != CAM_SUCCESS) { + cam_sim_free(sc->sim, TRUE); /* passing true will free the devq */ + sc->sim = NULL; /* so cam_detach will not try to free it */ + mtx_unlock(&sc->sim_lock); + tws_log(sc, TWS_XPT_BUS_REGISTER); + return(ENXIO); + } + if (xpt_create_path(&sc->path, NULL, cam_sim_path(sc->sim), + CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sc->sim)); + /* Passing TRUE to cam_sim_free will free the devq as well. */ + cam_sim_free(sc->sim, TRUE); + tws_log(sc, TWS_XPT_CREATE_PATH); + mtx_unlock(&sc->sim_lock); + return(ENXIO); + } + mtx_unlock(&sc->sim_lock); + + return(0); +} + +void +tws_cam_detach(struct tws_softc *sc) +{ + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + mtx_lock(&sc->sim_lock); + if (sc->path) + xpt_free_path(sc->path); + if (sc->sim) { + xpt_bus_deregister(cam_sim_path(sc->sim)); + cam_sim_free(sc->sim, TRUE); + } + mtx_unlock(&sc->sim_lock); +} + +int +tws_bus_scan(struct tws_softc *sc) +{ + union ccb *ccb; + + TWS_TRACE_DEBUG(sc, "entry", sc, 0); + if (!(sc->sim)) + return(ENXIO); + mtx_assert(&sc->sim_lock, MA_OWNED); + if ((ccb = xpt_alloc_ccb()) == NULL) + return(ENOMEM); + + if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(sc->sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_free_ccb(ccb); + return(EIO); + } + xpt_rescan(ccb); + + return(0); +} + +static void +tws_action(struct cam_sim *sim, union ccb *ccb) +{ + struct tws_softc *sc = (struct tws_softc *)cam_sim_softc(sim); + + + switch( ccb->ccb_h.func_code ) { + case XPT_SCSI_IO: + { + if ( tws_execute_scsi(sc, ccb) ) + TWS_TRACE_DEBUG(sc, "execute scsi failed", 0, 0); + break; + } + case XPT_ABORT: + { + TWS_TRACE_DEBUG(sc, "abort i/o", 0, 0); + ccb->ccb_h.status = CAM_UA_ABORT; + xpt_done(ccb); + break; + } + case XPT_RESET_BUS: + { + TWS_TRACE_DEBUG(sc, "reset bus", sim, ccb); + break; + } + case XPT_SET_TRAN_SETTINGS: + { + TWS_TRACE_DEBUG(sc, "set tran settings", sim, ccb); + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + xpt_done(ccb); + + break; + } + case XPT_GET_TRAN_SETTINGS: + { + TWS_TRACE_DEBUG(sc, "get tran settings", sim, ccb); + +#if (__FreeBSD_version >= 700000 ) + ccb->cts.protocol = PROTO_SCSI; + ccb->cts.protocol_version = SCSI_REV_2; + ccb->cts.transport = XPORT_SPI; + ccb->cts.transport_version = 2; + + ccb->cts.xport_specific.spi.valid = CTS_SPI_VALID_DISC; + ccb->cts.xport_specific.spi.flags = CTS_SPI_FLAGS_DISC_ENB; + ccb->cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ; + ccb->cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB; +#else + ccb->cts.valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID); + ccb->cts.flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); +#endif + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + + break; + } + case XPT_CALC_GEOMETRY: + { + TWS_TRACE_DEBUG(sc, "calc geometry(ccb,block-size)", ccb, + ccb->ccg.block_size); + cam_calc_geometry(&ccb->ccg, 1/* extended */); + xpt_done(ccb); + + break; + } + case XPT_PATH_INQ: + { + TWS_TRACE_DEBUG(sc, "path inquiry", sim, ccb); + ccb->cpi.version_num = 1; + ccb->cpi.hba_inquiry = 0; + ccb->cpi.target_sprt = 0; + ccb->cpi.hba_misc = 0; + ccb->cpi.hba_eng_cnt = 0; + ccb->cpi.max_target = TWS_MAX_NUM_UNITS; + ccb->cpi.max_lun = TWS_MAX_NUM_LUNS - 1; + ccb->cpi.unit_number = cam_sim_unit(sim); + ccb->cpi.bus_id = cam_sim_bus(sim); + ccb->cpi.initiator_id = TWS_SCSI_INITIATOR_ID; + ccb->cpi.base_transfer_speed = 6000000; + strncpy(ccb->cpi.sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(ccb->cpi.hba_vid, "3ware", HBA_IDLEN); + strncpy(ccb->cpi.dev_name, cam_sim_name(sim), DEV_IDLEN); +#if (__FreeBSD_version >= 700000 ) + ccb->cpi.transport = XPORT_SPI; + ccb->cpi.transport_version = 2; + ccb->cpi.protocol = PROTO_SCSI; + ccb->cpi.protocol_version = SCSI_REV_2; + ccb->cpi.maxio = TWS_MAX_IO_SIZE; +#endif + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + + break; + } + default: + TWS_TRACE_DEBUG(sc, "default", sim, ccb); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} + +static void +tws_scsi_complete(struct tws_request *req) +{ + struct tws_softc *sc = req->sc; + + mtx_lock(&sc->q_lock); + tws_q_remove_request(sc, req, TWS_BUSY_Q); + mtx_unlock(&sc->q_lock); + + untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + tws_unmap_request(req->sc, req); + + + req->ccb_ptr->ccb_h.status = CAM_REQ_CMP; + mtx_lock(&sc->sim_lock); + xpt_done(req->ccb_ptr); + mtx_unlock(&sc->sim_lock); + + mtx_lock(&sc->q_lock); + tws_q_insert_tail(sc, req, TWS_FREE_Q); + mtx_unlock(&sc->q_lock); +} + +void +tws_getset_param_complete(struct tws_request *req) +{ + struct tws_softc *sc = req->sc; + + TWS_TRACE_DEBUG(sc, "getset complete", req, req->request_id); + + untimeout(tws_timeout, req, req->thandle); + tws_unmap_request(sc, req); + + free(req->data, M_TWS); + + req->state = TWS_REQ_STATE_FREE; +} + +void +tws_aen_complete(struct tws_request *req) +{ + struct tws_softc *sc = req->sc; + struct tws_command_header *sense; + struct tws_event_packet event; + u_int16_t aen_code=0; + + TWS_TRACE_DEBUG(sc, "aen complete", 0, req->request_id); + + untimeout(tws_timeout, req, req->thandle); + tws_unmap_request(sc, req); + + sense = (struct tws_command_header *)req->data; + + TWS_TRACE_DEBUG(sc,"sense code, key",sense->sense_data[0], + sense->sense_data[2]); + TWS_TRACE_DEBUG(sc,"sense rid, seve",sense->header_desc.request_id, + sense->status_block.res__severity); + TWS_TRACE_DEBUG(sc,"sense srcnum, error",sense->status_block.srcnum, + sense->status_block.error); + TWS_TRACE_DEBUG(sc,"sense shdr, ssense",sense->header_desc.size_header, + sense->header_desc.size_sense); + + aen_code = sense->status_block.error; + + switch ( aen_code ) { + case TWS_AEN_SYNC_TIME_WITH_HOST : + tws_aen_synctime_with_host(sc); + break; + case TWS_AEN_QUEUE_EMPTY : + break; + default : + bzero(&event, sizeof(struct tws_event_packet)); + event.sequence_id = sc->seq_id; + event.time_stamp_sec = (u_int32_t)TWS_LOCAL_TIME; + event.aen_code = sense->status_block.error; + event.severity = sense->status_block.res__severity & 0x7; + event.event_src = TWS_SRC_CTRL_EVENT; + strcpy(event.severity_str, tws_sev_str[event.severity]); + event.retrieved = TWS_AEN_NOT_RETRIEVED; + + bcopy(sense->err_specific_desc, event.parameter_data, + TWS_ERROR_SPECIFIC_DESC_LEN); + event.parameter_data[TWS_ERROR_SPECIFIC_DESC_LEN - 1] = '\0'; + event.parameter_len = (u_int8_t)strlen(event.parameter_data)+1; + + if ( event.parameter_len < TWS_ERROR_SPECIFIC_DESC_LEN ) { + event.parameter_len += ((u_int8_t)strlen(event.parameter_data + + event.parameter_len) + 1); + } + + device_printf(sc->tws_dev, "%s: (0x%02X: 0x%04X): %s: %s\n", + event.severity_str, + event.event_src, + event.aen_code, + event.parameter_data + + (strlen(event.parameter_data) + 1), + event.parameter_data); + + mtx_lock(&sc->gen_lock); + tws_circular_aenq_insert(sc, &sc->aen_q, &event); + sc->seq_id++; + mtx_unlock(&sc->gen_lock); + break; + + } + + free(req->data, M_TWS); + + req->state = TWS_REQ_STATE_FREE; + + if ( aen_code != TWS_AEN_QUEUE_EMPTY ) { + /* timeout(tws_fetch_aen, sc, 1);*/ + sc->stats.num_aens++; + tws_fetch_aen((void *)sc); + } +} + +void +tws_cmd_complete(struct tws_request *req) +{ + struct tws_softc *sc = req->sc; + + untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + tws_unmap_request(sc, req); +} + +static void +tws_err_complete(struct tws_softc *sc, u_int64_t mfa) +{ + struct tws_command_header *hdr; + struct tws_sense *sen; + struct tws_request *req; + u_int16_t req_id; + u_int32_t reg, status; + + if ( !mfa ) { + TWS_TRACE_DEBUG(sc, "null mfa", 0, mfa); + return; + } else { + /* lookup the sense */ + sen = tws_find_sense_from_mfa(sc, mfa); + if ( sen == NULL ) { + TWS_TRACE_DEBUG(sc, "found null req", 0, mfa); + return; + } + hdr = sen->hdr; + TWS_TRACE_DEBUG(sc, "sen, hdr", sen, hdr); + req_id = hdr->header_desc.request_id; + req = &sc->reqs[req_id]; + TWS_TRACE_DEBUG(sc, "req, id", req, req_id); + if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) + TWS_TRACE_DEBUG(sc, "submit failure?", 0, req->error_code); + } + + switch (req->type) { + case TWS_REQ_TYPE_PASSTHRU : + tws_passthru_err_complete(req, hdr); + break; + case TWS_REQ_TYPE_GETSET_PARAM : + tws_getset_param_complete(req); + break; + case TWS_REQ_TYPE_SCSI_IO : + tws_scsi_err_complete(req, hdr); + break; + + } + + mtx_lock(&sc->io_lock); + hdr->header_desc.size_header = 128; + reg = (u_int32_t)( mfa>>32); + tws_write_reg(sc, TWS_I2O0_HOBQPH, reg, 4); + reg = (u_int32_t)(mfa); + tws_write_reg(sc, TWS_I2O0_HOBQPL, reg, 4); + + status = tws_read_reg(sc, TWS_I2O0_STATUS, 4); + if ( status & TWS_BIT13 ) { + device_printf(sc->tws_dev, "OBFL Overrun\n"); + sc->obfl_q_overrun = true; + } + mtx_unlock(&sc->io_lock); +} + +static void +tws_scsi_err_complete(struct tws_request *req, struct tws_command_header *hdr) +{ + u_int8_t *sense_data; + struct tws_softc *sc = req->sc; + union ccb *ccb = req->ccb_ptr; + + TWS_TRACE_DEBUG(sc, "sbe, cmd_status", hdr->status_block.error, + req->cmd_pkt->cmd.pkt_a.status); + if ( hdr->status_block.error == TWS_ERROR_LOGICAL_UNIT_NOT_SUPPORTED || + hdr->status_block.error == TWS_ERROR_UNIT_OFFLINE ) { + + if ( ccb->ccb_h.target_lun ) { + TWS_TRACE_DEBUG(sc, "invalid lun error",0,0); + ccb->ccb_h.status |= CAM_LUN_INVALID; + } else { + TWS_TRACE_DEBUG(sc, "invalid target error",0,0); + ccb->ccb_h.status |= CAM_TID_INVALID; + } + + } else { + TWS_TRACE_DEBUG(sc, "scsi status error",0,0); + ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + if (((ccb->csio.cdb_io.cdb_bytes[0] == 0x1A) && + (hdr->status_block.error == TWS_ERROR_NOT_SUPPORTED))) { + ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; + TWS_TRACE_DEBUG(sc, "page mode not supported",0,0); + } + } + + /* if there were no error simply mark complete error */ + if (ccb->ccb_h.status == 0) + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + + sense_data = (u_int8_t *)&ccb->csio.sense_data; + if (sense_data) { + memcpy(sense_data, hdr->sense_data, TWS_SENSE_DATA_LENGTH ); + ccb->csio.sense_len = TWS_SENSE_DATA_LENGTH; + ccb->ccb_h.status |= CAM_AUTOSNS_VALID; + } + ccb->csio.scsi_status = req->cmd_pkt->cmd.pkt_a.status; + + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + mtx_lock(&sc->sim_lock); + xpt_done(ccb); + mtx_unlock(&sc->sim_lock); + + untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + tws_unmap_request(req->sc, req); + mtx_lock(&sc->q_lock); + tws_q_remove_request(sc, req, TWS_BUSY_Q); + tws_q_insert_tail(sc, req, TWS_FREE_Q); + mtx_unlock(&sc->q_lock); +} + +static void +tws_passthru_err_complete(struct tws_request *req, + struct tws_command_header *hdr) +{ + TWS_TRACE_DEBUG(req->sc, "entry", hdr, req->request_id); + req->error_code = hdr->status_block.error; + memcpy(&(req->cmd_pkt->hdr), hdr, sizeof(struct tws_command_header)); + tws_passthru_complete(req); +} + +static void +tws_drain_busy_queue(struct tws_softc *sc) +{ + struct tws_request *req; + union ccb *ccb; + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + + mtx_lock(&sc->q_lock); + req = tws_q_remove_tail(sc, TWS_BUSY_Q); + mtx_unlock(&sc->q_lock); + while ( req ) { + TWS_TRACE_DEBUG(sc, "moved to TWS_COMPLETE_Q", 0, req->request_id); + untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + + req->error_code = TWS_REQ_RET_RESET; + ccb = (union ccb *)(req->ccb_ptr); + + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + ccb->ccb_h.status |= CAM_REQUEUE_REQ; + ccb->ccb_h.status |= CAM_SCSI_BUS_RESET; + + tws_unmap_request(req->sc, req); + + mtx_lock(&sc->sim_lock); + xpt_done(req->ccb_ptr); + mtx_unlock(&sc->sim_lock); + + mtx_lock(&sc->q_lock); + tws_q_insert_tail(sc, req, TWS_FREE_Q); + req = tws_q_remove_tail(sc, TWS_BUSY_Q); + mtx_unlock(&sc->q_lock); + } +} + + +static void +tws_drain_reserved_reqs(struct tws_softc *sc) +{ + struct tws_request *r; + + r = &sc->reqs[TWS_REQ_TYPE_AEN_FETCH]; + if ( r->state != TWS_REQ_STATE_FREE ) { + TWS_TRACE_DEBUG(sc, "reset aen req", 0, 0); + untimeout(tws_timeout, r, r->thandle); + tws_unmap_request(sc, r); + free(r->data, M_TWS); + r->state = TWS_REQ_STATE_FREE; + r->error_code = TWS_REQ_RET_RESET; + } + + r = &sc->reqs[TWS_REQ_TYPE_PASSTHRU]; + if ( r->state == TWS_REQ_STATE_BUSY ) { + TWS_TRACE_DEBUG(sc, "reset passthru req", 0, 0); + r->error_code = TWS_REQ_RET_RESET; + } + + r = &sc->reqs[TWS_REQ_TYPE_GETSET_PARAM]; + if ( r->state != TWS_REQ_STATE_FREE ) { + TWS_TRACE_DEBUG(sc, "reset setparam req", 0, 0); + untimeout(tws_timeout, r, r->thandle); + tws_unmap_request(sc, r); + free(r->data, M_TWS); + r->state = TWS_REQ_STATE_FREE; + r->error_code = TWS_REQ_RET_RESET; + } +} + +static void +tws_drain_response_queue(struct tws_softc *sc) +{ + u_int16_t req_id; + u_int64_t mfa; + while ( tws_get_response(sc, &req_id, &mfa) ); +} + + +static int32_t +tws_execute_scsi(struct tws_softc *sc, union ccb *ccb) +{ + struct tws_command_packet *cmd_pkt; + struct tws_request *req; + struct ccb_hdr *ccb_h = &(ccb->ccb_h); + struct ccb_scsiio *csio = &(ccb->csio); + int error; + u_int16_t lun; + + mtx_assert(&sc->sim_lock, MA_OWNED); + if (ccb_h->target_id >= TWS_MAX_NUM_UNITS) { + TWS_TRACE_DEBUG(sc, "traget id too big", ccb_h->target_id, ccb_h->target_lun); + ccb_h->status |= CAM_TID_INVALID; + xpt_done(ccb); + return(0); + } + if (ccb_h->target_lun >= TWS_MAX_NUM_LUNS) { + TWS_TRACE_DEBUG(sc, "target lun 2 big", ccb_h->target_id, ccb_h->target_lun); + ccb_h->status |= CAM_LUN_INVALID; + xpt_done(ccb); + return(0); + } + + if(ccb_h->flags & CAM_CDB_PHYS) { + TWS_TRACE_DEBUG(sc, "cdb phy", ccb_h->target_id, ccb_h->target_lun); + ccb_h->status = CAM_REQ_INVALID; + xpt_done(ccb); + return(0); + } + + /* + * We are going to work on this request. Mark it as enqueued (though + * we don't actually queue it...) + */ + ccb_h->status |= CAM_SIM_QUEUED; + + req = tws_get_request(sc, TWS_REQ_TYPE_SCSI_IO); + if ( !req ) { + TWS_TRACE_DEBUG(sc, "no reqs", ccb_h->target_id, ccb_h->target_lun); + ccb_h->status |= CAM_REQUEUE_REQ; + xpt_done(ccb); + return(0); + } + + if((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + if(ccb_h->flags & CAM_DIR_IN) + req->flags |= TWS_DIR_IN; + if(ccb_h->flags & CAM_DIR_OUT) + req->flags |= TWS_DIR_OUT; + } else { + req->flags = TWS_DIR_NONE; /* no data */ + } + + req->type = TWS_REQ_TYPE_SCSI_IO; + req->cb = tws_scsi_complete; + + cmd_pkt = req->cmd_pkt; + /* cmd_pkt->hdr.header_desc.size_header = 128; */ + cmd_pkt->cmd.pkt_a.res__opcode = TWS_FW_CMD_EXECUTE_SCSI; + cmd_pkt->cmd.pkt_a.unit = ccb_h->target_id; + cmd_pkt->cmd.pkt_a.status = 0; + cmd_pkt->cmd.pkt_a.sgl_offset = 16; + + /* lower nibble */ + lun = ccb_h->target_lun & 0XF; + lun = lun << 12; + cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun | req->request_id; + /* upper nibble */ + lun = ccb_h->target_lun & 0XF0; + lun = lun << 8; + cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries = lun; + +#ifdef TWS_DEBUG + if ( csio->cdb_len > 16 ) + TWS_TRACE(sc, "cdb len too big", ccb_h->target_id, csio->cdb_len); +#endif + + if(ccb_h->flags & CAM_CDB_POINTER) + bcopy(csio->cdb_io.cdb_ptr, cmd_pkt->cmd.pkt_a.cdb, csio->cdb_len); + else + bcopy(csio->cdb_io.cdb_bytes, cmd_pkt->cmd.pkt_a.cdb, csio->cdb_len); + + if (!(ccb_h->flags & CAM_DATA_PHYS)) { + /* Virtual data addresses. Need to convert them... */ + if (!(ccb_h->flags & CAM_SCATTER_VALID)) { + if (csio->dxfer_len > TWS_MAX_IO_SIZE) { + TWS_TRACE(sc, "I/O is big", csio->dxfer_len, 0); + tws_release_request(req); + ccb_h->status = CAM_REQ_TOO_BIG; + xpt_done(ccb); + return(0); + } + + req->length = csio->dxfer_len; + if (req->length) { + req->data = csio->data_ptr; + /* there is 1 sgl_entrie */ + /* cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries |= 1; */ + } + } else { + TWS_TRACE_DEBUG(sc, "got sglist", ccb_h->target_id, ccb_h->target_lun); + tws_release_request(req); + ccb_h->status = CAM_REQ_INVALID; + xpt_done(ccb); + return(0); + } + } else { + /* Data addresses are physical. */ + TWS_TRACE_DEBUG(sc, "Phy data addr", ccb_h->target_id, ccb_h->target_lun); + tws_release_request(req); + ccb_h->status = CAM_REQ_INVALID; + ccb_h->status &= ~CAM_SIM_QUEUED; + xpt_done(ccb); + return(0); + } + /* save ccb ptr */ + req->ccb_ptr = ccb; + /* + * tws_map_load_data_callback will fill in the SGL, + * and submit the I/O. + */ + sc->stats.scsi_ios++; + ccb_h->timeout_ch = timeout(tws_timeout, req, (ccb_h->timeout * hz)/1000); + error = tws_map_request(sc, req); + return(error); +} + + +int +tws_send_scsi_cmd(struct tws_softc *sc, int cmd) +{ + struct tws_request *req; + struct tws_command_packet *cmd_pkt; + int error; + + TWS_TRACE_DEBUG(sc, "entry",sc, cmd); + req = tws_get_request(sc, TWS_REQ_TYPE_AEN_FETCH); + + if ( req == NULL ) + return(ENOMEM); + + req->cb = tws_aen_complete; + + cmd_pkt = req->cmd_pkt; + cmd_pkt->cmd.pkt_a.res__opcode = TWS_FW_CMD_EXECUTE_SCSI; + cmd_pkt->cmd.pkt_a.status = 0; + cmd_pkt->cmd.pkt_a.unit = 0; + cmd_pkt->cmd.pkt_a.sgl_offset = 16; + cmd_pkt->cmd.pkt_a.lun_l4__req_id = req->request_id; + + cmd_pkt->cmd.pkt_a.cdb[0] = (u_int8_t)cmd; + cmd_pkt->cmd.pkt_a.cdb[4] = 128; + + req->length = TWS_SECTOR_SIZE; + req->data = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT); + if ( req->data == NULL ) + return(ENOMEM); + bzero(req->data, TWS_SECTOR_SIZE); + req->flags = TWS_DIR_IN; + + req->thandle = timeout(tws_timeout, req, (TWS_IO_TIMEOUT * hz)); + error = tws_map_request(sc, req); + return(error); + +} + +int +tws_set_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, + u_int32_t param_size, void *data) +{ + struct tws_request *req; + struct tws_command_packet *cmd_pkt; + union tws_command_giga *cmd; + struct tws_getset_param *param; + int error; + + req = tws_get_request(sc, TWS_REQ_TYPE_GETSET_PARAM); + if ( req == NULL ) { + TWS_TRACE_DEBUG(sc, "null req", 0, 0); + return(ENOMEM); + } + + req->length = TWS_SECTOR_SIZE; + req->data = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT); + if ( req->data == NULL ) + return(ENOMEM); + bzero(req->data, TWS_SECTOR_SIZE); + param = (struct tws_getset_param *)req->data; + + req->cb = tws_getset_param_complete; + req->flags = TWS_DIR_OUT; + cmd_pkt = req->cmd_pkt; + + cmd = &cmd_pkt->cmd.pkt_g; + cmd->param.sgl_off__opcode = + BUILD_SGL_OFF__OPCODE(2, TWS_FW_CMD_SET_PARAM); + cmd->param.request_id = (u_int8_t)req->request_id; + cmd->param.host_id__unit = 0; + cmd->param.param_count = 1; + cmd->param.size = 2; /* map routine will add sgls */ + + /* Specify which parameter we want to set. */ + param->table_id = (table_id | TWS_9K_PARAM_DESCRIPTOR); + param->parameter_id = (u_int8_t)(param_id); + param->parameter_size_bytes = (u_int16_t)param_size; + memcpy(param->data, data, param_size); + + req->thandle = timeout(tws_timeout, req, (TWS_IOCTL_TIMEOUT * hz)); + error = tws_map_request(sc, req); + return(error); + +} + +int +tws_get_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, + u_int32_t param_size, void *data) +{ + struct tws_request *req; + struct tws_command_packet *cmd_pkt; + union tws_command_giga *cmd; + struct tws_getset_param *param; + u_int16_t reqid; + u_int64_t mfa; + int error = SUCCESS; + + + req = tws_get_request(sc, TWS_REQ_TYPE_GETSET_PARAM); + if ( req == NULL ) { + TWS_TRACE_DEBUG(sc, "null req", 0, 0); + return(FAILURE); + } + + req->length = TWS_SECTOR_SIZE; + req->data = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT); + if ( req->data == NULL ) + return(FAILURE); + bzero(req->data, TWS_SECTOR_SIZE); + param = (struct tws_getset_param *)req->data; + + req->cb = NULL; + req->flags = TWS_DIR_IN; + cmd_pkt = req->cmd_pkt; + + cmd = &cmd_pkt->cmd.pkt_g; + cmd->param.sgl_off__opcode = + BUILD_SGL_OFF__OPCODE(2, TWS_FW_CMD_GET_PARAM); + cmd->param.request_id = (u_int8_t)req->request_id; + cmd->param.host_id__unit = 0; + cmd->param.param_count = 1; + cmd->param.size = 2; /* map routine will add sgls */ + + /* Specify which parameter we want to set. */ + param->table_id = (table_id | TWS_9K_PARAM_DESCRIPTOR); + param->parameter_id = (u_int8_t)(param_id); + param->parameter_size_bytes = (u_int16_t)param_size; + + error = tws_map_request(sc, req); + if (!error) { + reqid = tws_poll4_response(sc, &mfa); + tws_unmap_request(sc, req); + + if ( reqid == TWS_REQ_TYPE_GETSET_PARAM ) { + memcpy(data, param->data, param_size); + } else { + error = FAILURE; + } + } + + free(req->data, M_TWS); + req->state = TWS_REQ_STATE_FREE; + return(error); + +} + +void +tws_unmap_request(struct tws_softc *sc, struct tws_request *req) +{ + if (req->data != NULL) { + if ( req->flags & TWS_DIR_IN ) + bus_dmamap_sync(sc->data_tag, req->dma_map, + BUS_DMASYNC_POSTREAD); + if ( req->flags & TWS_DIR_OUT ) + bus_dmamap_sync(sc->data_tag, req->dma_map, + BUS_DMASYNC_POSTWRITE); + mtx_lock(&sc->io_lock); + bus_dmamap_unload(sc->data_tag, req->dma_map); + mtx_unlock(&sc->io_lock); + } +} + +int32_t +tws_map_request(struct tws_softc *sc, struct tws_request *req) +{ + int32_t error = 0; + + + /* If the command involves data, map that too. */ + if (req->data != NULL) { + int my_flags = ((req->type == TWS_REQ_TYPE_SCSI_IO) ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT); + + /* + * Map the data buffer into bus space and build the SG list. + */ + mtx_lock(&sc->io_lock); + error = bus_dmamap_load(sc->data_tag, req->dma_map, + req->data, req->length, + tws_dmamap_data_load_cbfn, req, + my_flags); + mtx_unlock(&sc->io_lock); + + if (error == EINPROGRESS) { + TWS_TRACE(sc, "in progress", 0, error); + tws_freeze_simq(sc, req); + } + } else { /* no data involved */ + error = tws_submit_command(sc, req); + } + return(error); +} + + +static void +tws_dmamap_data_load_cbfn(void *arg, bus_dma_segment_t *segs, + int nseg, int error) +{ + struct tws_request *req = (struct tws_request *)arg; + struct tws_softc *sc = req->sc; + u_int16_t sgls = nseg; + void *sgl_ptr; + struct tws_cmd_generic *gcmd; + + + if ( error == EFBIG ) { + TWS_TRACE(sc, "not enough data segs", 0, nseg); + req->error_code = error; + req->ccb_ptr->ccb_h.status = CAM_REQ_TOO_BIG; + return; + } + + if ( req->flags & TWS_DIR_IN ) + bus_dmamap_sync(req->sc->data_tag, req->dma_map, + BUS_DMASYNC_PREREAD); + if ( req->flags & TWS_DIR_OUT ) + bus_dmamap_sync(req->sc->data_tag, req->dma_map, + BUS_DMASYNC_PREWRITE); + if ( segs ) { + if ( (req->type == TWS_REQ_TYPE_PASSTHRU && + GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) != + TWS_FW_CMD_EXECUTE_SCSI) || + req->type == TWS_REQ_TYPE_GETSET_PARAM) { + gcmd = &req->cmd_pkt->cmd.pkt_g.generic; + sgl_ptr = (u_int32_t *)(gcmd) + gcmd->size; + gcmd->size += sgls * + ((req->sc->is64bit && !tws_use_32bit_sgls) ? 4 :2 ); + tws_fill_sg_list(req->sc, (void *)segs, sgl_ptr, sgls); + + } else { + tws_fill_sg_list(req->sc, (void *)segs, + (void *)req->cmd_pkt->cmd.pkt_a.sg_list, sgls); + req->cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries |= sgls ; + } + } + + + req->error_code = tws_submit_command(req->sc, req); + +} + + +static void +tws_fill_sg_list(struct tws_softc *sc, void *sgl_src, void *sgl_dest, + u_int16_t num_sgl_entries) +{ + int i; + + if ( sc->is64bit ) { + struct tws_sg_desc64 *sgl_s = (struct tws_sg_desc64 *)sgl_src; + + if ( !tws_use_32bit_sgls ) { + struct tws_sg_desc64 *sgl_d = (struct tws_sg_desc64 *)sgl_dest; + if ( num_sgl_entries > TWS_MAX_64BIT_SG_ELEMENTS ) + TWS_TRACE(sc, "64bit sg overflow", num_sgl_entries, 0); + for (i = 0; i < num_sgl_entries; i++) { + sgl_d[i].address = sgl_s->address; + sgl_d[i].length = sgl_s->length; + sgl_d[i].flag = 0; + sgl_d[i].reserved = 0; + sgl_s = (struct tws_sg_desc64 *) (((u_int8_t *)sgl_s) + + sizeof(bus_dma_segment_t)); + } + } else { + struct tws_sg_desc32 *sgl_d = (struct tws_sg_desc32 *)sgl_dest; + if ( num_sgl_entries > TWS_MAX_32BIT_SG_ELEMENTS ) + TWS_TRACE(sc, "32bit sg overflow", num_sgl_entries, 0); + for (i = 0; i < num_sgl_entries; i++) { + sgl_d[i].address = sgl_s->address; + sgl_d[i].length = sgl_s->length; + sgl_d[i].flag = 0; + sgl_s = (struct tws_sg_desc64 *) (((u_int8_t *)sgl_s) + + sizeof(bus_dma_segment_t)); + } + } + } else { + struct tws_sg_desc32 *sgl_s = (struct tws_sg_desc32 *)sgl_src; + struct tws_sg_desc32 *sgl_d = (struct tws_sg_desc32 *)sgl_dest; + + if ( num_sgl_entries > TWS_MAX_32BIT_SG_ELEMENTS ) + TWS_TRACE(sc, "32bit sg overflow", num_sgl_entries, 0); + + + for (i = 0; i < num_sgl_entries; i++) { + sgl_d[i].address = sgl_s[i].address; + sgl_d[i].length = sgl_s[i].length; + sgl_d[i].flag = 0; + } + } +} + + +void +tws_intr(void *arg) +{ + struct tws_softc *sc = (struct tws_softc *)arg; + u_int32_t histat=0, db=0; + + if (!(sc)) { + device_printf(sc->tws_dev, "null softc!!!\n"); + return; + } + + if ( tws_get_state(sc) == TWS_RESET ) { + return; + } + + if ( tws_get_state(sc) != TWS_ONLINE ) { + return; + } + + sc->stats.num_intrs++; + histat = tws_read_reg(sc, TWS_I2O0_HISTAT, 4); + if ( histat & TWS_BIT2 ) { + TWS_TRACE_DEBUG(sc, "door bell :)", histat, TWS_I2O0_HISTAT); + db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4); + if ( db & TWS_BIT21 ) { + tws_intr_attn_error(sc); + return; + } + if ( db & TWS_BIT18 ) { + tws_intr_attn_aen(sc); + } + } + + if ( histat & TWS_BIT3 ) { + tws_intr_resp(sc); + } +} + +static void +tws_intr_attn_aen(struct tws_softc *sc) +{ + u_int32_t db=0; + + /* maskoff db intrs untill all the aens are fetched */ + /* tws_disable_db_intr(sc); */ + tws_fetch_aen((void *)sc); + tws_write_reg(sc, TWS_I2O0_HOBDBC, TWS_BIT18, 4); + db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4); + +} + +static void +tws_intr_attn_error(struct tws_softc *sc) +{ + u_int32_t db=0; + + TWS_TRACE(sc, "attn error", 0, 0); + tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); + db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4); + device_printf(sc->tws_dev, "Micro controller error.\n"); + tws_reset(sc); +} + +static void +tws_intr_resp(struct tws_softc *sc) +{ + u_int16_t req_id; + u_int64_t mfa; + + while ( tws_get_response(sc, &req_id, &mfa) ) { + sc->stats.reqs_out++; + if ( req_id == TWS_INVALID_REQID ) { + TWS_TRACE_DEBUG(sc, "invalid req_id", mfa, req_id); + sc->stats.reqs_errored++; + tws_err_complete(sc, mfa); + continue; + } + sc->reqs[req_id].cb(&sc->reqs[req_id]); + } + +} + + +static void +tws_poll(struct cam_sim *sim) +{ + struct tws_softc *sc = (struct tws_softc *)cam_sim_softc(sim); + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + tws_intr((void *) sc); +} + +void +tws_timeout(void *arg) +{ + struct tws_request *req = (struct tws_request *)arg; + struct tws_softc *sc = req->sc; + + + if ( req->error_code == TWS_REQ_RET_RESET ) { + return; + } + + mtx_lock(&sc->gen_lock); + if ( req->error_code == TWS_REQ_RET_RESET ) { + mtx_unlock(&sc->gen_lock); + return; + } + + if ( tws_get_state(sc) == TWS_RESET ) { + mtx_unlock(&sc->gen_lock); + return; + } + + tws_teardown_intr(sc); + xpt_freeze_simq(sc->sim, 1); + + tws_send_event(sc, TWS_RESET_START); + + if (req->type == TWS_REQ_TYPE_SCSI_IO) { + device_printf(sc->tws_dev, "I/O Request timed out... Resetting controller\n"); + } else if (req->type == TWS_REQ_TYPE_PASSTHRU) { + device_printf(sc->tws_dev, "IOCTL Request timed out... Resetting controller\n"); + } else { + device_printf(sc->tws_dev, "Internal Request timed out... Resetting controller\n"); + } + + tws_assert_soft_reset(sc); + tws_turn_off_interrupts(sc); + tws_reset_cb( (void*) sc ); + tws_reinit( (void*) sc ); + +// device_printf(sc->tws_dev, "Controller Reset complete!\n"); + tws_send_event(sc, TWS_RESET_COMPLETE); + mtx_unlock(&sc->gen_lock); + + xpt_release_simq(sc->sim, 1); + tws_setup_intr(sc, sc->irqs); +} + +void +tws_reset(void *arg) +{ + struct tws_softc *sc = (struct tws_softc *)arg; + + mtx_lock(&sc->gen_lock); + if ( tws_get_state(sc) == TWS_RESET ) { + mtx_unlock(&sc->gen_lock); + return; + } + + tws_teardown_intr(sc); + xpt_freeze_simq(sc->sim, 1); + + tws_send_event(sc, TWS_RESET_START); + + device_printf(sc->tws_dev, "Resetting controller\n"); + + tws_assert_soft_reset(sc); + tws_turn_off_interrupts(sc); + tws_reset_cb( (void*) sc ); + tws_reinit( (void*) sc ); + +// device_printf(sc->tws_dev, "Controller Reset complete!\n"); + tws_send_event(sc, TWS_RESET_COMPLETE); + mtx_unlock(&sc->gen_lock); + + xpt_release_simq(sc->sim, 1); + tws_setup_intr(sc, sc->irqs); +} + +static void +tws_reset_cb(void *arg) +{ + struct tws_softc *sc = (struct tws_softc *)arg; + time_t endt; + int found = 0; + u_int32_t reg; + + if ( tws_get_state(sc) != TWS_RESET ) { + return; + } + +// device_printf(sc->tws_dev, "Draining Busy Queue\n"); + tws_drain_busy_queue(sc); +// device_printf(sc->tws_dev, "Draining Reserved Reqs\n"); + tws_drain_reserved_reqs(sc); +// device_printf(sc->tws_dev, "Draining Response Queue\n"); + tws_drain_response_queue(sc); + +// device_printf(sc->tws_dev, "Looking for controller ready flag...\n"); + endt = TWS_LOCAL_TIME + TWS_POLL_TIMEOUT; + while ((TWS_LOCAL_TIME <= endt) && (!found)) { + reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4); + if ( reg & TWS_BIT13 ) { + found = 1; +// device_printf(sc->tws_dev, " ... Got it!\n"); + } + } + if ( !found ) + device_printf(sc->tws_dev, " ... Controller ready flag NOT found!\n"); +} + +static void +tws_reinit(void *arg) +{ + struct tws_softc *sc = (struct tws_softc *)arg; + int timeout_val=0; + int try=2; + int done=0; + + +// device_printf(sc->tws_dev, "Waiting for Controller Ready\n"); + while ( !done && try ) { + if ( tws_ctlr_ready(sc) ) { + done = 1; + break; + } else { + timeout_val += 5; + if ( timeout_val >= TWS_RESET_TIMEOUT ) { + timeout_val = 0; + if ( try ) + tws_assert_soft_reset(sc); + try--; + } + mtx_sleep(sc, &sc->gen_lock, 0, "tws_reinit", 5*hz); + } + } + + if (!done) { + device_printf(sc->tws_dev, "FAILED to get Controller Ready!\n"); + return; + } + + sc->obfl_q_overrun = false; +// device_printf(sc->tws_dev, "Sending initConnect\n"); + if ( tws_init_connect(sc, tws_queue_depth) ) { + TWS_TRACE_DEBUG(sc, "initConnect failed", 0, sc->is64bit); + } + tws_init_obfl_q(sc); + + tws_turn_on_interrupts(sc); + + if ( sc->chan ) { + sc->chan = 0; + wakeup_one((void *)&sc->chan); + } +} + + +static void +tws_freeze_simq(struct tws_softc *sc, struct tws_request *req) +{ + /* Only for IO commands */ + if (req->type == TWS_REQ_TYPE_SCSI_IO) { + union ccb *ccb = (union ccb *)(req->ccb_ptr); + + xpt_freeze_simq(sc->sim, 1); + ccb->ccb_h.status |= CAM_RELEASE_SIMQ; + ccb->ccb_h.status |= CAM_REQUEUE_REQ; + } +} + + +TUNABLE_INT("hw.tws.cam_depth", &tws_cam_depth); diff --git a/sys/dev/tws/tws_hdm.c b/sys/dev/tws/tws_hdm.c new file mode 100644 index 0000000..3f6bea0 --- /dev/null +++ b/sys/dev/tws/tws_hdm.c @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +#include <dev/tws/tws.h> +#include <dev/tws/tws_services.h> +#include <dev/tws/tws_hdm.h> + + +int tws_use_32bit_sgls=0; +extern u_int64_t mfa_base; +extern struct tws_request *tws_get_request(struct tws_softc *sc, + u_int16_t type); +extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ); +extern struct tws_request * tws_q_remove_request(struct tws_softc *sc, + struct tws_request *req, u_int8_t q_type ); + +extern void tws_cmd_complete(struct tws_request *req); +extern void tws_print_stats(void *arg); +extern int tws_send_scsi_cmd(struct tws_softc *sc, int cmd); +extern int tws_set_param(struct tws_softc *sc, u_int32_t table_id, + u_int32_t param_id, u_int32_t param_size, void *data); +extern int tws_get_param(struct tws_softc *sc, u_int32_t table_id, + u_int32_t param_id, u_int32_t param_size, void *data); +extern void tws_reset(void *arg); + +int tws_init_connect(struct tws_softc *sc, u_int16_t mc); +int tws_init_ctlr(struct tws_softc *sc); +int tws_submit_command(struct tws_softc *sc, struct tws_request *req); +void tws_nop_cmd(void *arg); +u_int16_t tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa); +boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id, + u_int64_t *mfa); +boolean tws_ctlr_ready(struct tws_softc *sc); +void tws_turn_on_interrupts(struct tws_softc *sc); +void tws_turn_off_interrupts(struct tws_softc *sc); +boolean tws_ctlr_reset(struct tws_softc *sc); +void tws_assert_soft_reset(struct tws_softc *sc); + +int tws_send_generic_cmd(struct tws_softc *sc, u_int8_t opcode); +void tws_fetch_aen(void *arg); +void tws_disable_db_intr(struct tws_softc *sc); +void tws_enable_db_intr(struct tws_softc *sc); +void tws_aen_synctime_with_host(struct tws_softc *sc); +void tws_init_obfl_q(struct tws_softc *sc); +void tws_display_ctlr_info(struct tws_softc *sc); + +int +tws_init_ctlr(struct tws_softc *sc) +{ + u_int64_t reg; + u_int32_t regh, regl; + + TWS_TRACE_DEBUG(sc, "entry", sc, sc->is64bit); + sc->obfl_q_overrun = false; + if ( tws_init_connect(sc, tws_queue_depth) ) + { + TWS_TRACE_DEBUG(sc, "initConnect failed", 0, sc->is64bit); + return(FAILURE); + + } + + + while( 1 ) { + regh = tws_read_reg(sc, TWS_I2O0_IOPOBQPH, 4); + regl = tws_read_reg(sc, TWS_I2O0_IOPOBQPL, 4); + reg = (((u_int64_t)regh) << 32) | regl; + TWS_TRACE_DEBUG(sc, "host outbound clenup",reg, regl); + if ( regh == TWS_FIFO_EMPTY32 ) + break; + } + + tws_init_obfl_q(sc); + tws_display_ctlr_info(sc); + tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); + tws_turn_on_interrupts(sc); + return(SUCCESS); +} + +void +tws_init_obfl_q(struct tws_softc *sc) +{ + int i=0; + u_int64_t paddr; + u_int32_t paddrh, paddrl, status; + + TWS_TRACE_DEBUG(sc, "entry", 0, sc->obfl_q_overrun); + + while ( i < tws_queue_depth ) { + paddr = sc->sense_bufs[i].hdr_pkt_phy; + paddrh = (u_int32_t)( paddr>>32); + paddrl = (u_int32_t) paddr; + tws_write_reg(sc, TWS_I2O0_HOBQPH, paddrh, 4); + tws_write_reg(sc, TWS_I2O0_HOBQPL, paddrl, 4); + + status = tws_read_reg(sc, TWS_I2O0_STATUS, 4); + if ( status & TWS_BIT13 ) { + device_printf(sc->tws_dev, "OBFL Overrun\n"); + sc->obfl_q_overrun = true; + break; + } + i++; + } + + if ( i == tws_queue_depth ) + sc->obfl_q_overrun = false; +} + +int +tws_init_connect(struct tws_softc *sc, u_int16_t mcreadits ) +{ + struct tws_request *req; + struct tws_cmd_init_connect *initc; + u_int16_t reqid; + u_int64_t mfa; + + TWS_TRACE_DEBUG(sc, "entry", 0, mcreadits); +#if 0 + req = tws_get_request(sc, TWS_REQ_TYPE_INTERNAL_CMD); +#else // 0 + req = &sc->reqs[TWS_REQ_TYPE_INTERNAL_CMD]; + bzero(&req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); + req->data = NULL; + req->length = 0; + req->type = TWS_REQ_TYPE_INTERNAL_CMD; + req->flags = TWS_DIR_UNKNOWN; + req->error_code = TWS_REQ_RET_INVALID; + req->cb = NULL; + req->ccb_ptr = NULL; + req->thandle.callout = NULL; + req->next = req->prev = NULL; + req->state = TWS_REQ_STATE_BUSY; +#endif // 0 + + if ( req == NULL ) { + TWS_TRACE_DEBUG(sc, "no requests", 0, 0); +// device_printf(sc->tws_dev, "No requests for initConnect\n"); + return(FAILURE); + } + + tws_swap16(0xbeef); /* just for test */ + tws_swap32(0xdeadbeef); /* just for test */ + tws_swap64(0xdeadbeef); /* just for test */ + initc = &(req->cmd_pkt->cmd.pkt_g.init_connect); + /* req->cmd_pkt->hdr.header_desc.size_header = 128; */ + + initc->res1__opcode = + BUILD_RES__OPCODE(0, TWS_FW_CMD_INIT_CONNECTION); + initc->size = 6; + initc->request_id = req->request_id; + initc->message_credits = mcreadits; + initc->features |= TWS_BIT_EXTEND; + if ( sc->is64bit && !tws_use_32bit_sgls ) + initc->features |= TWS_64BIT_SG_ADDRESSES; + /* assuming set features is always on */ + + initc->size = 6; + initc->fw_srl = sc->cinfo.working_srl = TWS_CURRENT_FW_SRL; + initc->fw_arch_id = 0; + initc->fw_branch = sc->cinfo.working_branch = 0; + initc->fw_build = sc->cinfo.working_build = 0; + + req->error_code = tws_submit_command(sc, req); + reqid = tws_poll4_response(sc, &mfa); + if ( reqid != TWS_INVALID_REQID && reqid == req->request_id ) { + sc->cinfo.fw_on_ctlr_srl = initc->fw_srl; + sc->cinfo.fw_on_ctlr_branch = initc->fw_branch; + sc->cinfo.fw_on_ctlr_build = initc->fw_build; + sc->stats.reqs_out++; + req->state = TWS_REQ_STATE_FREE; + } + else { + /* + * REVISIT::If init connect fails we need to reset the ctlr + * and try again? + */ + TWS_TRACE(sc, "unexpected req_id ", reqid, 0); + TWS_TRACE(sc, "INITCONNECT FAILED", reqid, 0); + return(FAILURE); + } + return(SUCCESS); +} + +void +tws_display_ctlr_info(struct tws_softc *sc) +{ + + uint8_t fw_ver[16], bios_ver[16], ctlr_model[16], num_phys=0; + uint32_t error[4]; + + error[0] = tws_get_param(sc, TWS_PARAM_PHYS_TABLE, + TWS_PARAM_CONTROLLER_PHYS_COUNT, 1, &num_phys); + error[1] = tws_get_param(sc, TWS_PARAM_VERSION_TABLE, + TWS_PARAM_VERSION_FW, 16, fw_ver); + error[2] = tws_get_param(sc, TWS_PARAM_VERSION_TABLE, + TWS_PARAM_VERSION_BIOS, 16, bios_ver); + error[3] = tws_get_param(sc, TWS_PARAM_VERSION_TABLE, + TWS_PARAM_CTLR_MODEL, 16, ctlr_model); + + if ( !error[0] && !error[1] && !error[2] && !error[3] ) { + device_printf( sc->tws_dev, + "Controller details: Model %.16s, %d Phys, Firmware %.16s, BIOS %.16s\n", + ctlr_model, num_phys, fw_ver, bios_ver); + } + +} + +int +tws_send_generic_cmd(struct tws_softc *sc, u_int8_t opcode) +{ + struct tws_request *req; + struct tws_cmd_generic *cmd; + + TWS_TRACE_DEBUG(sc, "entry", sc, opcode); + req = tws_get_request(sc, TWS_REQ_TYPE_INTERNAL_CMD); + + if ( req == NULL ) { + TWS_TRACE_DEBUG(sc, "no requests", 0, 0); + return(FAILURE); + } + + cmd = &(req->cmd_pkt->cmd.pkt_g.generic); + bzero(cmd, sizeof(struct tws_cmd_generic)); + /* req->cmd_pkt->hdr.header_desc.size_header = 128; */ + req->cb = tws_cmd_complete; + + cmd->sgl_off__opcode = BUILD_RES__OPCODE(0, opcode); + cmd->size = 2; + cmd->request_id = req->request_id; + cmd->host_id__unit = 0; + cmd->status = 0; + cmd->flags = 0; + cmd->count = 0; + + req->error_code = tws_submit_command(sc, req); + + return(SUCCESS); + +} + + +int +tws_submit_command(struct tws_softc *sc, struct tws_request *req) +{ + u_int32_t regl, regh; + u_int64_t mfa=0; + + /* + * mfa register read and write must be in order. + * Get the io_lock to protect against simultinous + * passthru calls + */ + mtx_lock(&sc->io_lock); + + if ( sc->obfl_q_overrun ) { + tws_init_obfl_q(sc); + } + +#ifdef TWS_PULL_MODE_ENABLE + regh = (u_int32_t)(req->cmd_pkt_phy >> 32); + /* regh = regh | TWS_MSG_ACC_MASK; */ + mfa = regh; + mfa = mfa << 32; + regl = (u_int32_t)req->cmd_pkt_phy; + regl = regl | TWS_BIT0; + mfa = mfa | regl; +#else + regh = tws_read_reg(sc, TWS_I2O0_HIBQPH, 4); + mfa = regh; + mfa = mfa << 32; + regl = tws_read_reg(sc, TWS_I2O0_HIBQPL, 4); + mfa = mfa | regl; +#endif + + mtx_unlock(&sc->io_lock); + + if ( mfa == TWS_FIFO_EMPTY ) { + TWS_TRACE_DEBUG(sc, "inbound fifo empty", mfa, 0); + + /* + * Generaly we should not get here. + * If the fifo was empty we can't do any thing much + * retry later + */ + return(TWS_REQ_RET_PEND_NOMFA); + + } + +#ifndef TWS_PULL_MODE_ENABLE + for (int i=mfa; i<(sizeof(struct tws_command_packet)+ mfa - + sizeof( struct tws_command_header)); i++) { + + bus_space_write_1(sc->bus_mfa_tag, sc->bus_mfa_handle,i, + ((u_int8_t *)&req->cmd_pkt->cmd)[i-mfa]); + + } +#endif + + if ( req->type == TWS_REQ_TYPE_SCSI_IO ) { + mtx_lock(&sc->q_lock); + tws_q_insert_tail(sc, req, TWS_BUSY_Q); + mtx_unlock(&sc->q_lock); + } + + /* + * mfa register read and write must be in order. + * Get the io_lock to protect against simultinous + * passthru calls + */ + mtx_lock(&sc->io_lock); + + tws_write_reg(sc, TWS_I2O0_HIBQPH, regh, 4); + tws_write_reg(sc, TWS_I2O0_HIBQPL, regl, 4); + + sc->stats.reqs_in++; + mtx_unlock(&sc->io_lock); + + return(TWS_REQ_RET_SUBMIT_SUCCESS); + +} + +/* + * returns true if the respose was available othewise, false. + * In the case of error the arg mfa will contain the address and + * req_id will be TWS_INVALID_REQID + */ +boolean +tws_get_response(struct tws_softc *sc, u_int16_t *req_id, u_int64_t *mfa) +{ + u_int64_t out_mfa=0, val=0; + struct tws_outbound_response out_res; + + *req_id = TWS_INVALID_REQID; + out_mfa = (u_int64_t)tws_read_reg(sc, TWS_I2O0_HOBQPH, 4); + + if ( out_mfa == TWS_FIFO_EMPTY32 ) { + return(false); + + } + out_mfa = out_mfa << 32; + val = tws_read_reg(sc, TWS_I2O0_HOBQPL, 4); + out_mfa = out_mfa | val; + + out_res = *(struct tws_outbound_response *)&out_mfa; + + if ( !out_res.not_mfa ) { + *mfa = out_mfa; + return(true); + } else { + *req_id = out_res.request_id; + } + + return(true); +} + + + + +u_int16_t +tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa) +{ + u_int16_t req_id; + time_t endt; + + endt = TWS_LOCAL_TIME + TWS_POLL_TIMEOUT; + do { + if(tws_get_response(sc, &req_id, mfa)) { + + if ( req_id == TWS_INVALID_REQID ) { + TWS_TRACE_DEBUG(sc, "invalid req_id", 0, req_id); + return(TWS_INVALID_REQID); + } + return(req_id); + } + } while (TWS_LOCAL_TIME <= endt); + TWS_TRACE_DEBUG(sc, "poll timeout", 0, 0); + return(TWS_INVALID_REQID); +} + +boolean +tws_ctlr_ready(struct tws_softc *sc) +{ + u_int32_t reg; + + reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4); + if ( reg & TWS_BIT13 ) + return(true); + else + return(false); +} + +void +tws_turn_on_interrupts(struct tws_softc *sc) +{ + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + /* turn on responce and db interrupt only */ + tws_write_reg(sc, TWS_I2O0_HIMASK, TWS_BIT0, 4); + +} + +void +tws_turn_off_interrupts(struct tws_softc *sc) +{ + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + + tws_write_reg(sc, TWS_I2O0_HIMASK, ~0, 4); + +} + +void +tws_disable_db_intr(struct tws_softc *sc) +{ + u_int32_t reg; + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4); + reg = reg | TWS_BIT2; + tws_write_reg(sc, TWS_I2O0_HIMASK, reg, 4); +} + +void +tws_enable_db_intr(struct tws_softc *sc) +{ + u_int32_t reg; + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4); + reg = reg & ~TWS_BIT2; + tws_write_reg(sc, TWS_I2O0_HIMASK, reg, 4); +} + +boolean +tws_ctlr_reset(struct tws_softc *sc) +{ + + u_int32_t reg; + time_t endt; + /* int i=0; */ + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + + tws_assert_soft_reset(sc); + + do { + reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4); + } while ( reg & TWS_BIT13 ); + + endt = TWS_LOCAL_TIME + TWS_RESET_TIMEOUT; + do { + if(tws_ctlr_ready(sc)) + return(true); + } while (TWS_LOCAL_TIME <= endt); + return(false); + +} + +void +tws_assert_soft_reset(struct tws_softc *sc) +{ + u_int32_t reg; + + reg = tws_read_reg(sc, TWS_I2O0_HIBDB, 4); + TWS_TRACE_DEBUG(sc, "in bound door bell read ", reg, TWS_I2O0_HIBDB); + tws_write_reg(sc, TWS_I2O0_HIBDB, reg | TWS_BIT8, 4); + +} + +void +tws_fetch_aen(void *arg) +{ + struct tws_softc *sc = (struct tws_softc *)arg; + int error = 0; + + TWS_TRACE_DEBUG(sc, "entry", 0, 0); + + if ((error = tws_send_scsi_cmd(sc, 0x03 /* REQUEST_SENSE */))) { + TWS_TRACE_DEBUG(sc, "aen fetch send in progress", 0, 0); + } +} + +void +tws_aen_synctime_with_host(struct tws_softc *sc) +{ + + int error; + long int sync_time; + + TWS_TRACE_DEBUG(sc, "entry", sc, 0); + + sync_time = (TWS_LOCAL_TIME - (3 * 86400)) % 604800; + TWS_TRACE_DEBUG(sc, "sync_time,ts", sync_time, time_second); + TWS_TRACE_DEBUG(sc, "utc_offset", utc_offset(), 0); + error = tws_set_param(sc, TWS_PARAM_TIME_TABLE, TWS_PARAM_TIME_SCHED_TIME, + 4, &sync_time); + if ( error ) + TWS_TRACE_DEBUG(sc, "set param failed", sync_time, error); +} + +TUNABLE_INT("hw.tws.use_32bit_sgls", &tws_use_32bit_sgls); diff --git a/sys/dev/tws/tws_hdm.h b/sys/dev/tws/tws_hdm.h new file mode 100644 index 0000000..5633fdf --- /dev/null +++ b/sys/dev/tws/tws_hdm.h @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +/* bit's defination */ + +#define TWS_BIT0 0x00000001 +#define TWS_BIT1 0x00000002 +#define TWS_BIT2 0x00000004 +#define TWS_BIT3 0x00000008 +#define TWS_BIT4 0x00000010 +#define TWS_BIT5 0x00000020 +#define TWS_BIT6 0x00000040 +#define TWS_BIT7 0x00000080 +#define TWS_BIT8 0x00000100 +#define TWS_BIT9 0x00000200 +#define TWS_BIT10 0x00000400 +#define TWS_BIT11 0x00000800 +#define TWS_BIT12 0x00001000 +#define TWS_BIT13 0x00002000 +#define TWS_BIT14 0x00004000 +#define TWS_BIT15 0x00008000 +#define TWS_BIT16 0x00010000 +#define TWS_BIT17 0x00020000 +#define TWS_BIT18 0x00040000 +#define TWS_BIT19 0x00080000 +#define TWS_BIT20 0x00100000 +#define TWS_BIT21 0x00200000 +#define TWS_BIT22 0x00400000 +#define TWS_BIT23 0x00800000 +#define TWS_BIT24 0x01000000 +#define TWS_BIT25 0x02000000 +#define TWS_BIT26 0x04000000 +#define TWS_BIT27 0x08000000 +#define TWS_BIT28 0x10000000 +#define TWS_BIT29 0x20000000 +#define TWS_BIT30 0x40000000 +#define TWS_BIT31 0x80000000 + +#define TWS_SENSE_DATA_LENGTH 18 +#define TWS_ERROR_SPECIFIC_DESC_LEN 98 + +/* response codes */ +#define TWS_SENSE_SCSI_CURRENT_ERROR 0x70 +#define TWS_SENSE_SCSI_DEFERRED_ERROR 0x71 + +#define TWS_SRC_CTRL_ERROR 3 +#define TWS_SRC_CTRL_EVENT 4 +#define TWS_SRC_FREEBSD_DRIVER 5 +#define TWS_SRC_FREEBSD_OS 8 + + +enum tws_sense_severity { + error = 1, + warning , + info, + debug, +}; + +/* + * Some errors of interest (in cmd_hdr->status_block.error) when a command + * is completed by the firmware with an error. + */ +#define TWS_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x010a +#define TWS_ERROR_NOT_SUPPORTED 0x010D +#define TWS_ERROR_UNIT_OFFLINE 0x0128 +#define TWS_ERROR_MORE_DATA 0x0231 + + +/* AEN codes of interest. */ +#define TWS_AEN_QUEUE_EMPTY 0x00 +#define TWS_AEN_SOFT_RESET 0x01 +#define TWS_AEN_SYNC_TIME_WITH_HOST 0x31 + + +/* AEN severity */ +#define TWS_SEVERITY_ERROR 0x1 +#define TWS_SEVERITY_WARNING 0x2 +#define TWS_SEVERITY_INFO 0x3 +#define TWS_SEVERITY_DEBUG 0x4 + +#define TWS_64BIT_SG_ADDRESSES 0x00000001 +#define TWS_BIT_EXTEND 0x00000002 + +#define TWS_BASE_FW_SRL 24 +#define TWS_BASE_FW_BRANCH 0 +#define TWS_BASE_FW_BUILD 1 +#define TWS_CURRENT_FW_SRL 41 + +#define TWS_CURRENT_FW_BRANCH 8 +#define TWS_CURRENT_FW_BUILD 4 +#define TWS_CURRENT_ARCH_ID 0x000A + + +#define TWS_FIFO_EMPTY 0xFFFFFFFFFFFFFFFFull +#define TWS_FIFO_EMPTY32 0xFFFFFFFFull + + +/* Register offsets from base address. */ +#define TWS_CONTROL_REGISTER_OFFSET 0x0 +#define TWS_STATUS_REGISTER_OFFSET 0x4 +#define TWS_COMMAND_QUEUE_OFFSET 0x8 +#define TWS_RESPONSE_QUEUE_OFFSET 0xC +#define TWS_COMMAND_QUEUE_OFFSET_LOW 0x20 +#define TWS_COMMAND_QUEUE_OFFSET_HIGH 0x24 +#define TWS_LARGE_RESPONSE_QUEUE_OFFSET 0x30 + +/* I2O offsets */ +#define TWS_I2O0_STATUS 0x0 + +#define TWS_I2O0_HIBDB 0x20 + +#define TWS_I2O0_HISTAT 0x30 +#define TWS_I2O0_HIMASK 0x34 + +#define TWS_I2O0_HIBQP 0x40 +#define TWS_I2O0_HOBQP 0x44 + +#define TWS_I2O0_CTL 0x74 + +#define TWS_I2O0_IOBDB 0x9C +#define TWS_I2O0_HOBDBC 0xA0 + +#define TWS_I2O0_SCRPD3 0xBC + +#define TWS_I2O0_HIBQPL 0xC0 /* 64bit inb port low */ +#define TWS_I2O0_HIBQPH 0xC4 /* 64bit inb port high */ +#define TWS_I2O0_HOBQPL 0xC8 /* 64bit out port low */ +#define TWS_I2O0_HOBQPH 0xCC /* 64bit out port high */ + +/* IOP related */ +#define TWS_I2O0_IOPOBQPL 0xD8 /* OBFL */ +#define TWS_I2O0_IOPOBQPH 0xDC /* OBFH */ +#define TWS_I2O0_SRC_ADDRH 0xF8 /* Msg ASA */ + +#define TWS_MSG_ACC_MASK 0x20000000 +#define TWS_32BIT_MASK 0xFFFFFFFF + +/* revisit */ +#define TWS_FW_CMD_NOP 0x0 +#define TWS_FW_CMD_INIT_CONNECTION 0x01 +#define TWS_FW_CMD_EXECUTE_SCSI 0x10 + +#define TWS_FW_CMD_ATA_PASSTHROUGH 0x11 +#define TWS_FW_CMD_GET_PARAM 0x12 +#define TWS_FW_CMD_SET_PARAM 0x13 + + +#define BUILD_SGL_OFF__OPCODE(sgl_off, opcode) \ + ((sgl_off << 5) & 0xE0) | (opcode & 0x1F) /* 3:5 */ + +#define BUILD_RES__OPCODE(res, opcode) \ + ((res << 5) & 0xE0) | (opcode & 0x1F) /* 3:5 */ + +#define GET_OPCODE(sgl_off__opcode) \ + (sgl_off__opcode & 0x1F) /* 3:5 */ + + + +/* end revisit */ + + +/* Table #'s and id's of parameters of interest in firmware's param table. */ +#define TWS_PARAM_VERSION_TABLE 0x0402 +#define TWS_PARAM_VERSION_FW 3 /* firmware version [16] */ +#define TWS_PARAM_VERSION_BIOS 4 /* BIOSs version [16] */ +#define TWS_PARAM_CTLR_MODEL 8 /* Controller model [16] */ + +#define TWS_PARAM_CONTROLLER_TABLE 0x0403 +#define TWS_PARAM_CONTROLLER_PORT_COUNT 3 /* number of ports [1] */ + +#define TWS_PARAM_TIME_TABLE 0x40A +#define TWS_PARAM_TIME_SCHED_TIME 0x3 + +#define TWS_PARAM_PHYS_TABLE 0x0001 +#define TWS_PARAM_CONTROLLER_PHYS_COUNT 2 /* number of phys */ + +#define TWS_9K_PARAM_DESCRIPTOR 0x8000 + + +/* ----------- request ------------- */ + + +#pragma pack(1) + +struct tws_cmd_init_connect { + u_int8_t res1__opcode; /* 3:5 */ + u_int8_t size; + u_int8_t request_id; + u_int8_t res2; + u_int8_t status; + u_int8_t flags; + u_int16_t message_credits; + u_int32_t features; + u_int16_t fw_srl; + u_int16_t fw_arch_id; + u_int16_t fw_branch; + u_int16_t fw_build; + u_int32_t result; +}; + +/* Structure for downloading firmware onto the controller. */ +struct tws_cmd_download_firmware { + u_int8_t sgl_off__opcode;/* 3:5 */ + u_int8_t size; + u_int8_t request_id; + u_int8_t unit; + u_int8_t status; + u_int8_t flags; + u_int16_t param; + u_int8_t sgl[1]; +}; + +/* Structure for hard resetting the controller. */ +struct tws_cmd_reset_firmware { + u_int8_t res1__opcode; /* 3:5 */ + u_int8_t size; + u_int8_t request_id; + u_int8_t unit; + u_int8_t status; + u_int8_t flags; + u_int8_t res2; + u_int8_t param; +}; + + +/* Structure for sending get/set param commands. */ +struct tws_cmd_param { + u_int8_t sgl_off__opcode;/* 3:5 */ + u_int8_t size; + u_int8_t request_id; + u_int8_t host_id__unit; /* 4:4 */ + u_int8_t status; + u_int8_t flags; + u_int16_t param_count; + u_int8_t sgl[1]; +}; + +/* Generic command packet. */ +struct tws_cmd_generic { + u_int8_t sgl_off__opcode;/* 3:5 */ + u_int8_t size; + u_int8_t request_id; + u_int8_t host_id__unit; /* 4:4 */ + u_int8_t status; + u_int8_t flags; + u_int16_t count; /* block cnt, parameter cnt, message credits */ +}; + + + + +/* Command packet header. */ +struct tws_command_header { + u_int8_t sense_data[TWS_SENSE_DATA_LENGTH]; + struct { /* status block - additional sense data */ + u_int16_t srcnum; + u_int8_t reserved; + u_int8_t status; + u_int16_t error; + u_int8_t res__srcid; /* 4:4 */ + u_int8_t res__severity; /* 5:3 */ + } status_block; + u_int8_t err_specific_desc[TWS_ERROR_SPECIFIC_DESC_LEN]; + struct { /* sense buffer descriptor */ + u_int8_t size_header; + u_int16_t request_id; + u_int8_t size_sense; + } header_desc; +}; + +/* Command - 1024 byte size including header (128+24+896)*/ +union tws_command_giga { + struct tws_cmd_init_connect init_connect; + struct tws_cmd_download_firmware download_fw; + struct tws_cmd_reset_firmware reset_fw; + struct tws_cmd_param param; + struct tws_cmd_generic generic; + u_int8_t padding[1024 - sizeof(struct tws_command_header)]; +}; + +/* driver command pkt - 1024 byte size including header(128+24+744+128) */ +/* h/w & f/w supported command size excluding header 768 */ +struct tws_command_apache { + u_int8_t res__opcode; /* 3:5 */ + u_int8_t unit; + u_int16_t lun_l4__req_id; /* 4:12 */ + u_int8_t status; + u_int8_t sgl_offset; /* offset (in bytes) to sg_list, + from the end of sgl_entries */ + u_int16_t lun_h4__sgl_entries; + u_int8_t cdb[16]; + u_int8_t sg_list[744]; /* 768 - 24 */ + u_int8_t padding[128]; /* make it 1024 bytes */ +}; + +struct tws_command_packet { + struct tws_command_header hdr; + union { + union tws_command_giga pkt_g; + struct tws_command_apache pkt_a; + } cmd; +}; + +/* Structure describing payload for get/set param commands. */ +struct tws_getset_param { + u_int16_t table_id; + u_int8_t parameter_id; + u_int8_t reserved; + u_int16_t parameter_size_bytes; + u_int16_t parameter_actual_size_bytes; + u_int8_t data[1]; +}; + +struct tws_outbound_response { + u_int32_t not_mfa :1; /* 1 if the structure is valid else MFA */ + u_int32_t reserved :7; /* reserved bits */ + u_int32_t status :8; /* should be 0 */ + u_int32_t request_id:16; /* request id */ +}; + + +/* Scatter/Gather list entry with 32 bit addresses. */ +struct tws_sg_desc32 { + u_int32_t address; + u_int32_t length :24; + u_int32_t flag :8; +}; + +/* Scatter/Gather list entry with 64 bit addresses. */ +struct tws_sg_desc64 { + u_int64_t address; + u_int64_t length :32; + u_int64_t reserved :24; + u_int64_t flag :8; +}; + +/* + * Packet that describes an AEN/error generated by the controller, + * shared with user + */ +struct tws_event_packet { + u_int32_t sequence_id; + u_int32_t time_stamp_sec; + u_int16_t aen_code; + u_int8_t severity; + u_int8_t retrieved; + u_int8_t repeat_count; + u_int8_t parameter_len; + u_int8_t parameter_data[TWS_ERROR_SPECIFIC_DESC_LEN]; + u_int32_t event_src; + u_int8_t severity_str[20]; +}; + + + +#pragma pack() + +struct tws_sense { + struct tws_command_header *hdr; + u_int64_t hdr_pkt_phy; +}; + +struct tws_request { + struct tws_command_packet *cmd_pkt; /* command pkt */ + u_int64_t cmd_pkt_phy; /* cmd pkt physical address */ + void *data; /* ptr to data being passed to fw */ + u_int32_t length; /* length of data being passed to fw */ + + u_int32_t state; /* request state */ + u_int32_t type; /* request type */ + u_int32_t flags; /* request flags */ + + u_int32_t error_code; /* error during request processing */ + + u_int32_t request_id; /* request id for tracking with fw */ + void (*cb)(struct tws_request *); /* callback func */ + bus_dmamap_t dma_map; /* dma map */ + union ccb *ccb_ptr; /* pointer to ccb */ + struct callout_handle thandle; /* handle to req timeout */ + struct tws_softc *sc; /* pointer back to ctlr softc */ + + struct tws_request *next; /* pointer to next request */ + struct tws_request *prev; /* pointer to prev request */ +}; + + diff --git a/sys/dev/tws/tws_services.c b/sys/dev/tws/tws_services.c new file mode 100644 index 0000000..07200b5 --- /dev/null +++ b/sys/dev/tws/tws_services.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <dev/tws/tws.h> +#include <dev/tws/tws_hdm.h> +#include <dev/tws/tws_services.h> +#include <sys/time.h> + +void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ); +struct tws_request * tws_q_remove_request(struct tws_softc *sc, + struct tws_request *req, u_int8_t q_type ); +struct tws_request *tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type ); +void tws_q_insert_head(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ); +struct tws_request * tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type ); +void tws_print_stats(void *arg); + +struct tws_sense *tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa); + + + +struct error_desc array[] = { + { "Cannot add sysctl tree node", 0x2000, ERROR, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Register window not available", 0x2001, ERROR, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Can't allocate register window", 0x2002, ERROR, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Can't allocate interrupt", 0x2003, ERROR, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Can't set up interrupt", 0x2004, ERROR, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Couldn't intialize CAM", 0x2007, ERROR, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Couldn't create SIM device queue", 0x2100, ENOMEM, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Unable to create SIM entry", 0x2101, ENOMEM, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Unable to register the bus", 0x2102, ENXIO, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Unable to create the path", 0x2103, ENXIO, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Bus scan request to CAM failed", 0x2104, ENXIO, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Unable to intialize the driver", 0x2008, ENXIO, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, + { "Unable to intialize the controller", 0x2009, ENXIO, + "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, +}; + +void +tws_trace(const char *file, const char *fun, int linenum, + struct tws_softc *sc, char *desc, u_int64_t val1, u_int64_t val2) +{ + + + struct tws_trace_rec *rec = (struct tws_trace_rec *)sc->trace_q.q; + volatile u_int16_t head, tail; + char fmt[256]; + + head = sc->trace_q.head; + tail = sc->trace_q.tail; +/* + getnanotime(&rec[tail].ts); +*/ + strncpy(rec[tail].fname, file, TWS_TRACE_FNAME_LEN); + strncpy(rec[tail].func, fun, TWS_TRACE_FUNC_LEN); + rec[tail].linenum = linenum; + strncpy(rec[tail].desc, desc, TWS_TRACE_DESC_LEN); + rec[tail].val1 = val1; + rec[tail].val2 = val2; + + tail = (tail+1) % sc->trace_q.depth; + + if ( head == tail ) { + sc->trace_q.overflow = 1; + sc->trace_q.head = (head+1) % sc->trace_q.depth; + } + sc->trace_q.tail = tail; + +/* + tws_circular_q_insert(sc, &sc->trace_q, + &rec, sizeof(struct tws_trace_rec)); +*/ + if ( sc->is64bit ) + strcpy(fmt, "%05d:%s::%s :%s: 0x%016lx : 0x%016lx \n"); + else + strcpy(fmt, "%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n"); + +/* + printf("%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n", + linenum, file, fun, desc, val1, val2); +*/ + printf(fmt, linenum, file, fun, desc, val1, val2); +} + +void +tws_log(struct tws_softc *sc, int index) +{ + device_printf((sc)->tws_dev, array[index].fmt, + array[index].error_str, + array[index].error_code, + array[index].severity_level, + array[index].desc ); +} + +/* ----------- swap functions ----------- */ + + +u_int16_t +tws_swap16(u_int16_t val) +{ + return((val << 8) | (val >> 8)); +} + +u_int32_t +tws_swap32(u_int32_t val) +{ + return(((val << 24) | ((val << 8) & (0xFF0000)) | + ((val >> 8) & (0xFF00)) | (val >> 24))); +} + + +u_int64_t +tws_swap64(u_int64_t val) +{ + return((((u_int64_t)(tws_swap32(((u_int32_t *)(&(val)))[1]))) << 32) | + ((u_int32_t)(tws_swap32(((u_int32_t *)(&(val)))[0])))); +} + + +/* ----------- reg access ----------- */ + + +void +tws_write_reg(struct tws_softc *sc, int offset, + u_int32_t value, int size) +{ + bus_space_tag_t bus_tag = sc->bus_tag; + bus_space_handle_t bus_handle = sc->bus_handle; + + if (size == 4) + bus_space_write_4(bus_tag, bus_handle, offset, value); + else + if (size == 2) + bus_space_write_2(bus_tag, bus_handle, offset, + (u_int16_t)value); + else + bus_space_write_1(bus_tag, bus_handle, offset, (u_int8_t)value); +} + +u_int32_t +tws_read_reg(struct tws_softc *sc, int offset, int size) +{ + bus_space_tag_t bus_tag = sc->bus_tag; + bus_space_handle_t bus_handle = sc->bus_handle; + + if (size == 4) + return((u_int32_t)bus_space_read_4(bus_tag, bus_handle, offset)); + else if (size == 2) + return((u_int32_t)bus_space_read_2(bus_tag, bus_handle, offset)); + else + return((u_int32_t)bus_space_read_1(bus_tag, bus_handle, offset)); +} + +/* --------------------- Q service --------------------- */ + +/* + * intialize q pointers with null. + */ +void +tws_init_qs(struct tws_softc *sc) +{ + + mtx_lock(&sc->q_lock); + for(int i=0;i<TWS_MAX_QS;i++) { + sc->q_head[i] = NULL; + sc->q_tail[i] = NULL; + } + mtx_unlock(&sc->q_lock); + +} + +/* called with lock held */ +static void +tws_insert2_empty_q(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ) +{ + + mtx_assert(&sc->q_lock, MA_OWNED); + req->next = req->prev = NULL; + sc->q_head[q_type] = sc->q_tail[q_type] = req; + +} + +/* called with lock held */ +void +tws_q_insert_head(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ) +{ + + mtx_assert(&sc->q_lock, MA_OWNED); + if ( sc->q_head[q_type] == NULL ) { + tws_insert2_empty_q(sc, req, q_type); + } else { + req->next = sc->q_head[q_type]; + req->prev = NULL; + sc->q_head[q_type]->prev = req; + sc->q_head[q_type] = req; + } + +} + +/* called with lock held */ +void +tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ) +{ + + mtx_assert(&sc->q_lock, MA_OWNED); + if ( sc->q_tail[q_type] == NULL ) { + tws_insert2_empty_q(sc, req, q_type); + } else { + req->prev = sc->q_tail[q_type]; + req->next = NULL; + sc->q_tail[q_type]->next = req; + sc->q_tail[q_type] = req; + } + +} + +/* called with lock held */ +struct tws_request * +tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type ) +{ + + struct tws_request *r; + + mtx_assert(&sc->q_lock, MA_OWNED); + r = sc->q_head[q_type]; + if ( !r ) + return(NULL); + if ( r->next == NULL && r->prev == NULL ) { + /* last element */ + sc->q_head[q_type] = sc->q_tail[q_type] = NULL; + } else { + sc->q_head[q_type] = r->next; + r->next->prev = NULL; + r->next = NULL; + r->prev = NULL; + } + return(r); +} + +/* called with lock held */ +struct tws_request * +tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type ) +{ + + struct tws_request *r; + + mtx_assert(&sc->q_lock, MA_OWNED); + r = sc->q_tail[q_type]; + if ( !r ) + return(NULL); + if ( r->next == NULL && r->prev == NULL ) { + /* last element */ + sc->q_head[q_type] = sc->q_tail[q_type] = NULL; + } else { + sc->q_tail[q_type] = r->prev; + r->prev->next = NULL; + r->next = NULL; + r->prev = NULL; + } + return(r); +} + +/* returns removed request if successful. return NULL otherwise */ +/* called with lock held */ +struct tws_request * +tws_q_remove_request(struct tws_softc *sc, struct tws_request *req, + u_int8_t q_type ) +{ + + struct tws_request *r; + + mtx_assert(&sc->q_lock, MA_OWNED); + if ( req == NULL ) { + TWS_TRACE_DEBUG(sc, "null req", 0, q_type); + return(NULL); + } + + if ( req == sc->q_head[q_type] ) + return(tws_q_remove_head(sc, q_type)); + if ( req == sc->q_tail[q_type] ) + return(tws_q_remove_tail(sc, q_type)); + + + /* The given node is not at head or tail. + * It's in the middle and there are more than + * 2 elements on the q. + */ + + if ( req->next == NULL || req->prev == NULL ) { + TWS_TRACE_DEBUG(sc, "invalid req", 0, q_type); + return(NULL); + } + +/* debug only */ + r = sc->q_head[q_type]; + while ( r ) { + if ( req == r ) + break; + r = r->next; + } + + if ( !r ) { + TWS_TRACE_DEBUG(sc, "req not in q", 0, req->request_id); + return(NULL); + } +/* debug end */ + + req->prev->next = r->next; + req->next->prev = r->prev; + req->next = NULL; + req->prev = NULL; + return(req); +} + +struct tws_sense * +tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa) +{ + struct tws_sense *s; + int i; + TWS_TRACE_DEBUG(sc, "entry",sc,mfa); + + i = (mfa - sc->dma_mem_phys) / sizeof(struct tws_command_packet); + if ( i>= 0 && i<tws_queue_depth) { + s = &sc->sense_bufs[i]; + if ( mfa == s->hdr_pkt_phy ) + return(s); + } + + TWS_TRACE_DEBUG(sc, "return null",0,mfa); + return(NULL); + +} + +/* --------------------- Q service end --------------------- */ +/* --------------------- misc service start --------------------- */ + + +void +tws_print_stats(void *arg) +{ + + struct tws_softc *sc = (struct tws_softc *)arg; + + TWS_TRACE(sc, "reqs(in, out)", sc->stats.reqs_in, sc->stats.reqs_out); + TWS_TRACE(sc, "reqs(err, intrs)", sc->stats.reqs_errored + , sc->stats.num_intrs); + TWS_TRACE(sc, "reqs(ioctls, scsi)", sc->stats.ioctls + , sc->stats.scsi_ios); + timeout(tws_print_stats, sc, 300*hz); + +} +/* --------------------- misc service end --------------------- */ diff --git a/sys/dev/tws/tws_services.h b/sys/dev/tws/tws_services.h new file mode 100644 index 0000000..643d720 --- /dev/null +++ b/sys/dev/tws/tws_services.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +/* #define TWS_DEBUG on */ + +void tws_trace(const char *file, const char *fun, int linenum, + struct tws_softc *sc, char *desc, u_int64_t val1, u_int64_t val2); +void tws_log(struct tws_softc *sc, int index); +u_int32_t tws_read_reg(struct tws_softc *sc, + int offset, int size); +void tws_write_reg(struct tws_softc *sc, int offset, + u_int32_t value, int size); + +u_int16_t tws_swap16(u_int16_t val); +u_int32_t tws_swap32(u_int32_t val); +u_int64_t tws_swap64(u_int64_t val); + +void tws_init_qs(struct tws_softc *sc); + + + +/* ----------------- trace ----------------- */ + +#define TWS_TRACE_ON on /* Alawys on - use wisely to trace errors */ + +#ifdef TWS_DEBUG + #define TWS_TRACE_DEBUG_ON on +#endif + +#ifdef TWS_TRACE_DEBUG_ON + #define TWS_TRACE_DEBUG(sc, desc, val1, val2) \ + tws_trace(__FILE__, __func__, __LINE__, sc, desc, \ + (u_int64_t)val1, (u_int64_t)val2) +#else + #define TWS_TRACE_DEBUG(sc, desc, val1, val2) +#endif + +#ifdef TWS_TRACE_ON + #define TWS_TRACE(sc, desc, val1, val2) \ + tws_trace(__FILE__, __func__, __LINE__, sc, desc, \ + (u_int64_t)val1, (u_int64_t)val2) +#else + #define TWS_TRACE(sc, desc, val1, val2) +#endif + +/* ---------------- logging ---------------- */ + + +/* ---------------- logging ---------------- */ +enum error_index { + SYSCTL_TREE_NODE_ADD, + PCI_COMMAND_READ, + ALLOC_MEMORY_RES, + ALLOC_IRQ_RES, + SETUP_INTR_RES, + TWS_CAM_ATTACH, + CAM_SIMQ_ALLOC, + CAM_SIM_ALLOC, + TWS_XPT_BUS_REGISTER, + TWS_XPT_CREATE_PATH, + TWS_BUS_SCAN_REQ, + TWS_INIT_FAILURE, + TWS_CTLR_INIT_FAILURE, +}; + +enum severity { + ERROR = 1, + WARNING, + INFO, +#if 0 + DEBUG, +#endif +}; + +struct error_desc { + char desc[256]; + u_int32_t error_code; + int severity_level; + char *fmt; + char *error_str; +}; + +extern struct error_desc array[]; +/* ----------- q services ------------- */ + +#define TWS_FREE_Q 0 +#define TWS_PENDING_Q 1 +#define TWS_BUSY_Q 2 +#define TWS_COMPLETE_Q 3 + +/* req return codes */ +#define TWS_REQ_RET_SUBMIT_SUCCESS 0 +#define TWS_REQ_RET_PEND_NOMFA 1 +#define TWS_REQ_RET_RESET 2 +#define TWS_REQ_RET_INVALID 0xdead + + +/* ------------------------ */ +#if (__FreeBSD_version >= 700000) +#include <sys/clock.h> +#define TWS_LOCAL_TIME (time_second - utc_offset()) +#else +#include <machine/clock.h> +#define TWS_LOCAL_TIME (time_second - (tz_minuteswest * 60) - \ + (wall_cmos_clock ? adjkerntz : 0)) +#endif + diff --git a/sys/dev/tws/tws_user.c b/sys/dev/tws/tws_user.c new file mode 100644 index 0000000..98b8a9b --- /dev/null +++ b/sys/dev/tws/tws_user.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <dev/tws/tws.h> +#include <dev/tws/tws_services.h> +#include <dev/tws/tws_hdm.h> +#include <dev/tws/tws_user.h> + + +int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags, + d_thread_t *proc); +void tws_passthru_complete(struct tws_request *req); +extern void tws_circular_aenq_insert(struct tws_softc *sc, + struct tws_circular_q *cq, struct tws_event_packet *aen); + + +static int tws_passthru(struct tws_softc *sc, void *buf); +static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf); + +extern int tws_bus_scan(struct tws_softc *sc); +extern struct tws_request *tws_get_request(struct tws_softc *sc, + u_int16_t type); +extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req); +extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req); +extern uint8_t tws_get_state(struct tws_softc *sc); +extern void tws_timeout(void *arg); + +int +tws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags, + d_thread_t *proc) +{ + struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1); + int error; + + TWS_TRACE_DEBUG(sc, "entry", sc, cmd); + sc->stats.ioctls++; + switch(cmd) { + case TWS_IOCTL_FIRMWARE_PASS_THROUGH : + error = tws_passthru(sc, (void *)buf); + break; + case TWS_IOCTL_SCAN_BUS : + TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0); + mtx_lock(&sc->sim_lock); + error = tws_bus_scan(sc); + mtx_unlock(&sc->sim_lock); + break; + default : + TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf); + error = tws_ioctl_aen(sc, cmd, (void *)buf); + break; + + } + return(error); +} + +static int +tws_passthru(struct tws_softc *sc, void *buf) +{ + struct tws_request *req; + struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf; + int error; + u_int16_t lun4; + + if ( tws_get_state(sc) != TWS_ONLINE) { + return(EBUSY); + } + + do { + req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU); + if ( !req ) { + sc->chan = 1; + error = tsleep((void *)&sc->chan, 0, + "tws_sleep", TWS_IOCTL_TIMEOUT*hz); + if ( error == EWOULDBLOCK ) { + return(ETIMEDOUT); + } + } else { + break; + } + }while(1); + + req->length = ubuf->driver_pkt.buffer_length; + TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); + if ( req->length ) { + req->data = malloc(req->length, M_TWS, M_WAITOK | M_ZERO); + if ( !req->data ) { + TWS_TRACE_DEBUG(sc, "malloc failed", 0, req->request_id); + req->state = TWS_REQ_STATE_FREE; + ubuf->driver_pkt.os_status = ENOMEM; + if ( sc->chan ) { + sc->chan = 0; + wakeup_one((void *)&sc->chan); + } + return(ENOMEM); + } + bzero(req->data, req->length); + error = copyin(ubuf->pdata, req->data, req->length); + } + req->flags = TWS_DIR_IN | TWS_DIR_OUT; + req->cb = tws_passthru_complete; + + memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, + sizeof(struct tws_command_apache)); + + if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == + TWS_FW_CMD_EXECUTE_SCSI ) { + lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000; + req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id; + } else { + req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id; + + } + + error = tws_map_request(sc, req); + if (error) { + ubuf->driver_pkt.os_status = error; + goto out; + } + +//================================================================================================== + mtx_lock(&sc->gen_lock); + error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz); + mtx_unlock(&sc->gen_lock); + if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) { + TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id); + tws_timeout((void*) req); + } + + if ( req->error_code == TWS_REQ_RET_RESET ) { + error = EBUSY; + req->error_code = EBUSY; + TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id); + } + + tws_unmap_request(sc, req); + + memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache)); + memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); + if ( !error && req->length ) { + error = copyout(req->data, ubuf->pdata, req->length); + } +//================================================================================================== + +out: + free(req->data, M_TWS); + + if ( error ) + TWS_TRACE_DEBUG(sc, "errored", error, 0); + + if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) + ubuf->driver_pkt.os_status = error; + + req->state = TWS_REQ_STATE_FREE; + + if ( sc->chan && (tws_get_state(sc) == TWS_ONLINE) ) { + sc->chan = 0; + wakeup_one((void *)&sc->chan); + } + return(error); +} + +void +tws_passthru_complete(struct tws_request *req) +{ + req->state = TWS_REQ_STATE_COMPLETE; + wakeup_one(req); + +} + +static void +tws_retrive_aen(struct tws_softc *sc, u_long cmd, + struct tws_ioctl_packet *ubuf) +{ + u_int16_t index=0; + struct tws_event_packet eventp, *qp; + + if ( sc->aen_q.head == sc->aen_q.tail ) { + ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; + return; + } + + ubuf->driver_pkt.status = 0; + + /* + * once this flag is set cli will not display alarms + * needs a revisit from tools? + */ + if ( sc->aen_q.overflow ) { + ubuf->driver_pkt.status = TWS_AEN_OVERFLOW; + sc->aen_q.overflow = 0; /* reset */ + } + + qp = (struct tws_event_packet *)sc->aen_q.q; + + switch (cmd) { + case TWS_IOCTL_GET_FIRST_EVENT : + index = sc->aen_q.head; + break; + case TWS_IOCTL_GET_LAST_EVENT : + /* index = tail-1 */ + index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth; + break; + case TWS_IOCTL_GET_NEXT_EVENT : + memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); + index = sc->aen_q.head; + do { + if ( qp[index].sequence_id == + (eventp.sequence_id + 1) ) + break; + index = (index+1) % sc->aen_q.depth; + }while ( index != sc->aen_q.tail ); + if ( index == sc->aen_q.tail ) { + ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; + return; + } + break; + case TWS_IOCTL_GET_PREVIOUS_EVENT : + memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); + index = sc->aen_q.head; + do { + if ( qp[index].sequence_id == + (eventp.sequence_id - 1) ) + break; + index = (index+1) % sc->aen_q.depth; + }while ( index != sc->aen_q.tail ); + if ( index == sc->aen_q.tail ) { + ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; + return; + } + break; + default : + TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd); + ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; + return; + } + + memcpy(ubuf->data_buf, &qp[index], + sizeof(struct tws_event_packet)); + qp[index].retrieved = TWS_AEN_RETRIEVED; + + return; + +} + +static int +tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf) +{ + + struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf; + struct tws_compatibility_packet cpkt; + struct tws_lock_packet lpkt; + time_t ctime; + + mtx_lock(&sc->gen_lock); + ubuf->driver_pkt.status = 0; + switch(cmd) { + case TWS_IOCTL_GET_FIRST_EVENT : + case TWS_IOCTL_GET_LAST_EVENT : + case TWS_IOCTL_GET_NEXT_EVENT : + case TWS_IOCTL_GET_PREVIOUS_EVENT : + tws_retrive_aen(sc,cmd,ubuf); + break; + case TWS_IOCTL_GET_LOCK : + ctime = TWS_LOCAL_TIME; + memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet)); + if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) || + (lpkt.force_flag) || + (ctime >= sc->ioctl_lock.timeout) ) { + sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD; + sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000); + lpkt.time_remaining_msec = lpkt.timeout_msec; + } else { + lpkt.time_remaining_msec = (u_int32_t) + ((sc->ioctl_lock.timeout - ctime) * 1000); + ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD; + + } + break; + case TWS_IOCTL_RELEASE_LOCK : + if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) { + ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD; + } else { + sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE; + ubuf->driver_pkt.status = 0; + } + break; + case TWS_IOCTL_GET_COMPATIBILITY_INFO : + TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd); + + memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING, + sizeof(TWS_DRIVER_VERSION_STRING)); + cpkt.working_srl = sc->cinfo.working_srl; + cpkt.working_branch = sc->cinfo.working_branch; + cpkt.working_build = sc->cinfo.working_build; + cpkt.driver_srl_high = TWS_CURRENT_FW_SRL; + cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH; + cpkt.driver_build_high = TWS_CURRENT_FW_BUILD; + cpkt.driver_srl_low = TWS_BASE_FW_SRL; + cpkt.driver_branch_low = TWS_BASE_FW_BRANCH; + cpkt.driver_build_low = TWS_BASE_FW_BUILD; + cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl; + cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch; + cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build; + ubuf->driver_pkt.status = 0; + int len = sizeof(struct tws_compatibility_packet); + if ( ubuf->driver_pkt.buffer_length < len ) + len = ubuf->driver_pkt.buffer_length; + memcpy(ubuf->data_buf, &cpkt, len); + + break; + default : + TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, + TWS_IOCTL_GET_COMPATIBILITY_INFO); + break; + + } + mtx_unlock(&sc->gen_lock); + return(SUCCESS); + +} + +void +tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, +struct tws_event_packet *aen) +{ + + struct tws_event_packet *q = (struct tws_event_packet *)cq->q; + volatile u_int16_t head, tail; + u_int8_t retr; + mtx_assert(&sc->gen_lock, MA_OWNED); + + head = cq->head; + tail = cq->tail; + retr = q[tail].retrieved; + + memcpy(&q[tail], aen, sizeof(struct tws_event_packet)); + tail = (tail+1) % cq->depth; + + if ( head == tail ) { /* q is full */ + if ( retr != TWS_AEN_RETRIEVED ) + cq->overflow = 1; + cq->head = (head+1) % cq->depth; + } + cq->tail = tail; + +} diff --git a/sys/dev/tws/tws_user.h b/sys/dev/tws/tws_user.h new file mode 100644 index 0000000..5ae29bc --- /dev/null +++ b/sys/dev/tws/tws_user.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010, LSI Corp. + * All rights reserved. + * Author : Manjunath Ranganathaiah + * Support: freebsdraid@lsi.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the <ORGANIZATION> nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define TWS_AEN_NOT_RETRIEVED 0x1 +#define TWS_AEN_RETRIEVED 0x2 + +#define TWS_AEN_NO_EVENTS 0x1003 /* No more events */ +#define TWS_AEN_OVERFLOW 0x1004 /* AEN overflow occurred */ + +#define TWS_IOCTL_LOCK_NOT_HELD 0x1001 /* Not locked */ +#define TWS_IOCTL_LOCK_ALREADY_HELD 0x1002 /* Already locked */ + +#define TWS_IOCTL_LOCK_HELD 0x1 +#define TWS_IOCTL_LOCK_FREE 0x0 + +#pragma pack(1) + +/* Structure used to handle GET/RELEASE LOCK ioctls. */ +struct tws_lock_packet { + u_int32_t timeout_msec; + u_int32_t time_remaining_msec; + u_int32_t force_flag; +}; + +/* Structure used to handle GET COMPATIBILITY INFO ioctl. */ +struct tws_compatibility_packet { + u_int8_t driver_version[32];/* driver version */ + u_int16_t working_srl; /* driver & firmware negotiated srl */ + u_int16_t working_branch; /* branch # of the firmware that the + driver is compatible with */ + u_int16_t working_build; /* build # of the firmware that the + driver is compatible with */ + u_int16_t driver_srl_high;/* highest driver supported srl */ + u_int16_t driver_branch_high;/* highest driver supported branch */ + u_int16_t driver_build_high;/* highest driver supported build */ + u_int16_t driver_srl_low;/* lowest driver supported srl */ + u_int16_t driver_branch_low;/* lowest driver supported branch */ + u_int16_t driver_build_low;/* lowest driver supported build */ + u_int16_t fw_on_ctlr_srl; /* srl of running firmware */ + u_int16_t fw_on_ctlr_branch;/* branch # of running firmware */ + u_int16_t fw_on_ctlr_build;/* build # of running firmware */ +}; + + +/* Driver understandable part of the ioctl packet built by the API. */ +struct tws_driver_packet { + u_int32_t control_code; + u_int32_t status; + u_int32_t unique_id; + u_int32_t sequence_id; + u_int32_t os_status; + u_int32_t buffer_length; +}; + +/* ioctl packet built by the API. */ +struct tws_ioctl_packet { + struct tws_driver_packet driver_pkt; + char padding[488]; + struct tws_command_packet cmd_pkt; + char data_buf[1]; +}; + +#pragma pack() + + +#pragma pack(1) +/* + * We need the structure below to ensure that the first byte of + * data_buf is not overwritten by the kernel, after we return + * from the ioctl call. Note that cmd_pkt has been reduced + * to an array of 1024 bytes even though it's actually 2048 bytes + * in size. This is because, we don't expect requests from user + * land requiring 2048 (273 sg elements) byte cmd pkts. + */ +struct tws_ioctl_no_data_buf { + struct tws_driver_packet driver_pkt; + void *pdata; /* points to data_buf */ + char padding[488 - sizeof(void *)]; + struct tws_command_packet cmd_pkt; +}; + +#pragma pack() + + +#include <sys/ioccom.h> + +#pragma pack(1) + +struct tws_ioctl_with_payload { + struct tws_driver_packet driver_pkt; + char padding[488]; + struct tws_command_packet cmd_pkt; + union { + struct tws_event_packet event_pkt; + struct tws_lock_packet lock_pkt; + struct tws_compatibility_packet compat_pkt; + char data_buf[1]; + } payload; +}; + +#pragma pack() + +/* ioctl cmds */ + +#define TWS_IOCTL_SCAN_BUS \ + _IO('T', 200) +#define TWS_IOCTL_FIRMWARE_PASS_THROUGH \ + _IOWR('T', 202, struct tws_ioctl_no_data_buf) +#define TWS_IOCTL_GET_FIRST_EVENT \ + _IOWR('T', 203, struct tws_ioctl_with_payload) +#define TWS_IOCTL_GET_LAST_EVENT \ + _IOWR('T', 204, struct tws_ioctl_with_payload) +#define TWS_IOCTL_GET_NEXT_EVENT \ + _IOWR('T', 205, struct tws_ioctl_with_payload) +#define TWS_IOCTL_GET_PREVIOUS_EVENT \ + _IOWR('T', 206, struct tws_ioctl_with_payload) +#define TWS_IOCTL_GET_LOCK \ + _IOWR('T', 207, struct tws_ioctl_with_payload) +#define TWS_IOCTL_RELEASE_LOCK \ + _IOWR('T', 208, struct tws_ioctl_with_payload) +#define TWS_IOCTL_GET_COMPATIBILITY_INFO \ + _IOWR('T', 209, struct tws_ioctl_with_payload) + diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 69e8d9b0..2f16ccf 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -147,6 +147,7 @@ device iir # Intel Integrated RAID device ips # IBM (Adaptec) ServeRAID device mly # Mylex AcceleRAID/eXtremeRAID device twa # 3ware 9000 series PATA/SATA RAID +device tws # LSI 3ware 9750 SATA+SAS 6Gb/s RAID controller # RAID controllers device aac # Adaptec FSA RAID diff --git a/sys/modules/Makefile b/sys/modules/Makefile index eec84d35..9b0f24b 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -302,6 +302,7 @@ SUBDIR= ${_3dfx} \ trm \ ${_twa} \ twe \ + tws \ tx \ txp \ uart \ diff --git a/sys/modules/tws/Makefile b/sys/modules/tws/Makefile new file mode 100644 index 0000000..af26579 --- /dev/null +++ b/sys/modules/tws/Makefile @@ -0,0 +1,10 @@ +# Makefile for tws (LSI 3ware 9750 SAS2/SATA-II RAID PCIe) driver +# $FreeBSD$ + +KMOD= tws +.PATH: ${.CURDIR}/../../dev/${KMOD} + +SRCS= tws.c tws_services.c tws_cam.c tws_hdm.c tws_user.c +SRCS+= device_if.h bus_if.h pci_if.h opt_cam.h opt_scsi.h + +.include <bsd.kmod.mk> |