78601fc5f8
- Add periodic status monitoring routine. Currently just detects lost commands, further functionality pending data from AMI. Add some new commands states; WEDGED (never coming back) and LATE (for when a command that wasmarked as WEDGED comes bacj, - Remove a number of redundant efforts to poll the card for completed commands. This is what interrupt handlers are for. - Limit the maximum number of outstanding I/O transactions. It seems that some controllers report more than they can really handle, and exceding this limit can cause the controller to lock up. - Don't use 'wait' mode for anything where the controller might not be able to generate interrupts. (Keep the 'wait' mode though sa it will become useful when we start taking userspace commands. - Use a similar atomic locking trategy to the Mylex driver to prevent some reentrancy problems. - Correctly calculate the block count for non-whoile-bloch transfers (actually illegal). - Use the dsik device's si_drv1 field instead of b_driver1 in the buf struct to pass the driver identifier arond. - Rewrite amr_start and amr_done() along the lines of the Mylex driver in order to improve robustnes. - Always force the PCI busmaster bit on.
248 lines
8.0 KiB
C
248 lines
8.0 KiB
C
/*-
|
|
* Copyright (c) 1999 Michael Smith
|
|
* 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$
|
|
*/
|
|
|
|
/*
|
|
* We could actually use all 17 segments, but using only 16 means that
|
|
* each scatter/gather map is 128 bytes in size, and thus we don't have to worry about
|
|
* maps crossing page boundaries.
|
|
*/
|
|
#define AMR_NSEG 16
|
|
|
|
#define AMR_CFG_BASE 0x10
|
|
#define AMR_CFG_SIG 0xa0
|
|
#define AMR_SIGNATURE 0x3344
|
|
|
|
#define AMR_MAXCMD 255 /* ident = 0 not allowed */
|
|
#define AMR_LIMITCMD 120 /* maximum count of outstanding commands */
|
|
#define AMR_MAXLD 40
|
|
|
|
#define AMR_BLKSIZE 512
|
|
|
|
struct amr_softc;
|
|
|
|
/*
|
|
* Per-logical-drive datastructure
|
|
*/
|
|
struct amr_logdrive
|
|
{
|
|
u_int32_t al_size;
|
|
int al_state;
|
|
int al_properties;
|
|
|
|
/* synthetic geometry */
|
|
int al_cylinders;
|
|
int al_heads;
|
|
int al_sectors;
|
|
|
|
/* driver */
|
|
device_t al_disk;
|
|
};
|
|
|
|
|
|
/*
|
|
* Per-command control structure.
|
|
*/
|
|
struct amr_command
|
|
{
|
|
TAILQ_ENTRY(amr_command) ac_link;
|
|
|
|
struct amr_softc *ac_sc;
|
|
u_int8_t ac_slot;
|
|
int ac_status;
|
|
#define AMR_STATUS_BUSY 0xffff
|
|
#define AMR_STATUS_WEDGED 0xdead
|
|
#define AMR_STATUS_LATE 0xdeed
|
|
struct amr_mailbox ac_mailbox;
|
|
u_int32_t ac_sgphys;
|
|
int ac_nsgent;
|
|
int ac_flags;
|
|
#define AMR_CMD_DATAIN (1<<0)
|
|
#define AMR_CMD_DATAOUT (1<<1)
|
|
#define AMR_CMD_PRIORITY (1<<2)
|
|
time_t ac_stamp;
|
|
|
|
void *ac_data;
|
|
size_t ac_length;
|
|
bus_dmamap_t ac_dmamap;
|
|
u_int32_t ac_dataphys;
|
|
|
|
void (* ac_complete)(struct amr_command *ac);
|
|
void *ac_private;
|
|
};
|
|
|
|
struct amr_softc
|
|
{
|
|
/* bus attachments */
|
|
device_t amr_dev;
|
|
struct resource *amr_reg; /* control registers */
|
|
bus_space_handle_t amr_bhandle;
|
|
bus_space_tag_t amr_btag;
|
|
bus_dma_tag_t amr_parent_dmat; /* parent DMA tag */
|
|
bus_dma_tag_t amr_buffer_dmat; /* data buffer DMA tag */
|
|
struct resource *amr_irq; /* interrupt */
|
|
void *amr_intr;
|
|
|
|
/* mailbox */
|
|
volatile struct amr_mailbox *amr_mailbox;
|
|
volatile struct amr_mailbox64 *amr_mailbox64;
|
|
u_int32_t amr_mailboxphys;
|
|
bus_dma_tag_t amr_mailbox_dmat;
|
|
bus_dmamap_t amr_mailbox_dmamap;
|
|
|
|
/* scatter/gather lists and their controller-visible mappings */
|
|
struct amr_sgentry *amr_sgtable; /* s/g lists */
|
|
u_int32_t amr_sgbusaddr; /* s/g table base address in bus space */
|
|
bus_dma_tag_t amr_sg_dmat; /* s/g buffer DMA tag */
|
|
bus_dmamap_t amr_sg_dmamap; /* map for s/g buffers */
|
|
|
|
/* controller limits and features */
|
|
int amr_maxio; /* maximum number of I/O transactions */
|
|
int amr_maxdrives; /* max number of logical drives */
|
|
|
|
/* connected logical drives */
|
|
struct amr_logdrive amr_drive[AMR_MAXLD];
|
|
|
|
/* controller status */
|
|
int amr_state;
|
|
#define AMR_STATE_OPEN (1<<0)
|
|
#define AMR_STATE_SUSPEND (1<<1)
|
|
#define AMR_STATE_INTEN (1<<2)
|
|
#define AMR_STATE_SHUTDOWN (1<<3)
|
|
struct callout_handle amr_timeout; /* periodic status check */
|
|
|
|
/* per-controller queues */
|
|
struct buf_queue_head amr_bufq; /* pending I/O */
|
|
int amr_waitbufs;
|
|
struct amr_command *amr_busycmd[AMR_MAXCMD];
|
|
int amr_busycmdcount;
|
|
TAILQ_HEAD(,amr_command) amr_work;
|
|
int amr_workcount;
|
|
TAILQ_HEAD(,amr_command) amr_freecmds;
|
|
|
|
int amr_locks; /* reentrancy avoidance */
|
|
|
|
/* controller type-specific support */
|
|
int amr_type;
|
|
#define AMR_TYPE_STD 0
|
|
#define AMR_TYPE_QUARTZ 1
|
|
void (* amr_submit_command)(struct amr_softc *sc);
|
|
int (* amr_get_work)(struct amr_softc *sc, struct amr_mailbox *mbsave);
|
|
void (* amr_attach_mailbox)(struct amr_softc *sc);
|
|
};
|
|
|
|
/*
|
|
* Simple (stupid) locks.
|
|
*
|
|
* Note that these are designed to avoid reentrancy, not concurrency, and will
|
|
* need to be replaced with something better.
|
|
*/
|
|
#define AMR_LOCK_COMPLETING (1<<0)
|
|
#define AMR_LOCK_STARTING (1<<1)
|
|
|
|
static __inline int
|
|
amr_lock_tas(struct amr_softc *sc, int lock)
|
|
{
|
|
if ((sc)->amr_locks & (lock))
|
|
return(1);
|
|
atomic_set_int(&sc->amr_locks, lock);
|
|
return(0);
|
|
}
|
|
|
|
static __inline void
|
|
amr_lock_clr(struct amr_softc *sc, int lock)
|
|
{
|
|
atomic_clear_int(&sc->amr_locks, lock);
|
|
}
|
|
|
|
/*
|
|
* I/O primitives
|
|
*/
|
|
/* Quartz */
|
|
#define AMR_QPUT_IDB(sc, val) bus_space_write_4(sc->amr_btag, sc->amr_bhandle, AMR_QIDB, val)
|
|
#define AMR_QGET_IDB(sc) bus_space_read_4 (sc->amr_btag, sc->amr_bhandle, AMR_QIDB)
|
|
#define AMR_QPUT_ODB(sc, val) bus_space_write_4(sc->amr_btag, sc->amr_bhandle, AMR_QODB, val)
|
|
#define AMR_QGET_ODB(sc) bus_space_read_4 (sc->amr_btag, sc->amr_bhandle, AMR_QODB)
|
|
|
|
/* Standard */
|
|
#define AMR_SPUT_ISTAT(sc, val) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_SINTR, val)
|
|
#define AMR_SGET_ISTAT(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SINTR)
|
|
#define AMR_SACK_INTERRUPT(sc) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_SCMD, AMR_SCMD_ACKINTR)
|
|
#define AMR_SPOST_COMMAND(sc) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_SCMD, AMR_SCMD_POST)
|
|
#define AMR_SGET_MBSTAT(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_BUSY)
|
|
#define AMR_SENABLE_INTR(sc) \
|
|
bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE, \
|
|
bus_space_read_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE) | AMR_STOGL_IENABLE)
|
|
#define AMR_SDISABLE_INTR(sc) \
|
|
bus_space_write_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE, \
|
|
bus_space_read_1(sc->amr_btag, sc->amr_bhandle, AMR_STOGGLE) & ~AMR_STOGL_IENABLE)
|
|
#define AMR_SBYTE_SET(sc, reg, val) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, reg, val)
|
|
|
|
/*
|
|
* Interface between bus connections and driver core.
|
|
*/
|
|
extern void amr_free(struct amr_softc *sc);
|
|
extern int amr_attach(struct amr_softc *sc);
|
|
extern void amr_startup(struct amr_softc *sc);
|
|
extern void amr_intr(void *data);
|
|
extern int amr_detach(device_t dev);
|
|
extern int amr_shutdown(device_t dev);
|
|
extern int amr_suspend(device_t dev);
|
|
extern int amr_resume(device_t dev);
|
|
extern d_open_t amr_open;
|
|
extern d_close_t amr_close;
|
|
extern d_ioctl_t amr_ioctl;
|
|
|
|
extern devclass_t amr_devclass;
|
|
|
|
/*
|
|
* MegaRAID logical disk driver
|
|
*/
|
|
struct amrd_softc
|
|
{
|
|
device_t amrd_dev;
|
|
dev_t amrd_dev_t;
|
|
struct amr_softc *amrd_controller;
|
|
struct amr_logdrive *amrd_drive;
|
|
struct disk amrd_disk;
|
|
struct devstat amrd_stats;
|
|
struct disklabel amrd_label;
|
|
int amrd_unit;
|
|
int amrd_flags;
|
|
#define AMRD_OPEN (1<<0) /* drive is open (can't shut down) */
|
|
};
|
|
|
|
/*
|
|
* Interface between driver core and disk driver (should be using a bus?)
|
|
*/
|
|
extern int amr_submit_buf(struct amr_softc *sc, struct buf *bp);
|
|
extern int amr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd,
|
|
caddr_t addr, int32_t flag, struct proc *p);
|
|
extern void amrd_intr(void *data);
|
|
|
|
extern void amr_report(void);
|