Major update to the AMI MegaRAID driver.

- New support for 40LD firmware found in Series 475 and 471 adapters.
 - Better support for 8LD firmware adapters
 - Ioctl passthrough interface for userland utilities.
 - Improved error handling and queueing.
 - Several bugfixes (including the 'still open' shutdown bug and
   closing some small race conditions).
 - Zone-style command allocator, reducing memory wasted under heavy
   load conditions.
 - CAM interface (disabled and not fully working) for SCSI passthrough
   access to non-disk devices

Thanks to AMI for supplying a pile of new adapters and various other
help in making this happen.
This commit is contained in:
Mike Smith 2000-08-30 07:52:50 +00:00
parent 429a82acc6
commit 9f1776230d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=65245
10 changed files with 2534 additions and 1153 deletions

File diff suppressed because it is too large Load diff

452
sys/dev/amr/amr_cam.c Normal file
View file

@ -0,0 +1,452 @@
/*-
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* 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$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <dev/amr/amr_compat.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/devicestat.h>
#include <sys/disk.h>
#include <sys/stat.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_debug.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <dev/amr/amrreg.h>
#include <dev/amr/amrvar.h>
static void amr_cam_action(struct cam_sim *sim, union ccb *ccb);
static void amr_cam_poll(struct cam_sim *sim);
static void amr_cam_complete(struct amr_command *ac);
/********************************************************************************
* Enqueue/dequeue functions
*/
static __inline void
amr_enqueue_ccb(struct amr_softc *sc, union ccb *ccb)
{
int s;
s = splbio();
TAILQ_INSERT_TAIL(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
splx(s);
}
static __inline void
amr_requeue_ccb(struct amr_softc *sc, union ccb *ccb)
{
int s;
s = splbio();
TAILQ_INSERT_HEAD(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
splx(s);
}
static __inline union ccb *
amr_dequeue_ccb(struct amr_softc *sc)
{
union ccb *ccb;
int s;
s = splbio();
if ((ccb = (union ccb *)TAILQ_FIRST(&sc->amr_cam_ccbq)) != NULL)
TAILQ_REMOVE(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
splx(s);
return(ccb);
}
/********************************************************************************
* Attach our 'real' SCSI channels to CAM
*/
int
amr_cam_attach(struct amr_softc *sc)
{
struct cam_devq *devq;
int chn;
/* initialise the ccb queue */
TAILQ_INIT(&sc->amr_cam_ccbq);
/*
* Allocate a devq for all our channels combined. This should
* allow for the maximum number of SCSI commands we will accept
* at one time.
*/
if ((devq = cam_simq_alloc(AMR_MAX_SCSI_CMDS)) == NULL)
return(ENOMEM);
/*
* Iterate over our channels, registering them with CAM
*/
for (chn = 0; chn < sc->amr_maxchan; chn++) {
/* allocate a sim */
if ((sc->amr_cam_sim[chn] = cam_sim_alloc(amr_cam_action,
amr_cam_poll,
"amr",
sc,
device_get_unit(sc->amr_dev),
1,
AMR_MAX_SCSI_CMDS,
devq)) == NULL) {
cam_simq_free(devq);
device_printf(sc->amr_dev, "CAM SIM attach failed\n");
return(ENOMEM);
}
/* register the bus ID so we can get it later */
if (xpt_bus_register(sc->amr_cam_sim[chn], chn)) {
device_printf(sc->amr_dev, "CAM XPT bus registration failed\n");
return(ENXIO);
}
}
/*
* XXX we should scan the config and work out which devices are actually
* protected.
*/
return(0);
}
/********************************************************************************
* Disconnect ourselves from CAM
*/
void
amr_cam_detach(struct amr_softc *sc)
{
int chn, first;
for (chn = 0, first = 1; chn < sc->amr_maxchan; chn++) {
/*
* If a sim was allocated for this channel, free it
*/
if (sc->amr_cam_sim[chn] != NULL) {
xpt_bus_deregister(cam_sim_path(sc->amr_cam_sim[chn]));
cam_sim_free(sc->amr_cam_sim[chn], first ? TRUE : FALSE);
first = 0;
}
}
}
/********************************************************************************
********************************************************************************
CAM passthrough interface
********************************************************************************
********************************************************************************/
/********************************************************************************
* Handle a request for action from CAM
*/
static void
amr_cam_action(struct cam_sim *sim, union ccb *ccb)
{
struct amr_softc *sc = cam_sim_softc(sim);
switch(ccb->ccb_h.func_code) {
/*
* Perform SCSI I/O to a physical device.
*/
case XPT_SCSI_IO:
{
struct ccb_hdr *ccbh = &ccb->ccb_h;
struct ccb_scsiio *csio = &ccb->csio;
/* Validate the CCB */
ccbh->status = CAM_REQ_INPROG;
/* check the CDB length */
if (csio->cdb_len > AMR_MAX_CDB_LEN)
ccbh->status = CAM_REQ_CMP_ERR;
/* check that the CDB pointer is not to a physical address */
if ((ccbh->flags & CAM_CDB_POINTER) && (ccbh->flags & CAM_CDB_PHYS))
ccbh->status = CAM_REQ_CMP_ERR;
/* if there is data transfer, it must be to/from a virtual address */
if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
if (ccbh->flags & CAM_DATA_PHYS) /* we can't map it */
ccbh->status = CAM_REQ_CMP_ERR;
if (ccbh->flags & CAM_SCATTER_VALID) /* we want to do the s/g setup */
ccbh->status = CAM_REQ_CMP_ERR;
}
/*
* If the command is to a LUN other than 0, fail it.
* This is probably incorrect, but during testing the firmware did not
* seem to respect the LUN field, and thus devices appear echoed.
*/
if (csio->ccb_h.target_lun != 0)
ccbh->status = CAM_REQ_CMP_ERR;
/* if we're happy with the request, queue it for attention */
if (ccbh->status == CAM_REQ_INPROG) {
/* save the channel number in the ccb */
csio->ccb_h.sim_priv.entries[0].field = cam_sim_bus(sim);
amr_enqueue_ccb(sc, ccb);
amr_startio(sc);
return;
}
break;
}
case XPT_CALC_GEOMETRY:
{
struct ccb_calc_geometry *ccg = &ccb->ccg;
u_int32_t size_in_mb;
u_int32_t secs_per_cylinder;
size_in_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size);
if (size_in_mb > 1024) {
ccg->heads = 255;
ccg->secs_per_track = 63;
} else {
ccg->heads = 64;
ccg->secs_per_track = 32;
}
secs_per_cylinder = ccg->heads * ccg->secs_per_track;
ccg->cylinders = ccg->volume_size / secs_per_cylinder;
ccb->ccb_h.status = CAM_REQ_CMP;
break;
}
/*
* Return path stats. Some of these should probably be
* amended.
*/
case XPT_PATH_INQ:
{
struct ccb_pathinq *cpi = & ccb->cpi;
cpi->version_num = 1; /* XXX??? */
cpi->hba_inquiry = PI_SDTR_ABLE;
cpi->target_sprt = 0;
cpi->hba_misc = 0;
cpi->hba_eng_cnt = 0;
cpi->max_target = AMR_MAX_TARGETS;
cpi->max_lun = AMR_MAX_LUNS;
cpi->initiator_id = 7; /* XXX variable? */
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
strncpy(cpi->hba_vid, "BSDi", HBA_IDLEN);
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
cpi->unit_number = cam_sim_unit(sim);
cpi->bus_id = cam_sim_bus(sim);
cpi->base_transfer_speed = 132 * 1024; /* XXX get from controller? */
cpi->ccb_h.status = CAM_REQ_CMP;
break;
}
/*
* Reject anything else as unsupported.
*/
default:
/* we can't do this */
ccb->ccb_h.status = CAM_REQ_INVALID;
break;
}
xpt_done(ccb);
}
/********************************************************************************
* Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI command.
*/
int
amr_cam_command(struct amr_softc *sc, struct amr_command **acp)
{
struct amr_command *ac;
struct amr_passthrough *ap;
struct ccb_scsiio *csio;
int bus, target, error;
error = 0;
ac = NULL;
ap = NULL;
/* check to see if there is a ccb for us to work with */
if ((csio = (struct ccb_scsiio *)amr_dequeue_ccb(sc)) == NULL)
goto out;
/* get bus/target, XXX validate against protected devices? */
bus = csio->ccb_h.sim_priv.entries[0].field;
target = csio->ccb_h.target_id;
/*
* Build a passthrough command.
*/
/* construct passthrough */
if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_NOWAIT)) == NULL) {
error = ENOMEM;
goto out;
}
bzero(ap, sizeof(*ap));
ap->ap_timeout = 0;
ap->ap_ars = 1;
ap->ap_request_sense_length = 14;
ap->ap_islogical = 0;
ap->ap_channel = bus;
ap->ap_scsi_id = target;
ap->ap_logical_drive_no = csio->ccb_h.target_lun;
ap->ap_cdb_length = csio->cdb_len;
if (csio->ccb_h.flags & CAM_CDB_POINTER) {
bcopy(csio->cdb_io.cdb_ptr, ap->ap_cdb, csio->cdb_len);
} else {
bcopy(csio->cdb_io.cdb_bytes, ap->ap_cdb, csio->cdb_len);
}
/* we leave the data s/g list and s/g count to the map routine later */
debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap->ap_cdb[0], ap->ap_cdb_length, csio->dxfer_len,
ap->ap_channel, ap->ap_scsi_id, ap->ap_logical_drive_no);
/* construct command */
if ((ac = amr_alloccmd(sc)) == NULL) {
error = ENOMEM;
goto out;
}
ac->ac_data = ap;
ac->ac_length = sizeof(*ap);
ac->ac_flags |= AMR_CMD_DATAOUT;
ac->ac_ccb_data = csio->data_ptr;
ac->ac_ccb_length = csio->dxfer_len;
if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
ac->ac_flags |= AMR_CMD_CCB_DATAIN;
if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
ac->ac_complete = amr_cam_complete;
ac->ac_private = csio;
ac->ac_mailbox.mb_command = AMR_CMD_PASS;
out:
if (error != 0) {
if (ac != NULL)
amr_releasecmd(ac);
if (ap != NULL)
free(ap, M_DEVBUF);
if (csio != NULL) /* put it back and try again later */
amr_requeue_ccb(sc, (union ccb *)csio);
}
*acp = ac;
return(error);
}
/********************************************************************************
* Check for interrupt status
*/
static void
amr_cam_poll(struct cam_sim *sim)
{
amr_done(cam_sim_softc(sim));
}
/********************************************************************************
* Handle completion of a command submitted via CAM.
*/
static void
amr_cam_complete(struct amr_command *ac)
{
struct amr_passthrough *ap = (struct amr_passthrough *)ac->ac_data;
struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private;
struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr;
/* XXX note that we're ignoring ac->ac_status - good idea? */
debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, ap->ap_scsi_status);
/*
* Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
*
* If the configuration provides a mechanism to mark a disk a "not managed", we
* could add handling for that to allow disks to be selectively visible.
*/
#if 0
if ((ap->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) {
bzero(csio->data_ptr, csio->dxfer_len);
if (ap->ap_scsi_status == 0xf0) {
csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
} else {
csio->ccb_h.status = CAM_DEV_NOT_THERE;
}
} else {
#else
{
#endif
/* handle passthrough SCSI status */
switch(ap->ap_scsi_status) {
case 0: /* completed OK */
csio->ccb_h.status = CAM_REQ_CMP;
break;
case 0x02:
csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
csio->scsi_status = SCSI_STATUS_CHECK_COND;
bcopy(ap->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN);
csio->sense_len = AMR_MAX_REQ_SENSE_LEN;
csio->ccb_h.status |= CAM_AUTOSNS_VALID;
break;
case 0x08:
csio->ccb_h.status = CAM_SCSI_BUSY;
break;
case 0xf0:
case 0xf4:
default:
csio->ccb_h.status = CAM_REQ_CMP_ERR;
break;
}
}
free(ap, M_DEVBUF);
if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " ");
xpt_done((union ccb *)csio);
amr_releasecmd(ac);
}

