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:
Xin LI 2011-10-04 21:40:25 +00:00
parent eed142e1e9
commit db1fda10b4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=226026
16 changed files with 4681 additions and 0 deletions

View file

@ -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
View 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.

View file

@ -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

View file

@ -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
View 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
View 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

File diff suppressed because it is too large Load diff

535
sys/dev/tws/tws_hdm.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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)

View file

@ -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

View file

@ -302,6 +302,7 @@ SUBDIR= ${_3dfx} \
trm \
${_twa} \
twe \
tws \
tx \
txp \
uart \

10
sys/modules/tws/Makefile Normal file
View 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>