summaryrefslogtreecommitdiffstats
path: root/sys/dev/mlx/mlx.c
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>2000-03-18 02:01:37 +0000
committermsmith <msmith@FreeBSD.org>2000-03-18 02:01:37 +0000
commitef7f889e6b8b1ac3dae87ea74d30099c859698e6 (patch)
treee3c9f1e13fe884e56e8cc25ee9f32037f4a29176 /sys/dev/mlx/mlx.c
parent29a5e54ca95e2cd3233dce4164ec4bd5b5554a1b (diff)
downloadFreeBSD-src-ef7f889e6b8b1ac3dae87ea74d30099c859698e6.zip
FreeBSD-src-ef7f889e6b8b1ac3dae87ea74d30099c859698e6.tar.gz
Update to the latest development version of the Mylex driver. Changes in
this version include: - Support for version 2.x firmware (2.42 or later recommended). This means we are the only open-source driver supporting these adapters. This code has only been tested on a Digital KZPCA adapter in an Alpha system, but is believed to be correct. NOTE: EISA adapters are not yet supported. - Support the BIOS/Firmware initialisation handshake protocol. This allows the driver to bring the card up to operational state even if the BIOS can't be run (eg. if it's an x86 BIOS in an Alpha system). - A working command pasthrough interface. This allows a user-space configuration tool (under development) to issue arbitrary commands to the controller or to devices in the system.
Diffstat (limited to 'sys/dev/mlx/mlx.c')
-rw-r--r--sys/dev/mlx/mlx.c562
1 files changed, 438 insertions, 124 deletions
diff --git a/sys/dev/mlx/mlx.c b/sys/dev/mlx/mlx.c
index 8760ccd..3e33bfa 100644
--- a/sys/dev/mlx/mlx.c
+++ b/sys/dev/mlx/mlx.c
@@ -40,6 +40,7 @@
#include <sys/conf.h>
#include <sys/devicestat.h>
#include <sys/disk.h>
+#include <sys/stat.h>
#include <machine/resource.h>
#include <machine/bus.h>
@@ -50,12 +51,6 @@
#include <dev/mlx/mlxvar.h>
#include <dev/mlx/mlxreg.h>
-#if 0
-#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
-#else
-#define debug(fmt, args...)
-#endif
-
#define MLX_CDEV_MAJOR 130
static struct cdevsw mlx_cdevsw = {
@@ -75,7 +70,6 @@ static struct cdevsw mlx_cdevsw = {
/* bmaj */ -1
};
-static int cdev_registered = 0;
devclass_t mlx_devclass;
/*
@@ -84,14 +78,17 @@ devclass_t mlx_devclass;
static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
static void mlx_v3_intaction(struct mlx_softc *sc, int action);
+static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
static void mlx_v4_intaction(struct mlx_softc *sc, int action);
+static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
static int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
static int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
static void mlx_v5_intaction(struct mlx_softc *sc, int action);
+static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
/*
* Status monitoring
@@ -143,7 +140,7 @@ static void mlx_complete(struct mlx_softc *sc);
*/
static char *mlx_diagnose_command(struct mlx_command *mc);
static void mlx_describe_controller(struct mlx_softc *sc);
-
+static int mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
/*
* Utility functions.
@@ -166,7 +163,7 @@ mlx_free(struct mlx_softc *sc)
{
struct mlx_command *mc;
- debug("called");
+ debug_called(1);
/* cancel status timeout */
untimeout(mlx_periodic, sc, sc->mlx_timeout);
@@ -205,6 +202,10 @@ mlx_free(struct mlx_softc *sc)
/* free controller enquiry data */
if (sc->mlx_enq2 != NULL)
free(sc->mlx_enq2, M_DEVBUF);
+
+ /* destroy control device */
+ if (sc->mlx_dev_t != (dev_t)NULL)
+ destroy_dev(sc->mlx_dev_t);
}
/********************************************************************************
@@ -215,7 +216,7 @@ mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct mlx_softc *sc = (struct mlx_softc *)arg;
- debug("called");
+ debug_called(1);
/* save base of s/g table's address in bus space */
sc->mlx_sgbusaddr = segs->ds_addr;
@@ -227,7 +228,7 @@ mlx_sglist_map(struct mlx_softc *sc)
size_t segsize;
int error;
- debug("called");
+ debug_called(1);
/* destroy any existing mappings */
if (sc->mlx_sgtable)
@@ -239,7 +240,7 @@ mlx_sglist_map(struct mlx_softc *sc)
* Create a single tag describing a region large enough to hold all of
* the s/g lists we will need.
*/
- segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * sc->mlx_maxiop;
+ segsize = sizeof(struct mlx_sgentry) * sc->mlx_sg_nseg * sc->mlx_maxiop;
error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */
1, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR, /* lowaddr */
@@ -277,9 +278,10 @@ mlx_sglist_map(struct mlx_softc *sc)
int
mlx_attach(struct mlx_softc *sc)
{
- int rid, error, fwminor;
+ struct mlx_enquiry_old *meo;
+ int rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
- debug("called");
+ debug_called(1);
/*
* Initialise per-controller queues.
@@ -292,20 +294,27 @@ mlx_attach(struct mlx_softc *sc)
* Select accessor methods based on controller interface type.
*/
switch(sc->mlx_iftype) {
+ case MLX_IFTYPE_2:
case MLX_IFTYPE_3:
sc->mlx_tryqueue = mlx_v3_tryqueue;
sc->mlx_findcomplete = mlx_v3_findcomplete;
sc->mlx_intaction = mlx_v3_intaction;
+ sc->mlx_fw_handshake = mlx_v3_fw_handshake;
+ sc->mlx_sg_nseg = MLX_NSEG_OLD;
break;
case MLX_IFTYPE_4:
sc->mlx_tryqueue = mlx_v4_tryqueue;
sc->mlx_findcomplete = mlx_v4_findcomplete;
sc->mlx_intaction = mlx_v4_intaction;
+ sc->mlx_fw_handshake = mlx_v4_fw_handshake;
+ sc->mlx_sg_nseg = MLX_NSEG_NEW;
break;
case MLX_IFTYPE_5:
sc->mlx_tryqueue = mlx_v5_tryqueue;
sc->mlx_findcomplete = mlx_v5_findcomplete;
sc->mlx_intaction = mlx_v5_intaction;
+ sc->mlx_fw_handshake = mlx_v5_fw_handshake;
+ sc->mlx_sg_nseg = MLX_NSEG_NEW;
break;
default:
device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype);
@@ -316,6 +325,32 @@ mlx_attach(struct mlx_softc *sc)
sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
/*
+ * Wait for the controller to come ready, handshake with the firmware if required.
+ * This is typically only necessary on platforms where the controller BIOS does not
+ * run.
+ */
+ hsmsg = 0;
+ DELAY(1000);
+ while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) {
+ /* report first time around... */
+ if (hsmsg == 0) {
+ device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
+ hsmsg = 1;
+ }
+ /* did we get a real message? */
+ if (hscode == 2) {
+ hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
+ /* fatal initialisation error? */
+ if (hscode != 0) {
+ mlx_free(sc);
+ return(ENXIO);
+ }
+ }
+ }
+ if (hsmsg == 1)
+ device_printf(sc->mlx_dev, "initialisation complete.\n");
+
+ /*
* Allocate and connect our interrupt.
*/
rid = 0;
@@ -335,14 +370,14 @@ mlx_attach(struct mlx_softc *sc)
/*
* Create DMA tag for mapping buffers into controller-addressable space.
*/
- error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */
- 1, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */
- BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
- 0, /* flags */
+ error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MAXBSIZE, sc->mlx_sg_nseg, /* maxsize, nsegments */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
+ 0, /* flags */
&sc->mlx_buffer_dmat);
if (error != 0) {
device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
@@ -370,14 +405,26 @@ mlx_attach(struct mlx_softc *sc)
*/
sc->mlx_lastevent = -1;
- /* print a little information about the controller */
- mlx_describe_controller(sc);
-
/*
* Do quirk/feature related things.
*/
fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
switch(sc->mlx_iftype) {
+ case MLX_IFTYPE_2:
+ /* These controllers don't report the firmware version in the ENQUIRY2 response */
+ if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
+ device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
+ return(ENXIO);
+ }
+ sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
+ free(meo, M_DEVBUF);
+
+ /* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */
+ if (meo->me_fwminor < 42) {
+ device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
+ device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
+ }
+ break;
case MLX_IFTYPE_3:
/* XXX certify 3.52? */
if (fwminor < 51) {
@@ -421,16 +468,19 @@ mlx_attach(struct mlx_softc *sc)
sc->mlx_check = -1;
/*
- * Register the control device on first attach.
+ * Create the control device.
*/
- if (cdev_registered++ == 0)
- cdevsw_add(&mlx_cdevsw);
+ sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR,
+ S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
/*
* Start the timeout routine.
*/
sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
+ /* print a little information about the controller */
+ mlx_describe_controller(sc);
+
return(0);
}
@@ -444,7 +494,7 @@ mlx_startup(struct mlx_softc *sc)
struct mlx_sysdrive *dr;
int i, error;
- debug("called");
+ debug_called(1);
/*
* Scan all the system drives and attach children for those that
@@ -504,7 +554,7 @@ mlx_detach(device_t dev)
struct mlxd_softc *mlxd;
int i, s, error;
- debug("called");
+ debug_called(1);
error = EBUSY;
s = splbio();
@@ -525,11 +575,6 @@ mlx_detach(device_t dev)
mlx_free(sc);
- /*
- * Deregister the control device on last detach.
- */
- if (--cdev_registered == 0)
- cdevsw_remove(&mlx_cdevsw);
error = 0;
out:
splx(s);
@@ -552,7 +597,7 @@ mlx_shutdown(device_t dev)
struct mlx_softc *sc = device_get_softc(dev);
int i, s, error;
- debug("called");
+ debug_called(1);
s = splbio();
error = 0;
@@ -591,7 +636,7 @@ mlx_suspend(device_t dev)
struct mlx_softc *sc = device_get_softc(dev);
int s;
- debug("called");
+ debug_called(1);
s = splbio();
sc->mlx_state |= MLX_STATE_SUSPEND;
@@ -614,7 +659,7 @@ mlx_resume(device_t dev)
{
struct mlx_softc *sc = device_get_softc(dev);
- debug("called");
+ debug_called(1);
sc->mlx_state &= ~MLX_STATE_SUSPEND;
sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
@@ -631,7 +676,7 @@ mlx_intr(void *arg)
{
struct mlx_softc *sc = (struct mlx_softc *)arg;
- debug("called");
+ debug_called(1);
/* collect finished commands, queue anything waiting */
mlx_done(sc);
@@ -646,7 +691,7 @@ mlx_submit_buf(struct mlx_softc *sc, struct buf *bp)
{
int s;
- debug("called");
+ debug_called(1);
s = splbio();
bufq_insert_tail(&sc->mlx_bufq, bp);
@@ -907,7 +952,7 @@ mlx_periodic(void *data)
{
struct mlx_softc *sc = (struct mlx_softc *)data;
- debug("called");
+ debug_called(1);
/*
* Run a bus pause?
@@ -946,7 +991,8 @@ mlx_periodic(void *data)
*
* XXX Note that this may not actually launch a command in situations of high load.
*/
- mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(struct mlx_enquiry), mlx_periodic_enquiry);
+ mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
+ imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
/*
* Check system drive status.
@@ -980,17 +1026,55 @@ mlx_periodic_enquiry(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
- debug("called");
+ debug_called(1);
/* Command completed OK? */
if (mc->mc_status != 0) {
- device_printf(sc->mlx_dev, "periodic enquiry failed\n");
+ device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
goto out;
}
/* respond to command */
switch(mc->mc_mailbox[0]) {
/*
+ * This is currently a bit fruitless, as we don't know how to extract the eventlog
+ * pointer yet.
+ */
+ case MLX_CMD_ENQUIRY_OLD:
+ {
+ struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data;
+ struct mlx_enquiry_old *meo = (struct mlx_enquiry_old *)mc->mc_data;
+ int i;
+
+ /* convert data in-place to new format */
+ for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
+ me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
+ me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
+ }
+ me->me_misc_flags = 0;
+ me->me_rebuild_count = meo->me_rebuild_count;
+ me->me_dead_count = meo->me_dead_count;
+ me->me_critical_sd_count = meo->me_critical_sd_count;
+ me->me_event_log_seq_num = 0;
+ me->me_offline_sd_count = meo->me_offline_sd_count;
+ me->me_max_commands = meo->me_max_commands;
+ me->me_rebuild_flag = meo->me_rebuild_flag;
+ me->me_fwmajor = meo->me_fwmajor;
+ me->me_fwminor = meo->me_fwminor;
+ me->me_status_flags = meo->me_status_flags;
+ me->me_flash_age = meo->me_flash_age;
+ for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
+ if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
+ me->me_drvsize[i] = 0; /* drive beyond supported range */
+ } else {
+ me->me_drvsize[i] = meo->me_drvsize[i];
+ }
+ }
+ me->me_num_sys_drvs = meo->me_num_sys_drvs;
+ }
+ /* FALLTHROUGH */
+
+ /*
* Generic controller status update. We could do more with this than just
* checking the event log.
*/
@@ -1004,8 +1088,7 @@ mlx_periodic_enquiry(struct mlx_command *mc)
} else if (me->me_event_log_seq_num != sc->mlx_lastevent) {
/* record where current events are up to */
sc->mlx_currevent = me->me_event_log_seq_num;
- device_printf(sc->mlx_dev, "event log pointer was %d, now %d\n",
- sc->mlx_lastevent, sc->mlx_currevent);
+ debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
/* drain new eventlog entries */
mlx_periodic_eventlog_poll(sc);
@@ -1065,7 +1148,7 @@ mlx_periodic_eventlog_poll(struct mlx_softc *sc)
void *result = NULL;
int error;
- debug("called");
+ debug_called(1);
/* get ourselves a command buffer */
error = 1;
@@ -1129,7 +1212,7 @@ mlx_periodic_eventlog_respond(struct mlx_command *mc)
struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data;
char *reason;
- debug("called");
+ debug_called(1);
sc->mlx_lastevent++; /* next message... */
if (mc->mc_status == 0) {
@@ -1321,7 +1404,7 @@ mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)
void *result;
int error;
- debug("called");
+ debug_called(1);
/* get ourselves a command buffer */
error = 1;
@@ -1387,7 +1470,7 @@ mlx_flush(struct mlx_softc *sc)
struct mlx_command *mc;
int error;
- debug("called");
+ debug_called(1);
/* get ourselves a command buffer */
error = 1;
@@ -1429,7 +1512,7 @@ mlx_rebuild(struct mlx_softc *sc, int channel, int target)
struct mlx_command *mc;
int error;
- debug("called");
+ debug_called(1);
/* get ourselves a command buffer */
error = 0x10000;
@@ -1471,7 +1554,7 @@ mlx_wait_command(struct mlx_command *mc)
struct mlx_softc *sc = mc->mc_sc;
int error, count;
- debug("called");
+ debug_called(1);
mc->mc_complete = NULL;
mc->mc_private = mc; /* wake us when you're done */
@@ -1485,7 +1568,7 @@ mlx_wait_command(struct mlx_command *mc)
}
if (mc->mc_status != 0) {
- device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
+ device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
return(EIO);
}
return(0);
@@ -1495,7 +1578,7 @@ mlx_wait_command(struct mlx_command *mc)
/********************************************************************************
* Start the command (mc) and busy-wait for it to complete.
*
- * Should only be used when interrupts are not available. Returns 0 on
+ * Should only be used when interrupts can't be relied upon. Returns 0 on
* success, nonzero on error.
* Successfully completed commands are dequeued.
*/
@@ -1505,7 +1588,7 @@ mlx_poll_command(struct mlx_command *mc)
struct mlx_softc *sc = mc->mc_sc;
int error, count, s;
- debug("called");
+ debug_called(1);
mc->mc_complete = NULL;
mc->mc_private = NULL; /* we will poll for it */
@@ -1516,7 +1599,8 @@ mlx_poll_command(struct mlx_command *mc)
do {
/* poll for completion */
mlx_done(mc->mc_sc);
- } while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 10000));
+
+ } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
if (mc->mc_status != MLX_STATUS_BUSY) {
s = splbio();
TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
@@ -1532,7 +1616,7 @@ mlx_poll_command(struct mlx_command *mc)
* controller. Leave a couple of slots free for emergencies.
*
* Must be called at splbio or in an equivalent fashion that prevents
- * reentry or activity on the bufq..
+ * reentry or activity on the bufq.
*/
static void
mlx_startio(struct mlx_softc *sc)
@@ -1576,10 +1660,10 @@ mlx_startio(struct mlx_softc *sc)
mc->mc_length = bp->b_bcount;
if (bp->b_flags & B_READ) {
mc->mc_flags |= MLX_CMD_DATAIN;
- cmd = MLX_CMD_READOLDSG;
+ cmd = MLX_CMD_READSG;
} else {
mc->mc_flags |= MLX_CMD_DATAOUT;
- cmd = MLX_CMD_WRITEOLDSG;
+ cmd = MLX_CMD_WRITESG;
}
/* map the command so the controller can work with it */
@@ -1598,14 +1682,22 @@ mlx_startio(struct mlx_softc *sc)
* Build the I/O command. Note that the SG list type bits are set to zero,
* denoting the format of SG list that we are using.
*/
- mlx_make_type5(mc, cmd,
- blkcount & 0xff, /* xfer length low byte */
- (driveno << 3) | ((blkcount >> 8) & 0x07), /* target and length high 3 bits */
- bp->b_pblkno, /* physical block number */
- mc->mc_sgphys, /* location of SG list */
- mc->mc_nsgent & 0x3f); /* size of SG list (top 2 bits clear) */
+ if (sc->mlx_iftype == MLX_IFTYPE_2) {
+ mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD,
+ blkcount & 0xff, /* xfer length low byte */
+ bp->b_pblkno, /* physical block number */
+ driveno, /* target drive number */
+ mc->mc_sgphys, /* location of SG list */
+ mc->mc_nsgent & 0x3f); /* size of SG list (top 3 bits clear) */
+ } else {
+ mlx_make_type5(mc, cmd,
+ blkcount & 0xff, /* xfer length low byte */
+ (driveno << 3) | ((blkcount >> 8) & 0x07), /* target and length high 3 bits */
+ bp->b_pblkno, /* physical block number */
+ mc->mc_sgphys, /* location of SG list */
+ mc->mc_nsgent & 0x3f); /* size of SG list (top 3 bits clear) */
+ }
-
/* try to give command to controller */
if (mlx_start(mc) != 0) {
/* fail the command */
@@ -1655,51 +1747,79 @@ mlx_completeio(struct mlx_command *mc)
/********************************************************************************
* Take a command from user-space and try to run it.
+ *
+ * XXX Note that this can't perform very much in the way of error checking, and
+ * as such, applications _must_ be considered trustworthy.
+ * XXX Commands using S/G for data are not supported.
*/
static int
mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
{
struct mlx_command *mc;
+ struct mlx_dcdb *dcdb;
void *kbuf;
int error;
+ debug_called(0);
+
kbuf = NULL;
mc = NULL;
+ dcdb = NULL;
error = ENOMEM;
- /* get a kernel buffer for the transfer */
- if (mu->mu_datasize > 0) {
- if ((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL)
- goto out;
- if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) < sizeof(u_int32_t)))) {
- error = EINVAL;
- goto out;
- }
- }
- /* get ourselves a command buffer */
+
+ /* get ourselves a command and copy in from user space */
if ((mc = mlx_alloccmd(sc)) == NULL)
goto out;
-
- /* copy the command and data */
bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
- if ((mu->mu_datasize > 0) && ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))))
- goto out;
+ debug(0, "got command buffer");
+
+ /* if we need a buffer for data transfer, allocate one and copy in its initial contents */
+ if (mu->mu_datasize > 0) {
+ if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
+ (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))
+ goto out;
+ debug(0, "got kernel buffer");
+ }
/* get a command slot */
if (mlx_getslot(mc))
goto out;
-
+ debug(0, "got a slot");
+
/* map the command so the controller can see it */
mc->mc_data = kbuf;
mc->mc_length = mu->mu_datasize;
mlx_mapcmd(mc);
+ debug(0, "mapped");
- /* if there's a data buffer, fix up the command */
+ /*
+ * If this is a passthrough SCSI command, the DCDB is packed at the
+ * beginning of the data area. Fix up the DCDB to point to the correct physical
+ * address and override any bufptr supplied by the caller since we know
+ * what it's meant to be.
+ */
+ if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
+ dcdb = (struct mlx_dcdb *)kbuf;
+ dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
+ mu->mu_bufptr = 8;
+ }
+
+ /*
+ * If there's a data buffer, fix up the command's buffer pointer.
+ */
if (mu->mu_datasize > 0) {
- mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_length & 0xff;
- mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_length >> 8) & 0xff;
- mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_length >> 16) & 0xff;
- mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_length >> 24) & 0xff;
+
+ /* range check the pointer to physical buffer address */
+ if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - sizeof(u_int32_t)))) {
+ error = EINVAL;
+ goto out;
+ }
+ mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_dataphys & 0xff;
+ mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8) & 0xff;
+ mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
+ mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
}
+ debug(0, "command fixup");
/* submit the command and wait */
if ((error = mlx_wait_command(mc)) != 0)
@@ -1736,7 +1856,7 @@ mlx_getslot(struct mlx_command *mc)
struct mlx_softc *sc = mc->mc_sc;
int s, slot;
- debug("called mc %p sc %p", mc, sc);
+ debug_called(1);
/* enforce slot-usage limit */
if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ?
@@ -1750,7 +1870,7 @@ mlx_getslot(struct mlx_command *mc)
*/
s = splbio();
for (slot = 0; slot < sc->mlx_maxiop; slot++) {
- debug("try slot %d", slot);
+ debug(2, "try slot %d", slot);
if (sc->mlx_busycmd[slot] == NULL)
break;
}
@@ -1764,7 +1884,7 @@ mlx_getslot(struct mlx_command *mc)
if (slot >= sc->mlx_maxiop)
return(EBUSY);
- debug("got slot %d", slot);
+ debug(2, "got slot %d", slot);
mc->mc_slot = slot;
return(0);
}
@@ -1780,14 +1900,14 @@ mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
struct mlx_sgentry *sg;
int i;
- debug("called");
+ debug_called(1);
/* get base address of s/g table */
- sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
+ sg = sc->mlx_sgtable + (mc->mc_slot * sc->mlx_sg_nseg);
/* save s/g table information in command */
mc->mc_nsgent = nsegments;
- mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
+ mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * sc->mlx_sg_nseg * sizeof(struct mlx_sgentry));
mc->mc_dataphys = segs[0].ds_addr;
/* populate s/g table */
@@ -1802,7 +1922,7 @@ mlx_mapcmd(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
- debug("called");
+ debug_called(1);
/* if the command involves data at all */
if (mc->mc_data != NULL) {
@@ -1822,7 +1942,7 @@ mlx_unmapcmd(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
- debug("called");
+ debug_called(1);
/* if the command involved data at all */
if (mc->mc_data != NULL) {
@@ -1847,7 +1967,7 @@ mlx_start(struct mlx_command *mc)
struct mlx_softc *sc = mc->mc_sc;
int i, s, done;
- debug("called");
+ debug_called(1);
/* save the slot number as ident so we can handle this command when complete */
mc->mc_mailbox[0x1] = mc->mc_slot;
@@ -1899,7 +2019,7 @@ mlx_done(struct mlx_softc *sc)
u_int8_t slot;
u_int16_t status;
- debug("called");
+ debug_called(2);
result = 0;
@@ -1947,7 +2067,7 @@ mlx_complete(struct mlx_softc *sc)
struct mlx_command *mc, *nc;
int s, count;
- debug("called");
+ debug_called(2);
/* avoid reentrancy XXX might want to signal and request a restart */
if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
@@ -2023,7 +2143,7 @@ mlx_alloccmd(struct mlx_softc *sc)
int error;
int s;
- debug("called");
+ debug_called(1);
s = splbio();
if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
@@ -2057,7 +2177,7 @@ mlx_releasecmd(struct mlx_command *mc)
{
int s;
- debug("called");
+ debug_called(1);
s = splbio();
TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
@@ -2072,8 +2192,7 @@ mlx_freecmd(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
- debug("called");
-
+ debug_called(1);
bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
free(mc, M_DEVBUF);
}
@@ -2096,7 +2215,7 @@ mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
{
int i;
- debug("called");
+ debug_called(2);
/* ready for our command? */
if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
@@ -2121,7 +2240,7 @@ static int
mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
{
- debug("called");
+ debug_called(2);
/* status available? */
if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
@@ -2144,7 +2263,7 @@ mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
static void
mlx_v3_intaction(struct mlx_softc *sc, int action)
{
- debug("called");
+ debug_called(1);
switch(action) {
case MLX_INTACTION_DISABLE:
@@ -2158,6 +2277,45 @@ mlx_v3_intaction(struct mlx_softc *sc, int action)
}
}
+/********************************************************************************
+ * Poll for firmware error codes during controller initialisation.
+ * Returns 0 if initialisation is complete, 1 if still in progress but no
+ * error has been fetched, 2 if an error has been retrieved.
+ */
+static int
+mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
+{
+ u_int8_t fwerror;
+ static int initted = 0;
+
+ debug_called(2);
+
+ /* first time around, clear any hardware completion status */
+ if (!initted) {
+ MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
+ DELAY(1000);
+ initted = 1;
+ }
+
+ /* init in progress? */
+ if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
+ return(0);
+
+ /* test error value */
+ fwerror = MLX_V3_GET_FWERROR(sc);
+ if (!(fwerror & MLX_V3_FWERROR_PEND))
+ return(1);
+
+ /* mask status pending bit, fetch status */
+ *error = fwerror & ~MLX_V3_FWERROR_PEND;
+ *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
+ *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
+
+ /* acknowledge */
+ MLX_V3_PUT_FWERROR(sc, 0);
+
+ return(2);
+}
/********************************************************************************
********************************************************************************
@@ -2176,7 +2334,7 @@ mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
{
int i;
- debug("called");
+ debug_called(2);
/* ready for our command? */
if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
@@ -2184,6 +2342,10 @@ mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
for (i = 0; i < 13; i++)
MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
+ /* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
+ bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
+ BUS_SPACE_BARRIER_WRITE);
+
/* post command */
MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
return(1);
@@ -2201,7 +2363,7 @@ static int
mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
{
- debug("called");
+ debug_called(2);
/* status available? */
if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
@@ -2224,7 +2386,7 @@ mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
static void
mlx_v4_intaction(struct mlx_softc *sc, int action)
{
- debug("called");
+ debug_called(1);
switch(action) {
case MLX_INTACTION_DISABLE:
@@ -2238,6 +2400,45 @@ mlx_v4_intaction(struct mlx_softc *sc, int action)
}
}
+/********************************************************************************
+ * Poll for firmware error codes during controller initialisation.
+ * Returns 0 if initialisation is complete, 1 if still in progress but no
+ * error has been fetched, 2 if an error has been retrieved.
+ */
+static int
+mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
+{
+ u_int8_t fwerror;
+ static int initted = 0;
+
+ debug_called(2);
+
+ /* first time around, clear any hardware completion status */
+ if (!initted) {
+ MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
+ DELAY(1000);
+ initted = 1;
+ }
+
+ /* init in progress? */
+ if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
+ return(0);
+
+ /* test error value */
+ fwerror = MLX_V4_GET_FWERROR(sc);
+ if (!(fwerror & MLX_V4_FWERROR_PEND))
+ return(1);
+
+ /* mask status pending bit, fetch status */
+ *error = fwerror & ~MLX_V4_FWERROR_PEND;
+ *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
+ *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
+
+ /* acknowledge */
+ MLX_V4_PUT_FWERROR(sc, 0);
+
+ return(2);
+}
/********************************************************************************
********************************************************************************
@@ -2255,15 +2456,15 @@ static int
mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
{
int i;
-
- debug("called");
+
+ debug_called(2);
/* ready for our command? */
if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
/* copy mailbox data to window */
for (i = 0; i < 13; i++)
MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
-
+
/* post command */
MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
return(1);
@@ -2281,7 +2482,7 @@ static int
mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
{
- debug("called");
+ debug_called(2);
/* status available? */
if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
@@ -2304,20 +2505,59 @@ mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
static void
mlx_v5_intaction(struct mlx_softc *sc, int action)
{
- debug("called");
+ debug_called(1);
switch(action) {
case MLX_INTACTION_DISABLE:
- MLX_V5_PUT_IER(sc, MLX_V5_IER_DISINT);
+ MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
sc->mlx_state &= ~MLX_STATE_INTEN;
break;
case MLX_INTACTION_ENABLE:
- MLX_V5_PUT_IER(sc, 0);
+ MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
sc->mlx_state |= MLX_STATE_INTEN;
break;
}
}
+/********************************************************************************
+ * Poll for firmware error codes during controller initialisation.
+ * Returns 0 if initialisation is complete, 1 if still in progress but no
+ * error has been fetched, 2 if an error has been retrieved.
+ */
+static int
+mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
+{
+ u_int8_t fwerror;
+ static int initted = 0;
+
+ debug_called(2);
+
+ /* first time around, clear any hardware completion status */
+ if (!initted) {
+ MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
+ DELAY(1000);
+ initted = 1;
+ }
+
+ /* init in progress? */
+ if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
+ return(0);
+
+ /* test for error value */
+ fwerror = MLX_V5_GET_FWERROR(sc);
+ if (!(fwerror & MLX_V5_FWERROR_PEND))
+ return(1);
+
+ /* mask status pending bit, fetch status */
+ *error = fwerror & ~MLX_V5_FWERROR_PEND;
+ *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
+ *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
+
+ /* acknowledge */
+ MLX_V5_PUT_FWERROR(sc, 0xff);
+
+ return(2);
+}
/********************************************************************************
********************************************************************************
@@ -2343,6 +2583,13 @@ static char *mlx_status_messages[] = {
"invalid or non-redundant drive", /* 11 */
"channel is busy", /* 12 */
"channel is not stopped", /* 13 */
+ "rebuild successfully terminated", /* 14 */
+ "unsupported command", /* 15 */
+ "check condition received", /* 16 */
+ "device is busy", /* 17 */
+ "selection or command timeout", /* 18 */
+ "command terminated abnormally", /* 19 */
+ ""
};
static struct
@@ -2351,18 +2598,25 @@ static struct
u_int16_t status;
int msg;
} mlx_messages[] = {
- {MLX_CMD_READOLDSG, 0x0001, 1},
- {MLX_CMD_READOLDSG, 0x0002, 1},
- {MLX_CMD_READOLDSG, 0x0105, 3},
- {MLX_CMD_READOLDSG, 0x010c, 4},
- {MLX_CMD_WRITEOLDSG, 0x0001, 1},
- {MLX_CMD_WRITEOLDSG, 0x0002, 1},
- {MLX_CMD_WRITEOLDSG, 0x0105, 3},
+ {MLX_CMD_READSG, 0x0001, 1},
+ {MLX_CMD_READSG, 0x0002, 1},
+ {MLX_CMD_READSG, 0x0105, 3},
+ {MLX_CMD_READSG, 0x010c, 4},
+ {MLX_CMD_WRITESG, 0x0001, 1},
+ {MLX_CMD_WRITESG, 0x0002, 1},
+ {MLX_CMD_WRITESG, 0x0105, 3},
+ {MLX_CMD_READSG_OLD, 0x0001, 1},
+ {MLX_CMD_READSG_OLD, 0x0002, 1},
+ {MLX_CMD_READSG_OLD, 0x0105, 3},
+ {MLX_CMD_WRITESG_OLD, 0x0001, 1},
+ {MLX_CMD_WRITESG_OLD, 0x0002, 1},
+ {MLX_CMD_WRITESG_OLD, 0x0105, 3},
{MLX_CMD_LOGOP, 0x0105, 5},
{MLX_CMD_REBUILDASYNC, 0x0002, 6},
{MLX_CMD_REBUILDASYNC, 0x0004, 7},
{MLX_CMD_REBUILDASYNC, 0x0105, 8},
{MLX_CMD_REBUILDASYNC, 0x0106, 9},
+ {MLX_CMD_REBUILDASYNC, 0x0107, 14},
{MLX_CMD_CHECKASYNC, 0x0002, 10},
{MLX_CMD_CHECKASYNC, 0x0105, 11},
{MLX_CMD_CHECKASYNC, 0x0106, 9},
@@ -2370,6 +2624,13 @@ static struct
{MLX_CMD_STOPCHANNEL, 0x0105, 8},
{MLX_CMD_STARTCHANNEL, 0x0005, 13},
{MLX_CMD_STARTCHANNEL, 0x0105, 8},
+ {MLX_CMD_DIRECT_CDB, 0x0002, 16},
+ {MLX_CMD_DIRECT_CDB, 0x0008, 17},
+ {MLX_CMD_DIRECT_CDB, 0x000e, 18},
+ {MLX_CMD_DIRECT_CDB, 0x000f, 19},
+ {MLX_CMD_DIRECT_CDB, 0x0105, 8},
+
+ {0, 0x0104, 14},
{-1, 0, 0}
};
@@ -2381,7 +2642,7 @@ mlx_diagnose_command(struct mlx_command *mc)
/* look up message in table */
for (i = 0; mlx_messages[i].command != -1; i++)
- if ((mc->mc_mailbox[0] == mlx_messages[i].command) &&
+ if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
(mc->mc_status == mlx_messages[i].status))
return(mlx_status_messages[mlx_messages[i].msg]);
@@ -2390,7 +2651,7 @@ mlx_diagnose_command(struct mlx_command *mc)
}
/*******************************************************************************
- * Return a string describing the controller (hwid)
+ * Print a string describing the controller (sc)
*/
static struct
{
@@ -2427,7 +2688,7 @@ mlx_describe_controller(struct mlx_softc *sc)
sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
model = buf;
}
- device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%d, %dMB RAM\n",
+ device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
model,
sc->mlx_enq2->me_actual_channels,
sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
@@ -2476,6 +2737,59 @@ mlx_describe_controller(struct mlx_softc *sc)
}
}
+/*******************************************************************************
+ * Emit a string describing the firmware handshake status code, and return a flag
+ * indicating whether the code represents a fatal error.
+ *
+ * Error code interpretations are from the Linux driver, and don't directly match
+ * the messages printed by Mylex's BIOS. This may change if documentation on the
+ * codes is forthcoming.
+ */
+static int
+mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
+{
+ switch(error) {
+ case 0x00:
+ device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
+ break;
+ case 0x08:
+ /* we could be neater about this and give some indication when we receive more of them */
+ if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
+ device_printf(sc->mlx_dev, "spinning up drives...\n");
+ sc->mlx_flags |= MLX_SPINUP_REPORTED;
+ }
+ break;
+ case 0x30:
+ device_printf(sc->mlx_dev, "configuration checksum error\n");
+ break;
+ case 0x60:
+ device_printf(sc->mlx_dev, "mirror race recovery failed\n");
+ break;
+ case 0x70:
+ device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
+ break;
+ case 0x90:
+ device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
+ break;
+ case 0xa0:
+ device_printf(sc->mlx_dev, "logical drive installation aborted\n");
+ break;
+ case 0xb0:
+ device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
+ break;
+ case 0xd0:
+ device_printf(sc->mlx_dev, "new controller configuration found\n");
+ break;
+ case 0xf0:
+ device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
+ return(1);
+ default:
+ device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
+ break;
+ }
+ return(0);
+}
+
/********************************************************************************
********************************************************************************
Utility Functions
OpenPOWER on IntegriCloud