61
sys/dev/amr/amr_compat.h Normal file
View file

@ -0,0 +1,61 @@
/*-
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* 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$
*/
/*
* Backwards compatibility support.
*/
#if __FreeBSD_version < 500003 /* old buf style */
# include <sys/buf.h>
# define FREEBSD_4
# define bio buf
# define bioq_init(x) bufq_init(x)
# define bioq_insert_tail(x, y) bufq_insert_tail(x, y)
# define bioq_remove(x, y) bufq_remove(x, y)
# define bioq_first(x) bufq_first(x)
# define bio_queue_head buf_queue_head
# define bio_bcount b_bcount
# define bio_blkno b_blkno
# define bio_caller1 b_caller1
# define bio_data b_data
# define bio_dev b_dev
# define bio_driver1 b_driver1
# define bio_driver2 b_driver2
# define bio_error b_error
# define bio_flags b_flags
# define bio_pblkno b_pblkno
# define bio_resid b_resid
# define BIO_ERROR B_ERROR
# define devstat_end_transaction_bio(x, y) devstat_end_transaction_buf(x, y)
# define BIO_IS_READ(x) ((x)-b_flags & B_READ)
#else
# include <sys/bio.h>
# define BIO_IS_READ(x) ((x)->bio_cmd == BIO_READ)
#endif

View file

