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:
parent
429a82acc6
commit
9f1776230d
1715
sys/dev/amr/amr.c
1715
sys/dev/amr/amr.c
File diff suppressed because it is too large
Load Diff
452
sys/dev/amr/amr_cam.c
Normal file
452
sys/dev/amr/amr_cam.c
Normal 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
61
sys/dev/amr/amr_compat.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
110
sys/dev/amr/amr_tables.h
Normal 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
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user