diff options
Diffstat (limited to 'sys/dev/mlx/mlx.c')
-rw-r--r-- | sys/dev/mlx/mlx.c | 562 |
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 |