@ -1,6 +1,7 @@
/*-
* Copyright (c) 1999 Jonathan Lemon
* Copyright (c) 1999 Michael Smith
* Copyright (c) 1999, 2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -35,7 +36,7 @@
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bio.h>
#include <dev/amr/amr_compat.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/devicestat.h>
@ -48,12 +49,7 @@
#include <dev/amr/amrio.h>
#include <dev/amr/amrreg.h>
#include <dev/amr/amrvar.h>
#if 0
#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
#else
#define debug(fmt, args...)
#endif
#include <dev/amr/amr_tables.h>
/* prototypes */
static int amrd_probe(device_t dev);
@ -65,7 +61,6 @@ static d_close_t amrd_close;
static d_strategy_t amrd_strategy;
static d_ioctl_t amrd_ioctl;
#define AMRD_BDEV_MAJOR 35
#define AMRD_CDEV_MAJOR 133
static struct cdevsw amrd_cdevsw = {
@ -82,12 +77,14 @@ static struct cdevsw amrd_cdevsw = {
/* dump */ nodump,
/* psize */ nopsize,
/* flags */ D_DISK,
/* bmaj */ AMRD_BDEV_MAJOR
/* bmaj */ 254
};
static devclass_t amrd_devclass;
static struct cdevsw amrddisk_cdevsw;
#ifdef FREEBSD_4
static int disks_registered = 0;
#endif
static device_method_t amrd_methods[] = {
DEVMETHOD(device_probe, amrd_probe),
@ -110,7 +107,7 @@ amrd_open(dev_t dev, int flags, int fmt, struct proc *p)
struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
struct disklabel *label;
debug("called");
debug_called(1);
if (sc == NULL)
return (ENXIO);
@ -138,7 +135,7 @@ amrd_close(dev_t dev, int flags, int fmt, struct proc *p)
{
struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
debug("called");
debug_called(1);
if (sc == NULL)
return (ENXIO);
@ -149,18 +146,7 @@ amrd_close(dev_t dev, int flags, int fmt, struct proc *p)
static int
amrd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
{
struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
int error;
debug("called");
if (sc == NULL)
return (ENXIO);
if ((error = amr_submit_ioctl(sc->amrd_controller, sc->amrd_drive, cmd, addr, flag, p)) != ENOIOCTL) {
debug("amr_submit_ioctl returned %d\n", error);
return(error);
}
return (ENOTTY);
}
@ -171,76 +157,60 @@ amrd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
* be a multiple of a sector in length.
*/
static void
amrd_strategy(struct bio *bp)
amrd_strategy(struct bio *bio)
{
struct amrd_softc *sc = (struct amrd_softc *)bp->bio_dev->si_drv1;
debug("called to %s %d bytes at b_blkno 0x%x b_pblkno 0x%x",
(bp->b_flags & B_READ) ? "read" : "write", bp->bio_bcount, bp->bio_blkno, bp->bio_pblkno);
struct amrd_softc *sc = (struct amrd_softc *)bio->bio_dev->si_drv1;
/* bogus disk? */
if (sc == NULL) {
bp->bio_error = EINVAL;
bio->bio_error = EINVAL;
goto bad;
}
#if 0
/* XXX may only be temporarily offline - sleep? */
if (sc->amrd_drive->ld_state == AMR_SYSD_OFFLINE) {
bp->bio_error = ENXIO;
goto bad;
}
#endif
/* do-nothing operation */
if (bp->bio_bcount == 0)
if (bio->bio_bcount == 0)
goto done;
devstat_start_transaction(&sc->amrd_stats);
amr_submit_buf(sc->amrd_controller, bp);
amr_submit_bio(sc->amrd_controller, bio);
return;
bad:
bp->bio_flags |= BIO_ERROR;
bio->bio_flags |= BIO_ERROR;
done:
/*
* Correctly set the buf to indicate a completed transfer
*/
bp->bio_resid = bp->bio_bcount;
biodone(bp);
bio->bio_resid = bio->bio_bcount;
biodone(bio);
return;
}
void
amrd_intr(void *data)
{
struct bio *bp = (struct bio *)data;
struct amrd_softc *sc = (struct amrd_softc *)bp->bio_dev->si_drv1;
struct bio *bio = (struct bio *)data;
struct amrd_softc *sc = (struct amrd_softc *)bio->bio_dev->si_drv1;
debug("called");
debug_called(2);
if (bp->bio_flags & BIO_ERROR) {
bp->bio_error = EIO;
debug("i/o error\n");
if (bio->bio_flags & BIO_ERROR) {
bio->bio_error = EIO;
debug(1, "i/o error\n");
} else {
#if 0
int i;
for (i = 0; i < 512; i += 16)
debug(" %04x %16D", i, bp->bio_data + i, " ");
#endif
bp->bio_resid = 0;
bio->bio_resid = 0;
}
devstat_end_transaction_bio(&sc->amrd_stats, bp);
biodone(bp);
devstat_end_transaction_bio(&sc->amrd_stats, bio);
biodone(bio);
}
static int
amrd_probe(device_t dev)
{
debug("called");
debug_called(1);
device_set_desc(dev, "MegaRAID logical drive");
return (0);
@ -251,9 +221,8 @@ amrd_attach(device_t dev)
{
struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
device_t parent;
char *state;
debug("called");
debug_called(1);
parent = device_get_parent(dev);
sc->amrd_controller = (struct amr_softc *)device_get_softc(parent);
@ -261,26 +230,10 @@ amrd_attach(device_t dev)
sc->amrd_drive = device_get_ivars(dev);
sc->amrd_dev = dev;
switch(sc->amrd_drive->al_state) {
case AMRD_OFFLINE:
state = "offline";
break;
case AMRD_DEGRADED:
state = "degraded";
break;
case AMRD_OPTIMAL:
state = "optimal";
break;
case AMRD_DELETED:
state = "deleted";
break;
default:
state = "unknown state";
}
device_printf(dev, "%uMB (%u sectors) RAID %d (%s)\n",
sc->amrd_drive->al_size / ((1024 * 1024) / AMR_BLKSIZE),
sc->amrd_drive->al_size, sc->amrd_drive->al_properties & 0xf, state);
sc->amrd_drive->al_size, sc->amrd_drive->al_properties & AMR_DRV_RAID_MASK,
amr_describe_code(amr_table_drvstate, AMR_DRV_CURSTATE(sc->amrd_drive->al_state)));
devstat_add_entry(&sc->amrd_stats, "amrd", sc->amrd_unit, AMR_BLKSIZE,
DEVSTAT_NO_ORDERED_TAGS,
@ -289,10 +242,12 @@ amrd_attach(device_t dev)
sc->amrd_dev_t = disk_create(sc->amrd_unit, &sc->amrd_disk, 0, &amrd_cdevsw, &amrddisk_cdevsw);
sc->amrd_dev_t->si_drv1 = sc;
#ifdef FREEBSD_4
disks_registered++;
#endif
/* set maximum I/O size */
/* dsk->si_iosize_max = ??? */;
/* set maximum I/O size to match the maximum s/g size */
sc->amrd_dev_t->si_iosize_max = (AMR_NSEG - 1) * PAGE_SIZE;
return (0);
}
@ -302,10 +257,18 @@ amrd_detach(device_t dev)
{
struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
debug("called");
debug_called(1);
if (sc->amrd_flags & AMRD_OPEN)
return(EBUSY);
devstat_remove_entry(&sc->amrd_stats);
#ifdef FREEBSD_4
if (--disks_registered == 0)
cdevsw_remove(&amrddisk_cdevsw);
#else
disk_destroy(sc->amrd_dev_t);
#endif
return(0);
}

View file

@ -1,5 +1,6 @@
/*-
* Copyright (c) 1999 Michael Smith
* Copyright (c) 1999,2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -30,8 +31,8 @@
#include <sys/systm.h>
#include <sys/kernel.h>
#include <dev/amr/amr_compat.h>
#include <sys/bus.h>
#include <sys/bio.h>
#include <sys/conf.h>
#include <sys/devicestat.h>
#include <sys/disk.h>
@ -49,23 +50,27 @@
#include <dev/amr/amrreg.h>
#include <dev/amr/amrvar.h>
#if 0
#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
#else
#define debug(fmt, args...)
#endif
static int amr_pci_probe(device_t dev);
static int amr_pci_attach(device_t dev);
static int amr_pci_probe(device_t dev);
static int amr_pci_attach(device_t dev);
static int amr_pci_detach(device_t dev);
static int amr_pci_shutdown(device_t dev);
static int amr_pci_suspend(device_t dev);
static int amr_pci_resume(device_t dev);
static void amr_pci_intr(void *arg);
static void amr_pci_free(struct amr_softc *sc);
static void amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
static int amr_sglist_map(struct amr_softc *sc);
static void amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
static int amr_setup_mbox(struct amr_softc *sc);
static device_method_t amr_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, amr_pci_probe),
DEVMETHOD(device_attach, amr_pci_attach),
DEVMETHOD(device_detach, amr_detach),
DEVMETHOD(device_shutdown, amr_shutdown),
DEVMETHOD(device_suspend, amr_suspend),
DEVMETHOD(device_resume, amr_resume),
DEVMETHOD(device_detach, amr_pci_detach),
DEVMETHOD(device_shutdown, amr_pci_shutdown),
DEVMETHOD(device_suspend, amr_pci_suspend),
DEVMETHOD(device_resume, amr_pci_resume),
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
@ -78,6 +83,7 @@ static driver_t amr_pci_driver = {
sizeof(struct amr_softc)
};
devclass_t amr_devclass;
DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0);
static struct
@ -89,7 +95,8 @@ static struct
} amr_device_ids[] = {
{0x101e, 0x9010, 0},
{0x101e, 0x9060, 0},
{0x8086, 0x1960, PROBE_SIGNATURE}, /* generic i960RD, check signature */
{0x8086, 0x1960, PROBE_SIGNATURE}, /* generic i960RD, check for signature */
{0x101e, 0x1960, 0},
{0, 0, 0}
};
@ -98,7 +105,7 @@ amr_pci_probe(device_t dev)
{
int i;
debug("called");
debug_called(1);
for (i = 0; amr_device_ids[i].vendor != 0; i++) {
if ((pci_get_vendor(dev) == amr_device_ids[i].vendor) &&
@ -122,7 +129,7 @@ amr_pci_attach(device_t dev)
int rid, rtype, error;
u_int32_t command;
debug("called");
debug_called(1);
/*
* Initialise softc.
@ -131,30 +138,30 @@ amr_pci_attach(device_t dev)
bzero(sc, sizeof(*sc));
sc->amr_dev = dev;
/* assume failure is 'not configured' */
error = ENXIO;
/*
* Determine board type..
* Determine board type.
*/
command = pci_read_config(dev, PCIR_COMMAND, 1);
if ((pci_get_vendor(dev) == 0x8086) && (pci_get_device(dev) == 0x1960)) {
sc->amr_type = AMR_TYPE_QUARTZ;
if (pci_get_device(dev) == 0x1960) {
/*
* Make sure we are going to be able to talk to this board.
*/
if ((command & PCIM_CMD_MEMEN) == 0) {
device_printf(dev, "memory window not available\n");
return(ENXIO);
goto out;
}
sc->amr_type |= AMR_TYPE_QUARTZ;
} else {
sc->amr_type = AMR_TYPE_STD;
/*
* Make sure we are going to be able to talk to this board.
*/
if ((command & PCIM_CMD_PORTEN) == 0) {
device_printf(dev, "I/O window not available\n");
return(ENXIO);
goto out;
}
}
@ -168,47 +175,381 @@ amr_pci_attach(device_t dev)
/*
* Allocate the PCI register window.
*/
rid = AMR_CFG_BASE;
rtype = (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT;
rid = PCIR_MAPS;
rtype = AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT;
sc->amr_reg = bus_alloc_resource(dev, rtype, &rid, 0, ~0, 1, RF_ACTIVE);
if (sc->amr_reg == NULL) {
device_printf(sc->amr_dev, "couldn't allocate register window\n");
amr_free(sc);
return(ENXIO);
device_printf(sc->amr_dev, "can't allocate register window\n");
goto out;
}
sc->amr_btag = rman_get_bustag(sc->amr_reg);
sc->amr_bhandle = rman_get_bushandle(sc->amr_reg);
/*
* Allocate and connect our interrupt.
*/
rid = 0;
sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->amr_irq == NULL) {
device_printf(sc->amr_dev, "can't allocate interrupt\n");
goto out;
}
if (bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO, amr_pci_intr, sc, &sc->amr_intr)) {
device_printf(sc->amr_dev, "can't set up interrupt\n");
goto out;
}
debug(2, "interrupt attached");
/* assume failure is 'out of memory' */
error = ENOMEM;
/*
* Allocate the parent bus DMA tag appropriate for PCI.
*/
error = bus_dma_tag_create(NULL, /* parent */
if (bus_dma_tag_create(NULL, /* parent */
1, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */
BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
BUS_DMA_ALLOCNOW, /* flags */
&sc->amr_parent_dmat)) {
device_printf(dev, "can't allocate parent DMA tag\n");
goto out;
}
/*
* Create DMA tag for mapping buffers into controller-addressable space.
*/
if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */
1, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */
BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
0, /* flags */
&sc->amr_buffer_dmat)) {
device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
goto out;
}
debug(2, "dma tag done");
/*
* Allocate and set up mailbox in a bus-visible fashion.
*/
if ((error = amr_setup_mbox(sc)) != 0)
goto out;
debug(2, "mailbox setup");
/*
* Build the scatter/gather buffers.
*/
if (amr_sglist_map(sc))
goto out;
debug(2, "s/g list mapped");
/*
* Do bus-independant initialisation, bring controller online.
*/
error = amr_attach(sc);
out:
if (error)
amr_pci_free(sc);
return(error);
}
/********************************************************************************
* Disconnect from the controller completely, in preparation for unload.
*/
static int
amr_pci_detach(device_t dev)
{
struct amr_softc *sc = device_get_softc(dev);
int error;
debug_called(1);
if (sc->amr_state & AMR_STATE_OPEN)
return(EBUSY);
if ((error = amr_pci_shutdown(dev)))
return(error);
amr_pci_free(sc);
return(0);
}
/********************************************************************************
* Bring the controller down to a dormant state and detach all child devices.
*
* This function is called before detach, system shutdown, or before performing
* an operation which may add or delete system disks. (Call amr_startup to
* resume normal operation.)
*
* Note that we can assume that the bioq on the controller is empty, as we won't
* allow shutdown if any device is open.
*/
static int
amr_pci_shutdown(device_t dev)
{
struct amr_softc *sc = device_get_softc(dev);
debug_called(1);
/* mark ourselves as in-shutdown */
sc->amr_state |= AMR_STATE_SHUTDOWN;
/* flush controller */
device_printf(sc->amr_dev, "flushing cache...");
printf("%s\n", amr_flush(sc) ? "failed" : "done");
/* XXX disable interrupts? */
return(0);
}
/********************************************************************************
* Bring the controller to a quiescent state, ready for system suspend.
*/
static int
amr_pci_suspend(device_t dev)
{
struct amr_softc *sc = device_get_softc(dev);
debug_called(1);
sc->amr_state |= AMR_STATE_SUSPEND;
/* flush controller */
device_printf(sc->amr_dev, "flushing cache...");
printf("%s\n", amr_flush(sc) ? "failed" : "done");
/* XXX disable interrupts? */
return(0);
}
/********************************************************************************
* Bring the controller back to a state ready for operation.
*/
static int
amr_pci_resume(device_t dev)
{
struct amr_softc *sc = device_get_softc(dev);
debug_called(1);
sc->amr_state &= ~AMR_STATE_SUSPEND;
/* XXX enable interrupts? */
return(0);
}
/*******************************************************************************
* Take an interrupt, or be poked by other code to look for interrupt-worthy
* status.
*/
static void
amr_pci_intr(void *arg)
{
struct amr_softc *sc = (struct amr_softc *)arg;
debug_called(2);
/* collect finished commands, queue anything waiting */
amr_done(sc);
}
/********************************************************************************
* Free all of the resources associated with (sc)
*
* Should not be called if the controller is active.
*/
static void
amr_pci_free(struct amr_softc *sc)
{
u_int8_t *p;
debug_called(1);
amr_free(sc);
/* destroy data-transfer DMA tag */
if (sc->amr_buffer_dmat)
bus_dma_tag_destroy(sc->amr_buffer_dmat);
/* free and destroy DMA memory and tag for s/g lists */
if (sc->amr_sgtable)
bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
if (sc->amr_sg_dmat)
bus_dma_tag_destroy(sc->amr_sg_dmat);
/* free and destroy DMA memory and tag for mailbox */
if (sc->amr_mailbox) {
p = (u_int8_t *)(uintptr_t)(volatile void *)sc->amr_mailbox;
bus_dmamem_free(sc->amr_sg_dmat, p - 16, sc->amr_sg_dmamap);
}
if (sc->amr_sg_dmat)
bus_dma_tag_destroy(sc->amr_sg_dmat);
/* disconnect the interrupt handler */
if (sc->amr_intr)
bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr);
if (sc->amr_irq != NULL)
bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq);
/* destroy the parent DMA tag */
if (sc->amr_parent_dmat)
bus_dma_tag_destroy(sc->amr_parent_dmat);
/* release the register window mapping */
if (sc->amr_reg != NULL)
bus_release_resource(sc->amr_dev,
AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT,
PCIR_MAPS, sc->amr_reg);
}
/********************************************************************************
* Allocate and map the scatter/gather table in bus space.
*/
static void
amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct amr_softc *sc = (struct amr_softc *)arg;
debug_called(1);
/* save base of s/g table's address in bus space */
sc->amr_sgbusaddr = segs->ds_addr;
}
static int
amr_sglist_map(struct amr_softc *sc)
{
size_t segsize;
int error;
debug_called(1);
/*
* Create a single tag describing a region large enough to hold all of
* the s/g lists we will need.
*
* Note that we could probably use AMR_LIMITCMD here, but that may become tunable.
*/
segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD;
error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */
1, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */
segsize, 1, /* maxsize, nsegments */
BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
BUS_DMA_ALLOCNOW, /* flags */
&sc->amr_parent_dmat);
0, /* flags */
&sc->amr_sg_dmat);
if (error != 0) {
device_printf(dev, "can't allocate parent DMA tag\n");
amr_free(sc);
device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n");
return(ENOMEM);
}
/*
* Do bus-independant initialisation.
* Allocate enough s/g maps for all commands and permanently map them into
* controller-visible space.
*
* XXX this assumes we can get enough space for all the s/g maps in one
* contiguous slab. We may need to switch to a more complex arrangement where
* we allocate in smaller chunks and keep a lookup table from slot to bus address.
*
* XXX HACK ALERT: at least some controllers don't like the s/g memory being
* allocated below 0x2000. We leak some memory if we get some
* below this mark and allocate again. We should be able to
* avoid this with the tag setup, but that does't seem to work.
*/
error = amr_attach(sc);
if (error != 0) {
amr_free(sc);
return(error);
retry:
error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap);
if (error) {
device_printf(sc->amr_dev, "can't allocate s/g table\n");
return(ENOMEM);
}
bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_sglist_map_helper, sc, 0);
if (sc->amr_sgbusaddr < 0x2000) {
debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr);
goto retry;
}
/*
* Start the controller.
*/
amr_startup(sc);
return(0);
}
/********************************************************************************
* Allocate and set up mailbox areas for the controller (sc)
*
* The basic mailbox structure should be 16-byte aligned. This means that the
* mailbox64 structure has 4 bytes hanging off the bottom.
*/
static void
amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct amr_softc *sc = (struct amr_softc *)arg;
debug_called(1);
/* save phsyical base of the basic mailbox structure */
sc->amr_mailboxphys = segs->ds_addr + 16;
}
static int
amr_setup_mbox(struct amr_softc *sc)
{
int error;
u_int8_t *p;
debug_called(1);
/*
* Create a single tag describing a region large enough to hold the entire
* mailbox.
*/
error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */
16, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */
BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
0, /* flags */
&sc->amr_mailbox_dmat);
if (error != 0) {
device_printf(sc->amr_dev, "can't allocate mailbox tag\n");
return(ENOMEM);
}
/*
* Allocate the mailbox structure and permanently map it into
* controller-visible space.
*/
error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT,
&sc->amr_mailbox_dmamap);
if (error) {
device_printf(sc->amr_dev, "can't allocate mailbox memory\n");
return(ENOMEM);
}
bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p,
sizeof(struct amr_mailbox64), amr_setup_mbox_helper, sc, 0);
/*
* Conventional mailbox is inside the mailbox64 region.
*/
bzero(p, sizeof(struct amr_mailbox64));
sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12);
sc->amr_mailbox = (struct amr_mailbox *)(p + 16);
return(0);
}

