f0c1dee27f
3ware's 9xxx series controllers. This corresponds to the 9.2 release (for FreeBSD 5.2.1) on the 3ware website. Highlights of this release are: 1. The driver has been re-architected to use a "Common Layer" (all tw_cl* files), which is a consolidation of all OS-independent parts of the driver. The FreeBSD OS specific portions of the driver go into an "OS Layer" (all tw_osl* files). This re-architecture is to achieve better maintainability, consistency of behavior across OS's, and better portability to new OS's (drivers for new OS's can be written by just adding an OS Layer that's specific to the OS, by complying to a "Common Layer Programming Interface" API. 2. The driver takes advantage of multiple processors. 3. The driver has a new firmware image bundled, the new features of which include Online Capacity Expansion and multi-lun support, among others. More details about 3ware's 9.2 release can be found here: http://www.3ware.com/download/Escalade9000Series/9.2/9.2_Release_Notes_Web.pdf Since the Common Layer is used across OS's, the FreeBSD specific include path for header files (/sys/dev/twa) is not part of the #include pre-processor directive in any of the source files. For being able to integrate twa into the kernel despite this, Makefile.<arch> has been changed to add the include path to CFLAGS. Reviewed by: scottl
317 lines
9.6 KiB
C
317 lines
9.6 KiB
C
/*
|
|
* Copyright (c) 2004-05 Applied Micro Circuits Corporation.
|
|
* Copyright (c) 2004-05 Vinod Kashyap.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* AMCC'S 3ware driver for 9000 series storage controllers.
|
|
*
|
|
* Author: Vinod Kashyap
|
|
*/
|
|
|
|
|
|
|
|
#ifndef TW_OSL_H
|
|
|
|
#define TW_OSL_H
|
|
|
|
|
|
/*
|
|
* OS Layer internal macros, structures and functions.
|
|
*/
|
|
|
|
|
|
#define TW_OSLI_DEVICE_NAME "3ware 9000 series Storage Controller"
|
|
|
|
#define TW_OSLI_MALLOC_CLASS M_TWA
|
|
#define TW_OSLI_MAX_NUM_IOS TW_CL_MAX_SIMULTANEOUS_REQUESTS
|
|
#define TW_OSLI_MAX_NUM_AENS 0x100
|
|
|
|
/* Possible values of req->state. */
|
|
#define TW_OSLI_REQ_STATE_INIT 0x0 /* being initialized */
|
|
#define TW_OSLI_REQ_STATE_BUSY 0x1 /* submitted to CL */
|
|
#define TW_OSLI_REQ_STATE_PENDING 0x2 /* in pending queue */
|
|
#define TW_OSLI_REQ_STATE_COMPLETE 0x3 /* completed by CL */
|
|
|
|
/* Possible values of req->flags. */
|
|
#define TW_OSLI_REQ_FLAGS_DATA_IN (1<<0) /* read request */
|
|
#define TW_OSLI_REQ_FLAGS_DATA_OUT (1<<1) /* write request */
|
|
#define TW_OSLI_REQ_FLAGS_DATA_COPY_NEEDED (1<<2)/* data in ccb is misaligned,
|
|
have to copy to/from private buffer */
|
|
#define TW_OSLI_REQ_FLAGS_MAPPED (1<<3) /* request has been mapped */
|
|
#define TW_OSLI_REQ_FLAGS_IN_PROGRESS (1<<4) /* bus_dmamap_load returned
|
|
EINPROGRESS */
|
|
#define TW_OSLI_REQ_FLAGS_PASSTHRU (1<<5) /* pass through request */
|
|
#define TW_OSLI_REQ_FLAGS_SLEEPING (1<<6) /* owner sleeping on this cmd */
|
|
|
|
/* Possible values of sc->state. */
|
|
#define TW_OSLI_CTLR_STATE_OPEN (1<<0) /* control device is open */
|
|
#define TW_OSLI_CTLR_STATE_SIMQ_FROZEN (1<<1) /* simq frozen */
|
|
|
|
|
|
#ifdef TW_OSL_DEBUG
|
|
struct tw_osli_q_stats {
|
|
TW_UINT32 cur_len; /* current # of items in q */
|
|
TW_UINT32 max_len; /* max value reached by q_length */
|
|
};
|
|
#endif /* TW_OSL_DEBUG */
|
|
|
|
|
|
/* Queues of OSL internal request context packets. */
|
|
#define TW_OSLI_FREE_Q 0 /* free q */
|
|
#define TW_OSLI_BUSY_Q 1 /* q of reqs submitted to CL */
|
|
#define TW_OSLI_Q_COUNT 2 /* total number of queues */
|
|
|
|
/* Driver's request packet. */
|
|
struct tw_osli_req_context {
|
|
struct tw_cl_req_handle req_handle;/* tag to track req b/w OSL & CL */
|
|
struct twa_softc *ctlr; /* ptr to OSL's controller context */
|
|
TW_VOID *data; /* ptr to data being passed to CL */
|
|
TW_UINT32 length; /* length of buf being passed to CL */
|
|
|
|
/*
|
|
* ptr to, and length of data passed to us from above, in case a buffer
|
|
* copy was done due to non-compliance to alignment requirements
|
|
*/
|
|
TW_VOID *real_data;
|
|
TW_UINT32 real_length;
|
|
|
|
TW_UINT32 state; /* request state */
|
|
TW_UINT32 flags; /* request flags */
|
|
|
|
/* error encountered before request submission to CL */
|
|
TW_UINT32 error_code;
|
|
|
|
/* ptr to orig req for use during callback */
|
|
TW_VOID *orig_req;
|
|
|
|
struct tw_cl_link link; /* to link this request in a list */
|
|
bus_dmamap_t dma_map;/* DMA map for data */
|
|
struct tw_cl_req_packet req_pkt;/* req pkt understood by CL */
|
|
};
|
|
|
|
|
|
/* Per-controller structure. */
|
|
struct twa_softc {
|
|
struct tw_cl_ctlr_handle ctlr_handle;
|
|
struct tw_osli_req_context *req_ctxt_buf;
|
|
|
|
/* Controller state. */
|
|
TW_UINT32 state;
|
|
TW_UINT32 flags;
|
|
|
|
TW_UINT32 alignment;
|
|
TW_UINT32 sg_size_factor;
|
|
|
|
TW_VOID *non_dma_mem;
|
|
TW_VOID *dma_mem;
|
|
TW_UINT64 dma_mem_phys;
|
|
#ifdef TW_OSL_FLASH_FIRMWARE
|
|
TW_VOID *flash_dma_mem;
|
|
TW_UINT64 flash_dma_mem_phys;
|
|
#endif /* TW_OSL_FLASH_FIRMWARE */
|
|
|
|
|
|
/* Request queues and arrays. */
|
|
struct tw_cl_link req_q_head[TW_OSLI_Q_COUNT];
|
|
|
|
struct task deferred_intr_callback;/* taskqueue function */
|
|
struct mtx io_lock_handle;/* general purpose lock */
|
|
struct mtx *io_lock;/* ptr to general purpose lock */
|
|
struct mtx q_lock_handle; /* queue manipulation lock */
|
|
struct mtx *q_lock;/* ptr to queue manipulation lock */
|
|
|
|
#ifdef TW_OSL_DEBUG
|
|
struct tw_osli_q_stats q_stats[TW_OSLI_Q_COUNT];/* queue statistics */
|
|
#endif /* TW_OSL_DEBUG */
|
|
|
|
device_t bus_dev; /* bus device */
|
|
struct cdev *ctrl_dev; /* control device */
|
|
struct resource *reg_res; /* register interface window */
|
|
TW_INT32 reg_res_id; /* register resource id */
|
|
bus_space_handle_t bus_handle; /* bus space handle */
|
|
bus_space_tag_t bus_tag; /* bus space tag */
|
|
bus_dma_tag_t parent_tag; /* parent DMA tag */
|
|
bus_dma_tag_t cmd_tag; /* DMA tag for CL's DMA'able mem */
|
|
bus_dma_tag_t dma_tag; /* data buffer DMA tag */
|
|
bus_dma_tag_t ioctl_tag; /* ioctl data buffer DMA tag */
|
|
bus_dmamap_t cmd_map; /* DMA map for CL's DMA'able mem */
|
|
bus_dmamap_t ioctl_map; /* DMA map for ioctl data buffers */
|
|
#ifdef TW_OSL_FLASH_FIRMWARE
|
|
bus_dma_tag_t flash_tag;/* DMA tag for CL's fw flash mem */
|
|
bus_dmamap_t flash_map;/* DMA map for CL's fw flash mem */
|
|
#endif /* TW_OSL_FLASH_FIRMWARE */
|
|
struct resource *irq_res; /* interrupt resource */
|
|
TW_INT32 irq_res_id; /* register resource id */
|
|
TW_VOID *intr_handle; /* interrupt handle */
|
|
|
|
struct sysctl_ctx_list sysctl_ctxt; /* sysctl context */
|
|
struct sysctl_oid *sysctl_tree; /* sysctl oid */
|
|
|
|
struct cam_sim *sim; /* sim for this controller */
|
|
struct cam_path *path; /* peripheral, path, tgt, lun
|
|
associated with this controller */
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Queue primitives.
|
|
*/
|
|
|
|
#ifdef TW_OSL_DEBUG
|
|
|
|
#define TW_OSLI_Q_INIT(sc, q_type) do { \
|
|
(sc)->q_stats[q_type].cur_len = 0; \
|
|
(sc)->q_stats[q_type].max_len = 0; \
|
|
} while(0)
|
|
|
|
|
|
#define TW_OSLI_Q_INSERT(sc, q_type) do { \
|
|
struct tw_osli_q_stats *q_stats = &((sc)->q_stats[q_type]); \
|
|
\
|
|
if (++(q_stats->cur_len) > q_stats->max_len) \
|
|
q_stats->max_len = q_stats->cur_len; \
|
|
} while(0)
|
|
|
|
|
|
#define TW_OSLI_Q_REMOVE(sc, q_type) \
|
|
(sc)->q_stats[q_type].cur_len--
|
|
|
|
|
|
#else /* TW_OSL_DEBUG */
|
|
|
|
#define TW_OSLI_Q_INIT(sc, q_index)
|
|
#define TW_OSLI_Q_INSERT(sc, q_index)
|
|
#define TW_OSLI_Q_REMOVE(sc, q_index)
|
|
|
|
#endif /* TW_OSL_DEBUG */
|
|
|
|
|
|
|
|
/* Initialize a queue of requests. */
|
|
static __inline TW_VOID
|
|
tw_osli_req_q_init(struct twa_softc *sc, TW_UINT8 q_type)
|
|
{
|
|
TW_CL_Q_INIT(&(sc->req_q_head[q_type]));
|
|
TW_OSLI_Q_INIT(sc, q_type);
|
|
}
|
|
|
|
|
|
|
|
/* Insert the given request at the head of the given queue (q_type). */
|
|
static __inline TW_VOID
|
|
tw_osli_req_q_insert_head(struct tw_osli_req_context *req, TW_UINT8 q_type)
|
|
{
|
|
mtx_lock_spin(req->ctlr->q_lock);
|
|
TW_CL_Q_INSERT_HEAD(&(req->ctlr->req_q_head[q_type]), &(req->link));
|
|
TW_OSLI_Q_INSERT(req->ctlr, q_type);
|
|
mtx_unlock_spin(req->ctlr->q_lock);
|
|
}
|
|
|
|
|
|
|
|
/* Insert the given request at the tail of the given queue (q_type). */
|
|
static __inline TW_VOID
|
|
tw_osli_req_q_insert_tail(struct tw_osli_req_context *req, TW_UINT8 q_type)
|
|
{
|
|
mtx_lock_spin(req->ctlr->q_lock);
|
|
TW_CL_Q_INSERT_TAIL(&(req->ctlr->req_q_head[q_type]), &(req->link));
|
|
TW_OSLI_Q_INSERT(req->ctlr, q_type);
|
|
mtx_unlock_spin(req->ctlr->q_lock);
|
|
}
|
|
|
|
|
|
|
|
/* Remove and return the request at the head of the given queue (q_type). */
|
|
static __inline struct tw_osli_req_context *
|
|
tw_osli_req_q_remove_head(struct twa_softc *sc, TW_UINT8 q_type)
|
|
{
|
|
struct tw_osli_req_context *req = NULL;
|
|
struct tw_cl_link *link;
|
|
|
|
mtx_lock_spin(sc->q_lock);
|
|
if ((link = TW_CL_Q_FIRST_ITEM(&(sc->req_q_head[q_type]))) !=
|
|
TW_CL_NULL) {
|
|
req = TW_CL_STRUCT_HEAD(link,
|
|
struct tw_osli_req_context, link);
|
|
TW_CL_Q_REMOVE_ITEM(&(sc->req_q_head[q_type]), &(req->link));
|
|
TW_OSLI_Q_REMOVE(sc, q_type);
|
|
}
|
|
mtx_unlock_spin(sc->q_lock);
|
|
return(req);
|
|
}
|
|
|
|
|
|
|
|
/* Remove the given request from the given queue (q_type). */
|
|
static __inline TW_VOID
|
|
tw_osli_req_q_remove_item(struct tw_osli_req_context *req, TW_UINT8 q_type)
|
|
{
|
|
mtx_lock_spin(req->ctlr->q_lock);
|
|
TW_CL_Q_REMOVE_ITEM(&(req->ctlr->req_q_head[q_type]), &(req->link));
|
|
TW_OSLI_Q_REMOVE(req->ctlr, q_type);
|
|
mtx_unlock_spin(req->ctlr->q_lock);
|
|
}
|
|
|
|
|
|
|
|
#ifdef TW_OSL_DEBUG
|
|
|
|
extern TW_INT32 TW_DEBUG_LEVEL_FOR_OSL;
|
|
|
|
#define tw_osli_dbg_dprintf(dbg_level, sc, fmt, args...) \
|
|
if (dbg_level <= TW_DEBUG_LEVEL_FOR_OSL) \
|
|
device_printf(sc->bus_dev, "%s: " fmt "\n", \
|
|
__func__, ##args)
|
|
|
|
|
|
#define tw_osli_dbg_printf(dbg_level, fmt, args...) \
|
|
if (dbg_level <= TW_DEBUG_LEVEL_FOR_OSL) \
|
|
printf("%s: " fmt "\n", __func__, ##args)
|
|
|
|
#else /* TW_OSL_DEBUG */
|
|
|
|
#define tw_osli_dbg_dprintf(dbg_level, sc, fmt, args...)
|
|
#define tw_osli_dbg_printf(dbg_level, fmt, args...)
|
|
|
|
#endif /* TW_OSL_DEBUG */
|
|
|
|
|
|
/* For regular printing. */
|
|
#define twa_printf(sc, fmt, args...) \
|
|
device_printf(((struct twa_softc *)(sc))->bus_dev, fmt, ##args)
|
|
|
|
/* For printing in the "consistent error reporting" format. */
|
|
#define tw_osli_printf(sc, err_specific_desc, args...) \
|
|
device_printf((sc)->bus_dev, \
|
|
"%s: (0x%02X: 0x%04X): %s: " err_specific_desc "\n", ##args)
|
|
|
|
|
|
|
|
#endif /* TW_OSL_H */
|