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]
This commit is contained in:
parent
eed142e1e9
commit
db1fda10b4
@ -447,6 +447,7 @@ MAN= aac.4 \
|
||||
tun.4 \
|
||||
twa.4 \
|
||||
twe.4 \
|
||||
tws.4 \
|
||||
tx.4 \
|
||||
txp.4 \
|
||||
u3g.4 \
|
||||
|
118
share/man/man4/tws.4
Normal file
118
share/man/man4/tws.4
Normal file
@ -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.
|
@ -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
|
||||
|
@ -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
|
||||
|
905
sys/dev/tws/tws.c
Normal file
905
sys/dev/tws/tws.c
Normal file
@ -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);
|
265
sys/dev/tws/tws.h
Normal file
265
sys/dev/tws/tws.h
Normal file
@ -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 */
|
||||
};
|
1342
sys/dev/tws/tws_cam.c
Normal file
1342
sys/dev/tws/tws_cam.c
Normal file
File diff suppressed because it is too large
Load Diff
535
sys/dev/tws/tws_hdm.c
Normal file
535
sys/dev/tws/tws_hdm.c
Normal file
@ -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);
|
420
sys/dev/tws/tws_hdm.h
Normal file
420
sys/dev/tws/tws_hdm.h
Normal file
@ -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 */
|
||||
};
|
||||
|
||||
|
401
sys/dev/tws/tws_services.c
Normal file
401
sys/dev/tws/tws_services.c
Normal file
@ -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 --------------------- */
|
141
sys/dev/tws/tws_services.h
Normal file
141
sys/dev/tws/tws_services.h
Normal file
@ -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
|
||||
|
379
sys/dev/tws/tws_user.c
Normal file
379
sys/dev/tws/tws_user.c
Normal file
@ -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;
|
||||
|
||||
}
|
156
sys/dev/tws/tws_user.h
Normal file
156
sys/dev/tws/tws_user.h
Normal file
@ -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)
|
||||
|
@ -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
|
||||
|
@ -302,6 +302,7 @@ SUBDIR= ${_3dfx} \
|
||||
trm \
|
||||
${_twa} \
|
||||
twe \
|
||||
tws \
|
||||
tx \
|
||||
txp \
|
||||
uart \
|
||||
|
10
sys/modules/tws/Makefile
Normal file
10
sys/modules/tws/Makefile
Normal file
@ -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>
|
Loading…
x
Reference in New Issue
Block a user