110
sys/dev/amr/amr_tables.h Normal file
View file

@ -0,0 +1,110 @@
/*-
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* 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$
*/
/*
* Lookup table for code-to-text translations.
*/
struct amr_code_lookup {
char *string;
u_int32_t code;
};
extern char *amr_describe_code(struct amr_code_lookup *table, u_int32_t code);
#ifndef AMR_DEFINE_TABLES
extern struct amr_code_lookup amr_table_qinit[];
extern struct amr_code_lookup amr_table_sinit[];
extern struct amr_code_lookup amr_table_drvstate[];
#else /* AMR_DEFINE_TABLES */
/********************************************************************************
* Look up a text description of a numeric code and return a pointer to same.
*/
char *
amr_describe_code(struct amr_code_lookup *table, u_int32_t code)
{
int i;
for (i = 0; table[i].string != NULL; i++)
if (table[i].code == code)
return(table[i].string);
return(table[i+1].string);
}
struct amr_code_lookup amr_table_qinit[] = {
{"init scanning drives", AMR_QINIT_SCAN},
{"init scanning initialising", AMR_QINIT_SCANINIT},
{"init firmware initing", AMR_QINIT_FIRMWARE},
{"init in progress", AMR_QINIT_INPROG},
{"init spinning drives", AMR_QINIT_SPINUP},
{"insufficient memory", AMR_QINIT_NOMEM},
{"init flushing cache", AMR_QINIT_CACHEFLUSH},
{"init successfully done", AMR_QINIT_DONE},
{NULL, 0},
{"unknown init code", 0}
};
struct amr_code_lookup amr_table_sinit[] = {
{"init abnormal terminated", AMR_SINIT_ABEND},
{"insufficient memory", AMR_SINIT_NOMEM},
{"firmware flushing cache", AMR_SINIT_CACHEFLUSH},
{"init in progress", AMR_SINIT_INPROG},
{"firmware spinning drives", AMR_SINIT_SPINUP},
{"init successfully done", AMR_SINIT_DONE},
{NULL, 0},
{"unknown init code", 0}
};
struct amr_code_lookup amr_table_drvstate[] = {
{"offline", AMR_DRV_OFFLINE},
{"degraded", AMR_DRV_DEGRADED},
{"optimal", AMR_DRV_OPTIMAL},
{"online", AMR_DRV_ONLINE},
{"failed", AMR_DRV_FAILED},
{"rebuild", AMR_DRV_REBUILD},
{"hot spare", AMR_DRV_HOTSPARE},
{NULL, 0},
{"unknown", 0}
};
struct amr_code_lookup amr_table_adaptertype[] = {
{"Series 431", AMR_SIG_431},
{"Series 438", AMR_SIG_438},
{"Series 762", AMR_SIG_762},
{"Integrated HP NetRAID (T5)", AMR_SIG_T5},
{"Series 466", AMR_SIG_466},
{"Series 467", AMR_SIG_467},
{"Integrated HP NetRAID (T7)", AMR_SIG_T7},
{"Series 490", AMR_SIG_490},
{NULL, 0},
{"unknown adapter", 0}
};
#endif

View file

@ -27,10 +27,54 @@
*/
/*
* Drive status
* ioctl interface
*/
#define AMRD_OFFLINE 0x0
#define AMRD_DEGRADED 0x1
#define AMRD_OPTIMAL 0x2
#define AMRD_DELETED 0x3
#include <sys/ioccom.h>
/*
* Fetch the driver's interface version.
*/
#define AMR_IO_VERSION_NUMBER 0x01
#define AMR_IO_VERSION _IOR('A', 0x200, int)
/*
* Pass a command from userspace through to the adapter.
*
* Note that in order to be code-compatible with the Linux
* interface where possible, the formatting of the au_cmd field is
* somewhat Interesting.
*
* For normal commands, the layout is (fields from struct amr_mailbox_ioctl):
*
* 0 mb_command
* 1 mb_channel
* 2 mb_param
* 3 mb_pad[0]
* 4 mb_drive
*
* For SCSI passthrough commands, the layout is:
*
* 0 AMR_CMD_PASS (0x3)
* 1 reserved, 0
* 2 cdb length
* 3 cdb data
* 3+cdb_len passthrough control byte (timeout, ars, islogical)
* 4+cdb_len reserved, 0
* 5+cdb_len channel
* 6+cdb_len target
*/
struct amr_user_ioctl {
unsigned char au_cmd[32]; /* command text from userspace */
void *au_buffer; /* data buffer in userspace */
unsigned long au_length; /* data buffer size (0 == no data) */
int au_direction; /* data transfer direction */
#define AMR_IO_NODATA 0
#define AMR_IO_READ 1
#define AMR_IO_WRITE 2
int au_status; /* command status returned by adapter */
};
#define AMR_IO_COMMAND _IOWR('A', 0x201, struct amr_user_ioctl)

View file

@ -1,5 +1,6 @@
/*-
* Copyright (c) 1999 Michael Smith
* Copyright (c) 1999,2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,16 +27,65 @@
* $FreeBSD$
*/
/********************************************************************************
********************************************************************************
Driver parameters
********************************************************************************
********************************************************************************/
/*
* 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.
*
* The AMI documentation says that the limit is 26. Unfortunately, there's no way to
* cleanly fit more than 16 entries in without a page boundary. But is this a concern,
* since we allocate the s/g maps contiguously anyway?
*/
#define AMR_NSEG 16
#define AMR_MAXCMD 255 /* ident = 0 not allowed */
#define AMR_LIMITCMD 120 /* maximum count of outstanding commands */
#define AMR_MAXLD 40
#define AMR_MAX_CHANNELS 4
#define AMR_MAX_TARGETS 15
#define AMR_MAX_LUNS 7
#define AMR_MAX_SCSI_CMDS (15 * AMR_MAX_CHANNELS) /* one for every target? */
#define AMR_MAX_CDB_LEN 0x0a
#define AMR_MAX_REQ_SENSE_LEN 0x20
#define AMR_BLKSIZE 512 /* constant for all controllers */
/*
* Perform at-startup board initialisation.
* At this point in time, this code doesn't work correctly, so leave it disabled.
*/
/*#define AMR_BOARD_INIT*/
/********************************************************************************
********************************************************************************
Interface Magic Numbers
********************************************************************************
********************************************************************************/
/*
* Mailbox commands
*/
#define AMR_CMD_LREAD 0x01
#define AMR_CMD_LWRITE 0x02
#define AMR_CMD_ENQUIRY 0x05
#define AMR_CMD_FLUSH 0x0a
#define AMR_CMD_CONFIG 0xa1
#define AMR_CMD_LREAD 0x01
#define AMR_CMD_LWRITE 0x02
#define AMR_CMD_PASS 0x03
#define AMR_CMD_EXT_ENQUIRY 0x04
#define AMR_CMD_ENQUIRY 0x05
#define AMR_CMD_FLUSH 0x0a
#define AMR_CMD_EXT_ENQUIRY2 0x0c
#define AMR_CONFIG_PRODINFO 0x0e
#define AMR_CONFIG_ENQ3 0x0f
#define AMR_CMD_GET_MACHINEID 0x36
#define AMR_CMD_GET_INITIATOR 0x7d /* returns one byte */
#define AMR_CMD_CONFIG 0xa1
#define AMR_CONFIG_PRODUCT_INFO 0x0e
#define AMR_CONFIG_ENQ3 0x0f
#define AMR_CONFIG_ENQ3_SOLICITED_NOTIFY 0x01
#define AMR_CONFIG_ENQ3_SOLICITED_FULL 0x02
#define AMR_CONFIG_ENQ3_UNSOLICITED 0x03
@ -48,45 +98,57 @@
#define AMR_STATUS_FAILED 0x80
/*
* Quartz doorbell registers
* Physical/logical drive states
*/
#define AMR_QIDB 0x20
#define AMR_QODB 0x2c
#define AMR_QIDB_SUBMIT 0x00000001 /* mailbox ready for work */
#define AMR_QIDB_ACK 0x00000002 /* mailbox done */
#define AMR_QODB_READY 0x10001234 /* work ready to be processed */
#define AMR_DRV_CURSTATE(x) ((x) & 0x0f)
#define AMR_DRV_PREVSTATE(x) (((x) >> 4) & 0x0f)
#define AMR_DRV_OFFLINE 0x00
#define AMR_DRV_DEGRADED 0x01
#define AMR_DRV_OPTIMAL 0x02
#define AMR_DRV_ONLINE 0x03
#define AMR_DRV_FAILED 0x04
#define AMR_DRV_REBUILD 0x05
#define AMR_DRV_HOTSPARE 0x06
/*
* Standard I/O registers
* Logical drive properties
*/
#define AMR_SCMD 0x10 /* command/ack register (write) */
#define AMR_SMBOX_BUSY 0x10 /* mailbox status (read) */
#define AMR_STOGGLE 0x11 /* interrupt enable bit here */
#define AMR_SMBOX_0 0x14 /* mailbox physical address low byte */
#define AMR_SMBOX_1 0x15
#define AMR_SMBOX_2 0x16
#define AMR_SMBOX_3 0x17 /* high byte */
#define AMR_SMBOX_ENABLE 0x18 /* atomic mailbox address enable */
#define AMR_SINTR 0x1a /* interrupt status */
#define AMR_DRV_RAID_MASK 0x0f /* RAID level 0, 1, 3, 5, etc. */
#define AMR_DRV_WRITEBACK 0x10 /* write-back enabled */
#define AMR_DRV_READHEAD 0x20 /* readhead policy enabled */
#define AMR_DRV_ADAPTIVE 0x40 /* adaptive I/O policy enabled */
/*
* Standard I/O magic numbers
* Battery status
*/
#define AMR_SCMD_POST 0x10 /* -> SCMD to initiate action on mailbox */
#define AMR_SCMD_ACKINTR 0x08 /* -> SCMD to ack mailbox retrieved */
#define AMR_STOGL_IENABLE 0xc0 /* in STOGGLE */
#define AMR_SINTR_VALID 0x40 /* in SINTR */
#define AMR_SMBOX_BUSYFLAG 0x10 /* in SMBOX_BUSY */
#define AMR_SMBOX_ADDR 0x00 /* -> SMBOX_ENABLE */
#define AMR_BATT_MODULE_MISSING 0x01
#define AMR_BATT_LOW_VOLTAGE 0x02
#define AMR_BATT_TEMP_HIGH 0x04
#define AMR_BATT_PACK_MISSING 0x08
#define AMR_BATT_CHARGE_MASK 0x30
#define AMR_BATT_CHARGE_DONE 0x00
#define AMR_BATT_CHARGE_INPROG 0x10
#define AMR_BATT_CHARGE_FAIL 0x20
#define AMR_BATT_CYCLES_EXCEEDED 0x40
/********************************************************************************
********************************************************************************
8LD Firmware Interface
********************************************************************************
********************************************************************************/
/*
* Old Enquiry results
* Array constraints
*/
#define AMR_8LD_MAXDRIVES 8
#define AMR_8LD_MAXCHAN 5
#define AMR_8LD_MAXTARG 15
#define AMR_8LD_MAXPHYSDRIVES (AMR_8LD_MAXCHAN * AMR_8LD_MAXTARG)
/*
* Adapter Info structure
*/
struct amr_adapter_info
{
u_int8_t aa_maxio;
@ -99,9 +161,18 @@ struct amr_adapter_info
u_int8_t aa_memorysize;
u_int8_t aa_cacheflush;
u_int8_t aa_bios[4];
u_int8_t res1[7];
u_int8_t aa_boardtype;
u_int8_t aa_scsisensealert;
u_int8_t aa_writeconfigcount;
u_int8_t aa_driveinsertioncount;
u_int8_t aa_inserteddrive;
u_int8_t aa_batterystatus;
u_int8_t res1;
} __attribute__ ((packed));
/*
* Logical Drive info structure
*/
struct amr_logdrive_info
{
u_int8_t al_numdrives;
@ -111,19 +182,60 @@ struct amr_logdrive_info
u_int8_t al_state[AMR_8LD_MAXDRIVES];
} __attribute__ ((packed));
/*
* Physical Drive info structure
*/
struct amr_physdrive_info
{
u_int8_t ap_state[AMR_8LD_MAXPHYSDRIVES];
u_int8_t res1;
u_int8_t ap_state[AMR_8LD_MAXPHYSDRIVES]; /* low nibble current state, high nibble previous state */
u_int8_t ap_predictivefailure;
} __attribute__ ((packed));
/*
* Enquiry response structure for AMR_CMD_ENQUIRY, AMR_CMD_EXT_ENQUIRY and
* AMR_CMD_EXT_ENQUIRY2.
* ENQUIRY EXT_ENQUIRY EXT_ENQUIRY2
*/
struct amr_enquiry
{
struct amr_adapter_info ae_adapter;
struct amr_logdrive_info ae_ldrv;
struct amr_physdrive_info ae_pdrv;
struct amr_adapter_info ae_adapter; /* X X X */
struct amr_logdrive_info ae_ldrv; /* X X X */
struct amr_physdrive_info ae_pdrv; /* X X X */
u_int8_t ae_formatting[AMR_8LD_MAXDRIVES];/* X X */
u_int8_t res1[AMR_8LD_MAXDRIVES]; /* X X */
u_int32_t ae_extlen; /* X */
u_int16_t ae_subsystem; /* X */
u_int16_t ae_subvendor; /* X */
u_int32_t ae_signature; /* X */
#define AMR_SIG_431 0xfffe0001
#define AMR_SIG_438 0xfffd0002
#define AMR_SIG_762 0xfffc0003
#define AMR_SIG_T5 0xfffb0004
#define AMR_SIG_466 0xfffa0005
#define AMR_SIG_467 0xfff90006
#define AMR_SIG_T7 0xfff80007
#define AMR_SIG_490 0xfff70008
u_int8_t res2[844]; /* X */
} __attribute__ ((packed));
/********************************************************************************
********************************************************************************
40LD Firmware Interface
********************************************************************************
********************************************************************************/
/*
* Array constraints
*/
#define AMR_40LD_MAXDRIVES 40
#define AMR_40LD_MAXCHAN 16
#define AMR_40LD_MAXTARG 16
#define AMR_40LD_MAXPHYSDRIVES 256
/*
* Product Info structure
*/
struct amr_prodinfo
{
u_int32_t ap_size; /* current size in bytes (not including resvd) */
@ -144,6 +256,105 @@ struct amr_prodinfo
u_int8_t ap_numnotifyctr; /* number of notify counters */
} __attribute__((packed));
/*
* Notify structure
*/
struct amr_notify
{
u_int32_t an_globalcounter; /* change counter */
u_int8_t an_paramcounter; /* parameter change counter */
u_int8_t an_paramid;
#define AMR_PARAM_REBUILD_RATE 0x01 /* value = new rebuild rate */
#define AMR_PARAM_FLUSH_INTERVAL 0x02 /* value = new flush interval */
#define AMR_PARAM_SENSE_ALERT 0x03 /* value = last physical drive with check condition set */
#define AMR_PARAM_DRIVE_INSERTED 0x04 /* value = last physical drive inserted */
#define AMR_PARAM_BATTERY_STATUS 0x05 /* value = battery status */
u_int16_t an_paramval;
u_int8_t an_writeconfigcounter; /* write config occurred */
u_int8_t res1[3];
u_int8_t an_ldrvopcounter; /* logical drive operation started/completed */
u_int8_t an_ldrvopid;
u_int8_t an_ldrvopcmd;
#define AMR_LDRVOP_CHECK 0x01
#define AMR_LDRVOP_INIT 0x02
#define AMR_LDRVOP_REBUILD 0x03
u_int8_t an_ldrvopstatus;
#define AMR_LDRVOP_SUCCESS 0x00
#define AMR_LDRVOP_FAILED 0x01
#define AMR_LDRVOP_ABORTED 0x02
#define AMR_LDRVOP_CORRECTED 0x03
#define AMR_LDRVOP_STARTED 0x04
u_int8_t an_ldrvstatecounter; /* logical drive state change occurred */
u_int8_t an_ldrvstateid;
u_int8_t an_ldrvstatenew;
u_int8_t an_ldrvstateold;
u_int8_t an_pdrvstatecounter; /* physical drive state change occurred */
u_int8_t an_pdrvstateid;
u_int8_t an_pdrvstatenew;
u_int8_t an_pdrvstateold;
u_int8_t an_pdrvfmtcounter;
u_int8_t an_pdrvfmtid;
u_int8_t an_pdrvfmtval;
#define AMR_FORMAT_START 0x01
#define AMR_FORMAT_COMPLETE 0x02
u_int8_t res2;
u_int8_t an_targxfercounter; /* scsi xfer rate change */
u_int8_t an_targxferid;
u_int8_t an_targxferval;
u_int8_t res3;
u_int8_t an_fcloopidcounter; /* FC/AL loop ID changed */
u_int8_t an_fcloopidpdrvid;
u_int8_t an_fcloopid0;
u_int8_t an_fcloopid1;
u_int8_t an_fcloopstatecounter; /* FC/AL loop status changed */
u_int8_t an_fcloopstate0;
u_int8_t an_fcloopstate1;
u_int8_t res4;
} __attribute__((packed));
/*
* Enquiry3 structure
*/
struct amr_enquiry3
{
u_int32_t ae_datasize; /* valid data size in this structure */
union { /* event notify structure */
struct amr_notify n;
u_int8_t pad[0x80];
} ae_notify;
u_int8_t ae_rebuildrate; /* current rebuild rate in % */
u_int8_t ae_cacheflush; /* flush interval in seconds */
u_int8_t ae_sensealert;
u_int8_t ae_driveinsertcount; /* count of inserted drives */
u_int8_t ae_batterystatus;
u_int8_t ae_numldrives;
u_int8_t ae_reconstate[AMR_40LD_MAXDRIVES / 8]; /* reconstruction state */
u_int16_t ae_opstatus[AMR_40LD_MAXDRIVES / 8]; /* operation status per drive */
u_int32_t ae_drivesize[AMR_40LD_MAXDRIVES]; /* logical drive size */
u_int8_t ae_driveprop[AMR_40LD_MAXDRIVES]; /* logical drive properties */
u_int8_t ae_drivestate[AMR_40LD_MAXDRIVES]; /* physical drive state */
u_int16_t ae_driveformat[AMR_40LD_MAXPHYSDRIVES];
u_int8_t ae_targxfer[80]; /* physical drive transfer rates */
u_int8_t res1[263]; /* pad to 1024 bytes */
} __attribute__ ((packed));
/********************************************************************************
********************************************************************************
Mailbox and Command Structures
********************************************************************************
********************************************************************************/
#define AMR_MBOX_CMDSIZE 0x10 /* portion worth copying for controller */
struct amr_mailbox
@ -177,17 +388,17 @@ struct amr_mailbox_ioctl
u_int8_t mb_ident;
u_int8_t mb_channel;
u_int8_t mb_param;
u_int8_t res1[4];
u_int8_t mb_pad[4];
u_int32_t mb_physaddr;
u_int8_t mb_drive;
u_int8_t mb_nsgelem;
u_int8_t res2;
u_int8_t res1;
u_int8_t mb_busy;
u_int8_t mb_nstatus;
u_int8_t mb_completed[46];
u_int8_t mb_poll;
u_int8_t mb_ack;
u_int8_t res3[16];
u_int8_t res4[16];
} __attribute__ ((packed));
struct amr_sgentry
@ -196,4 +407,138 @@ struct amr_sgentry
u_int32_t sg_count;
} __attribute__ ((packed));
struct amr_passthrough
{
u_int8_t ap_timeout:3;
u_int8_t ap_ars:1;
u_int8_t ap_dummy:3;
u_int8_t ap_islogical:1;
u_int8_t ap_logical_drive_no;
u_int8_t ap_channel;
u_int8_t ap_scsi_id;
u_int8_t ap_queue_tag;
u_int8_t ap_queue_action;
u_int8_t ap_cdb[AMR_MAX_CDB_LEN];
u_int8_t ap_cdb_length;
u_int8_t ap_request_sense_length;
u_int8_t ap_request_sense_area[AMR_MAX_REQ_SENSE_LEN];
u_int8_t ap_no_sg_elements;
u_int8_t ap_scsi_status;
u_int32_t ap_data_transfer_address;
u_int32_t ap_data_transfer_length;
} __attribute__ ((packed));
#ifdef _KERNEL
/********************************************************************************
********************************************************************************
"Quartz" i960 PCI bridge interface
********************************************************************************
********************************************************************************/
#define AMR_CFG_SIG 0xa0 /* PCI config register for signature */
#define AMR_SIGNATURE 0x3344 /* signature for Quartz adapters */
/*
* Doorbell registers
*/
#define AMR_QIDB 0x20
#define AMR_QODB 0x2c
#define AMR_QIDB_SUBMIT 0x00000001 /* mailbox ready for work */
#define AMR_QIDB_ACK 0x00000002 /* mailbox done */
#define AMR_QODB_READY 0x10001234 /* work ready to be processed */
/*
* Initialisation status
*/
#define AMR_QINIT_SCAN 0x01 /* init scanning drives */
#define AMR_QINIT_SCANINIT 0x02 /* init scanning initialising */
#define AMR_QINIT_FIRMWARE 0x03 /* init firmware initing */
#define AMR_QINIT_INPROG 0xdc /* init in progress */
#define AMR_QINIT_SPINUP 0x2c /* init spinning drives */
#define AMR_QINIT_NOMEM 0xac /* insufficient memory */
#define AMR_QINIT_CACHEFLUSH 0xbc /* init flushing cache */
#define AMR_QINIT_DONE 0x9c /* init successfully done */
/*
* I/O primitives
*/
#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)
#ifdef AMR_BOARD_INIT
#define AMR_QRESET(sc) \
do { \
pci_write_config((sc)->amr_dev, 0x40, pci_read_config((sc)->amr_dev, 0x40, 1) | 0x20, 1); \
pci_write_config((sc)->amr_dev, 0x64, 0x1122, 1); \
} while (0)
#define AMR_QGET_INITSTATUS(sc) pci_read_config((sc)->amr_dev, 0x9c, 1)
#define AMR_QGET_INITCHAN(sc) pci_read_config((sc)->amr_dev, 0x9f, 1)
#define AMR_QGET_INITTARG(sc) pci_read_config((sc)->amr_dev, 0x9e, 1)
#endif
/********************************************************************************
********************************************************************************
"Standard" old-style ASIC bridge interface
********************************************************************************
********************************************************************************/
/*
* I/O registers
*/
#define AMR_SCMD 0x10 /* command/ack register (write) */
#define AMR_SMBOX_BUSY 0x10 /* mailbox status (read) */
#define AMR_STOGGLE 0x11 /* interrupt enable bit here */
#define AMR_SMBOX_0 0x14 /* mailbox physical address low byte */
#define AMR_SMBOX_1 0x15
#define AMR_SMBOX_2 0x16
#define AMR_SMBOX_3 0x17 /* high byte */
#define AMR_SMBOX_ENABLE 0x18 /* atomic mailbox address enable */
#define AMR_SINTR 0x1a /* interrupt status */
/*
* I/O magic numbers
*/
#define AMR_SCMD_POST 0x10 /* -> SCMD to initiate action on mailbox */
#define AMR_SCMD_ACKINTR 0x08 /* -> SCMD to ack mailbox retrieved */
#define AMR_STOGL_IENABLE 0xc0 /* in STOGGLE */
#define AMR_SINTR_VALID 0x40 /* in SINTR */
#define AMR_SMBOX_BUSYFLAG 0x10 /* in SMBOX_BUSY */
#define AMR_SMBOX_ADDR 0x00 /* -> SMBOX_ENABLE */
/*
* Initialisation status
*/
#define AMR_SINIT_ABEND 0xee /* init abnormal terminated */
#define AMR_SINIT_NOMEM 0xca /* insufficient memory */
#define AMR_SINIT_CACHEFLUSH 0xbb /* firmware flushing cache */
#define AMR_SINIT_INPROG 0x11 /* init in progress */
#define AMR_SINIT_SPINUP 0x22 /* firmware spinning drives */
#define AMR_SINIT_DONE 0x99 /* init successfully done */
/*
* I/O primitives
*/
#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)
#ifdef AMR_BOARD_INIT
#define AMR_SRESET(sc) bus_space_write_1(sc->amr_btag, sc->amr_bhandle, 0, 0x80)
#define AMR_SGET_INITSTATUS(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE)
#define AMR_SGET_FAILDRIVE(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE + 1)
#define AMR_SGET_INITCHAN(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE + 2)
#define AMR_SGET_INITTARG(sc) bus_space_read_1 (sc->amr_btag, sc->amr_bhandle, AMR_SMBOX_ENABLE + 3)
#endif
#endif _KERNEL

View file

@ -1,5 +1,6 @@
/*-
* Copyright (c) 1999 Michael Smith
* Copyright (c) 1999,2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,24 +27,18 @@
* $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
#if __FreeBSD_version >= 500005
# include <sys/taskqueue.h>
#endif
#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;
#ifdef AMR_DEBUG
# define debug(level, fmt, args...) do {if (level <= AMR_DEBUG) printf("%s: " fmt "\n", __FUNCTION__ , ##args);} while(0)
# define debug_called(level) do {if (level <= AMR_DEBUG) printf("%s: called\n", __FUNCTION__);} while(0)
#else
# define debug(level, fmt, args...)
# define debug_called(level)
#endif
#define xdebug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
/*
* Per-logical-drive datastructure
@ -63,6 +58,16 @@ struct amr_logdrive
device_t al_disk;
};
/*
* Due to the difficulty of using the zone allocator to create a new
* zone from within a module, we use our own clustering to reduce
* memory wastage due to allocating lots of these small structures.
*
* 16k gives us a little under 200 command structures, which should
* normally be plenty. We will grab more if we need them.
*/
#define AMR_CMD_CLUSTERSIZE (16 * 1024)
/*
* Per-command control structure.
@ -73,28 +78,45 @@ struct amr_command
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
int ac_status; /* command completion status */
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;
#define AMR_CMD_CCB_DATAIN (1<<2)
#define AMR_CMD_CCB_DATAOUT (1<<3)
#define AMR_CMD_PRIORITY (1<<4)
#define AMR_CMD_MAPPED (1<<5)
#define AMR_CMD_SLEEP (1<<6)
#define AMR_CMD_BUSY (1<<7)
struct bio *ac_bio;
void *ac_data;
size_t ac_length;
bus_dmamap_t ac_dmamap;
u_int32_t ac_dataphys;
void *ac_ccb_data;
size_t ac_ccb_length;
bus_dmamap_t ac_ccb_dmamap;
u_int32_t ac_ccb_dataphys;
void (* ac_complete)(struct amr_command *ac);
void *ac_private;
};
struct amr_command_cluster
{
TAILQ_ENTRY(amr_command_cluster) acc_link;
struct amr_command acc_command[0];
};
#define AMR_CMD_CLUSTERCOUNT ((AMR_CMD_CLUSTERSIZE - sizeof(struct amr_command_cluster)) / \
sizeof(struct amr_command))
/*
* Per-controller-instance data
*/
struct amr_softc
{
/* bus attachments */
@ -123,101 +145,74 @@ struct amr_softc
/* controller limits and features */
int amr_maxio; /* maximum number of I/O transactions */
int amr_maxdrives; /* max number of logical drives */
int amr_maxchan; /* count of SCSI channels */
/* connected logical drives */
struct amr_logdrive amr_drive[AMR_MAXLD];
/* controller status */
/* controller state */
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 bio_queue_head amr_bioq; /* pending I/O */
int amr_waitbufs;
struct bio_queue_head amr_bioq; /* pending I/O with no commands */
TAILQ_HEAD(,amr_command) amr_ready; /* commands ready to be submitted */
struct amr_command *amr_busycmd[AMR_MAXCMD];
int amr_busycmdcount;
TAILQ_HEAD(,amr_command) amr_work;
int amr_workcount;
int amr_busyslots;
TAILQ_HEAD(,amr_command) amr_completed;
TAILQ_HEAD(,amr_command) amr_freecmds;
TAILQ_HEAD(,amr_command_cluster) amr_cmd_clusters;
int amr_locks; /* reentrancy avoidance */
/* CAM attachments for passthrough */
struct cam_sim *amr_cam_sim[AMR_MAX_CHANNELS];
TAILQ_HEAD(, ccb_hdr) amr_cam_ccbq;
/* control device */
dev_t amr_dev_t;
/* 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);
#define AMR_TYPE_QUARTZ (1<<0)
#define AMR_IS_QUARTZ(sc) ((sc)->amr_type & AMR_TYPE_QUARTZ)
#define AMR_TYPE_40LD (1<<1)
#define AMR_IS_40LD(sc) ((sc)->amr_type & AMR_TYPE_40LD)
int (* 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);
/* misc glue */
struct intr_config_hook amr_ich; /* wait-for-interrupts probe hook */
struct callout_handle amr_timeout; /* periodic status check */
#if __FreeBSD_version >= 500005
struct task amr_task_complete; /* deferred-completion task */
#endif
};
/*
* 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 void amr_free(struct amr_softc *sc);
extern int amr_flush(struct amr_softc *sc);
extern int amr_done(struct amr_softc *sc);
extern void amr_startio(struct amr_softc *sc);
extern devclass_t amr_devclass;
extern devclass_t amr_devclass;
/*
* Command buffer allocation.
*/
extern struct amr_command *amr_alloccmd(struct amr_softc *sc);
extern void amr_releasecmd(struct amr_command *ac);
/*
* CAM interface
*/
extern int amr_cam_attach(struct amr_softc *sc);
extern void amr_cam_detach(struct amr_softc *sc);
extern int amr_cam_command(struct amr_softc *sc, struct amr_command **acp);
/*
* MegaRAID logical disk driver
@ -233,15 +228,116 @@ struct amrd_softc
struct disklabel amrd_label;
int amrd_unit;
int amrd_flags;
#define AMRD_OPEN (1<<0) /* drive is open (can't shut down) */
#define AMRD_OPEN (1<<0) /* drive is open (can't detach) */
};
/*
* Interface between driver core and disk driver (should be using a bus?)
*/
extern int amr_submit_buf(struct amr_softc *sc, struct bio *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 int amr_submit_bio(struct amr_softc *sc, struct bio *bio);
extern void amrd_intr(void *data);
extern void amr_report(void);
/********************************************************************************
* Enqueue/dequeue functions
*/
static __inline void
amr_enqueue_bio(struct amr_softc *sc, struct bio *bio)
{
int s;
s = splbio();
bioq_insert_tail(&sc->amr_bioq, bio);
splx(s);
}
static __inline struct bio *
amr_dequeue_bio(struct amr_softc *sc)
{
struct bio *bio;
int s;
s = splbio();
if ((bio = bioq_first(&sc->amr_bioq)) != NULL)
bioq_remove(&sc->amr_bioq, bio);
splx(s);
return(bio);
}
static __inline void
amr_enqueue_ready(struct amr_command *ac)
{
int s;
s = splbio();
TAILQ_INSERT_TAIL(&ac->ac_sc->amr_ready, ac, ac_link);
splx(s);
}
static __inline void
amr_requeue_ready(struct amr_command *ac)
{
int s;
s = splbio();
TAILQ_INSERT_HEAD(&ac->ac_sc->amr_ready, ac, ac_link);
splx(s);
}
static __inline struct amr_command *
amr_dequeue_ready(struct amr_softc *sc)
{
struct amr_command *ac;
int s;
s = splbio();
if ((ac = TAILQ_FIRST(&sc->amr_ready)) != NULL)
TAILQ_REMOVE(&sc->amr_ready, ac, ac_link);
splx(s);
return(ac);
}
static __inline void
amr_enqueue_completed(struct amr_command *ac)
{
int s;
s = splbio();
TAILQ_INSERT_TAIL(&ac->ac_sc->amr_completed, ac, ac_link);
splx(s);
}
static __inline struct amr_command *
amr_dequeue_completed(struct amr_softc *sc)
{
struct amr_command *ac;
int s;
s = splbio();
if ((ac = TAILQ_FIRST(&sc->amr_completed)) != NULL)
TAILQ_REMOVE(&sc->amr_completed, ac, ac_link);
splx(s);
return(ac);
}
static __inline void
amr_enqueue_free(struct amr_command *ac)
{
int s;
s = splbio();
TAILQ_INSERT_TAIL(&ac->ac_sc->amr_freecmds, ac, ac_link);
splx(s);
}
static __inline struct amr_command *
amr_dequeue_free(struct amr_softc *sc)
{
struct amr_command *ac;
int s;
s = splbio();
if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL)
TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
splx(s);
return(ac);
}

View file

@ -2,6 +2,16 @@
.PATH: ${.CURDIR}/../../dev/amr
KMOD = amr
SRCS = amr.c amr_pci.c amr_disk.c device_if.h bus_if.h pci_if.h
SRCS = amr.c amr_pci.c amr_disk.c device_if.h bus_if.h pci_if.h
# SCSI passthrough support for non-disk devices
#CFLAGS += -DAMR_SCSI_PASSTHROUGH
#SRCS += amr_cam.c opt_cam.h opt_scsi.h
# Enable a questionable optimisation for newer adapters
#CFLAGS += -DAMR_QUARTZ_GOFASTER
# Debugging
#CFLAGS += -DAMR_DEBUG=3
.include <bsd.kmod.mk>