From ef7f889e6b8b1ac3dae87ea74d30099c859698e6 Mon Sep 17 00:00:00 2001 From: msmith Date: Sat, 18 Mar 2000 02:01:37 +0000 Subject: [PATCH] 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. --- sys/dev/mlx/mlx.c | 564 ++++++++++++++++++++++++++++++++--------- sys/dev/mlx/mlx_disk.c | 24 +- sys/dev/mlx/mlx_pci.c | 32 ++- sys/dev/mlx/mlxio.h | 5 + sys/dev/mlx/mlxreg.h | 213 +++++++++++++++- sys/dev/mlx/mlxvar.h | 30 ++- 6 files changed, 705 insertions(+), 163 deletions(-) diff --git a/sys/dev/mlx/mlx.c b/sys/dev/mlx/mlx.c index 8760ccd51526..3e33bfa8e139 100644 --- a/sys/dev/mlx/mlx.c +++ b/sys/dev/mlx/mlx.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -50,12 +51,6 @@ #include #include -#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); @@ -315,6 +324,32 @@ mlx_attach(struct mlx_softc *sc) /* disable interrupts before we start talking to the controller */ 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. */ @@ -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,16 +1026,54 @@ 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 (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; + /* + * 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) { + + /* 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 diff --git a/sys/dev/mlx/mlx_disk.c b/sys/dev/mlx/mlx_disk.c index acd3d79c2c18..1dcf93bfbe3e 100644 --- a/sys/dev/mlx/mlx_disk.c +++ b/sys/dev/mlx/mlx_disk.c @@ -50,12 +50,6 @@ #include #include -#if 0 -#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) -#else -#define debug(fmt, args...) -#endif - /* prototypes */ static int mlxd_probe(device_t dev); static int mlxd_attach(device_t dev); @@ -111,7 +105,7 @@ mlxd_open(dev_t dev, int flags, int fmt, struct proc *p) struct mlxd_softc *sc = (struct mlxd_softc *)dev->si_drv1; struct disklabel *label; - debug("called"); + debug_called(1); if (sc == NULL) return (ENXIO); @@ -139,7 +133,7 @@ mlxd_close(dev_t dev, int flags, int fmt, struct proc *p) { struct mlxd_softc *sc = (struct mlxd_softc *)dev->si_drv1; - debug("called"); + debug_called(1); if (sc == NULL) return (ENXIO); @@ -153,13 +147,13 @@ mlxd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) struct mlxd_softc *sc = (struct mlxd_softc *)dev->si_drv1; int error; - debug("called"); + debug_called(1); if (sc == NULL) return (ENXIO); if ((error = mlx_submit_ioctl(sc->mlxd_controller, sc->mlxd_drive, cmd, addr, flag, p)) != ENOIOCTL) { - debug("mlx_submit_ioctl returned %d\n", error); + debug(0, "mlx_submit_ioctl returned %d\n", error); return(error); } return (ENOTTY); @@ -176,7 +170,7 @@ mlxd_strategy(struct buf *bp) { struct mlxd_softc *sc = (struct mlxd_softc *)bp->b_dev->si_drv1; - debug("called"); + debug_called(1); /* bogus disk? */ if (sc == NULL) { @@ -216,7 +210,7 @@ mlxd_intr(void *data) struct buf *bp = (struct buf *)data; struct mlxd_softc *sc = (struct mlxd_softc *)bp->b_dev->si_drv1; - debug("called"); + debug_called(1); if (bp->b_flags & B_ERROR) bp->b_error = EIO; @@ -231,7 +225,7 @@ static int mlxd_probe(device_t dev) { - debug("called"); + debug_called(1); device_set_desc(dev, "Mylex System Drive"); return (0); @@ -245,7 +239,7 @@ mlxd_attach(device_t dev) char *state; dev_t dsk; - debug("called"); + debug_called(1); parent = device_get_parent(dev); sc->mlxd_controller = (struct mlx_softc *)device_get_softc(parent); @@ -291,7 +285,7 @@ mlxd_detach(device_t dev) { struct mlxd_softc *sc = (struct mlxd_softc *)device_get_softc(dev); - debug("called"); + debug_called(1); devstat_remove_entry(&sc->mlxd_stats); diff --git a/sys/dev/mlx/mlx_pci.c b/sys/dev/mlx/mlx_pci.c index 6448a5263d50..004c9aa6c7c9 100644 --- a/sys/dev/mlx/mlx_pci.c +++ b/sys/dev/mlx/mlx_pci.c @@ -50,12 +50,6 @@ #include #include -#if 0 -#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) -#else -#define debug(fmt, args...) -#endif - static int mlx_pci_probe(device_t dev); static int mlx_pci_attach(device_t dev); @@ -90,7 +84,7 @@ struct mlx_ident int iftype; char *desc; } mlx_identifiers[] = { -/* {0x1069, 0x0001, 0x0000, 0x0000, MLX_IFTYPE_2, "Mylex version 2 RAID interface"}, */ + {0x1069, 0x0001, 0x0000, 0x0000, MLX_IFTYPE_2, "Mylex version 2 RAID interface"}, {0x1069, 0x0002, 0x0000, 0x0000, MLX_IFTYPE_3, "Mylex version 3 RAID interface"}, {0x1069, 0x0010, 0x0000, 0x0000, MLX_IFTYPE_4, "Mylex version 4 RAID interface"}, {0x1011, 0x1065, 0x1069, 0x0020, MLX_IFTYPE_5, "Mylex version 5 RAID interface"}, @@ -102,7 +96,7 @@ mlx_pci_probe(device_t dev) { struct mlx_ident *m; - debug("called"); + debug_called(1); for (m = mlx_identifiers; m->vendor != 0; m++) { if ((m->vendor == pci_get_vendor(dev)) && @@ -124,16 +118,19 @@ mlx_pci_attach(device_t dev) int i, rid, error; u_int32_t command; - debug("called"); + debug_called(1); /* * Make sure we are going to be able to talk to this board. */ - command = pci_read_config(dev, PCIR_COMMAND, 1); + command = pci_read_config(dev, PCIR_COMMAND, 2); if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "memory window not available\n"); return(ENXIO); } + /* force the busmaster enable bit on */ + command |= PCIM_CMD_BUSMASTEREN; + pci_write_config(dev, PCIR_COMMAND, command, 2); /* * Initialise softc. @@ -161,8 +158,17 @@ mlx_pci_attach(device_t dev) * Allocate the PCI register window. */ - /* type 3 adapters have an I/O region we don't use at base 0 */ - rid = (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0; + /* type 2/3 adapters have an I/O region we don't use at base 0 */ + switch(sc->mlx_iftype) { + case MLX_IFTYPE_2: + case MLX_IFTYPE_3: + rid = MLX_CFG_BASE1; + break; + case MLX_IFTYPE_4: + case MLX_IFTYPE_5: + rid = MLX_CFG_BASE0; + break; + } sc->mlx_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->mlx_mem == NULL) { device_printf(sc->mlx_dev, "couldn't allocate mailbox window\n"); @@ -180,7 +186,7 @@ mlx_pci_attach(device_t dev) BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ - MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ + MAXBSIZE, MLX_NSEG_NEW, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ &sc->mlx_parent_dmat); diff --git a/sys/dev/mlx/mlxio.h b/sys/dev/mlx/mlxio.h index eabeb5047d20..984b223e8a94 100644 --- a/sys/dev/mlx/mlxio.h +++ b/sys/dev/mlx/mlxio.h @@ -79,6 +79,10 @@ struct mlx_usercommand /* command */ u_int16_t mu_status; /* command status returned */ u_int8_t mu_command[16]; /* command mailbox contents */ + + /* wrapper */ + int mu_error; /* result of submission to driver */ + }; #define MLX_NEXT_CHILD _IOWR('M', 0, int) @@ -86,3 +90,4 @@ struct mlx_usercommand #define MLX_DETACH_DRIVE _IOW ('M', 2, int) #define MLX_PAUSE_CHANNEL _IOW ('M', 3, struct mlx_pause) #define MLX_COMMAND _IOWR('M', 4, struct mlx_usercommand) + diff --git a/sys/dev/mlx/mlxreg.h b/sys/dev/mlx/mlxreg.h index d004a12c9705..c5382bffed26 100644 --- a/sys/dev/mlx/mlxreg.h +++ b/sys/dev/mlx/mlxreg.h @@ -26,19 +26,19 @@ * $FreeBSD$ */ -#define MLX_CFG_BASE0 0x10 /* first region */ -#define MLX_CFG_BASE1 0x14 /* second region (type 3 only) */ - #define MLX_BLKSIZE 512 /* fixed feature */ /* * Selected command codes. */ +#define MLX_CMD_ENQUIRY_OLD 0x05 #define MLX_CMD_ENQUIRY 0x53 #define MLX_CMD_ENQUIRY2 0x1c #define MLX_CMD_ENQSYSDRIVE 0x19 -#define MLX_CMD_READOLDSG 0xb6 -#define MLX_CMD_WRITEOLDSG 0xb7 +#define MLX_CMD_READSG 0xb6 +#define MLX_CMD_WRITESG 0xb7 +#define MLX_CMD_READSG_OLD 0x82 +#define MLX_CMD_WRITESG_OLD 0x83 #define MLX_CMD_FLUSH 0x0a #define MLX_CMD_LOGOP 0x72 #define MLX_CMD_REBUILDASYNC 0x16 @@ -46,6 +46,13 @@ #define MLX_CMD_REBUILDSTAT 0x0c #define MLX_CMD_STOPCHANNEL 0x13 #define MLX_CMD_STARTCHANNEL 0x12 +#define MLX_CMD_READ_CONFIG 0x4e +#define MLX_CMD_DIRECT_CDB 0x04 + +#ifdef _KERNEL + +#define MLX_CFG_BASE0 0x10 /* first region */ +#define MLX_CFG_BASE1 0x14 /* second region (type 3 only) */ /* * Status values. @@ -65,6 +72,9 @@ #define MLX_V3_IDBR 0x40 #define MLX_V3_ODBR 0x41 #define MLX_V3_IER 0x43 +#define MLX_V3_FWERROR 0x3f +#define MLX_V3_FWERROR_PARAM1 0x00 +#define MLX_V3_FWERROR_PARAM2 0x01 #define MLX_V3_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_MAILBOX + idx, val) #define MLX_V3_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_STATUS_IDENT) @@ -74,21 +84,33 @@ #define MLX_V3_GET_ODBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_ODBR) #define MLX_V3_PUT_ODBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_ODBR, val) #define MLX_V3_PUT_IER(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IER, val) +#define MLX_V3_GET_FWERROR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR) +#define MLX_V3_PUT_FWERROR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR, val) +#define MLX_V3_GET_FWERROR_PARAM1(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR_PARAM1) +#define MLX_V3_GET_FWERROR_PARAM2(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR_PARAM2) #define MLX_V3_IDB_FULL (1<<0) /* mailbox is full */ +#define MLX_V3_IDB_INIT_BUSY (1<<1) /* initialisation in progress */ + #define MLX_V3_IDB_SACK (1<<1) /* acknowledge status read */ #define MLX_V3_ODB_SAVAIL (1<<0) /* status is available */ +#define MLX_V3_FWERROR_PEND (1<<2) /* firmware error pending */ + /* * Accessor defines for the V4 interface. */ #define MLX_V4_MAILBOX 0x1000 -#define MLX_V4_STATUS_IDENT 0x1018 +#define MLX_V4_MAILBOX_LENGTH 16 +#define MLX_V4_STATUS_IDENT 0x1018 #define MLX_V4_STATUS 0x101a #define MLX_V4_IDBR 0x0020 #define MLX_V4_ODBR 0x002c #define MLX_V4_IER 0x0034 +#define MLX_V4_FWERROR 0x103f +#define MLX_V4_FWERROR_PARAM1 0x1000 +#define MLX_V4_FWERROR_PARAM2 0x1001 /* use longword access? */ #define MLX_V4_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX + idx, val) @@ -99,8 +121,13 @@ #define MLX_V4_GET_ODBR(sc) bus_space_read_4 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_ODBR) #define MLX_V4_PUT_ODBR(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_ODBR, val) #define MLX_V4_PUT_IER(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IER, val) +#define MLX_V4_GET_FWERROR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR) +#define MLX_V4_PUT_FWERROR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR, val) +#define MLX_V4_GET_FWERROR_PARAM1(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR_PARAM1) +#define MLX_V4_GET_FWERROR_PARAM2(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR_PARAM2) #define MLX_V4_IDB_FULL (1<<0) /* mailbox is full */ +#define MLX_V4_IDB_INIT_BUSY (1<<1) /* initialisation in progress */ #define MLX_V4_IDB_HWMBOX_CMD (1<<0) /* posted hardware mailbox command */ #define MLX_V4_IDB_SACK (1<<1) /* acknowledge status read */ @@ -115,15 +142,21 @@ #define MLX_V4_IER_MASK 0xfb /* message unit interrupt mask */ #define MLX_V4_IER_DISINT (1<<2) /* interrupt disable bit */ +#define MLX_V4_FWERROR_PEND (1<<2) /* firmware error pending */ + /* * Accessor defines for the V5 interface */ #define MLX_V5_MAILBOX 0x50 +#define MLX_V5_MAILBOX_LENGTH 16 #define MLX_V5_STATUS_IDENT 0x5d #define MLX_V5_STATUS 0x5e #define MLX_V5_IDBR 0x60 #define MLX_V5_ODBR 0x61 #define MLX_V5_IER 0x34 +#define MLX_V5_FWERROR 0x63 +#define MLX_V5_FWERROR_PARAM1 0x50 +#define MLX_V5_FWERROR_PARAM2 0x51 #define MLX_V5_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_MAILBOX + idx, val) #define MLX_V5_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_STATUS_IDENT) @@ -133,11 +166,17 @@ #define MLX_V5_GET_ODBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_ODBR) #define MLX_V5_PUT_ODBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_ODBR, val) #define MLX_V5_PUT_IER(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IER, val) +#define MLX_V5_GET_FWERROR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR) +#define MLX_V5_PUT_FWERROR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR, val) +#define MLX_V5_GET_FWERROR_PARAM1(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR_PARAM1) +#define MLX_V5_GET_FWERROR_PARAM2(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR_PARAM2) #define MLX_V5_IDB_EMPTY (1<<0) /* mailbox is empty */ +#define MLX_V5_IDB_INIT_DONE (1<<1) /* initialisation has completed */ #define MLX_V5_IDB_HWMBOX_CMD (1<<0) /* posted hardware mailbox command */ #define MLX_V5_IDB_SACK (1<<1) /* acknowledge status read */ +#define MLX_V5_IDB_RESET (1<<3) /* reset request */ #define MLX_V5_IDB_MEMMBOX_CMD (1<<4) /* posted memory mailbox command */ #define MLX_V5_ODB_HWSAVAIL (1<<0) /* status is available for hardware mailbox */ @@ -148,6 +187,9 @@ #define MLX_V5_IER_DISINT (1<<2) /* interrupt disable bit */ +#define MLX_V5_FWERROR_PEND (1<<2) /* firmware error pending */ + +#endif /* _KERNEL */ /* * Scatter-gather list format, type 1, kind 00. @@ -161,6 +203,34 @@ struct mlx_sgentry /* * Command result buffers, as placed in system memory by the controller. */ + +struct mlx_enquiry_old /* MLX_CMD_ENQUIRY_OLD */ +{ + u_int8_t me_num_sys_drvs; + u_int8_t res1[3]; + u_int32_t me_drvsize[8]; + u_int16_t me_flash_age; + u_int8_t me_status_flags; + u_int8_t me_free_state_change_count; + u_int8_t me_fwminor; + u_int8_t me_fwmajor; + u_int8_t me_rebuild_flag; + u_int8_t me_max_commands; + u_int8_t me_offline_sd_count; + u_int8_t res3; + u_int8_t me_critical_sd_count; + u_int8_t res4[3]; + u_int8_t me_dead_count; + u_int8_t res5; + u_int8_t me_rebuild_count; + u_int8_t me_misc_flags; + struct + { + u_int8_t dd_targ; + u_int8_t dd_chan; + } __attribute__ ((packed)) me_dead[20]; +} __attribute__ ((packed)); + struct mlx_enquiry /* MLX_CMD_ENQUIRY */ { u_int8_t me_num_sys_drvs; @@ -285,6 +355,135 @@ struct mlx_rebuild_stat /* MLX_CMD_REBUILDSTAT */ u_int32_t rb_remaining; } __attribute__ ((packed)); +struct mlx_config2 +{ + u_int16_t cf_flags1; +#define MLX_CF2_ACTV_NEG (1<<1) +#define MLX_CF2_NORSTRTRY (1<<7) +#define MLX_CF2_STRGWRK (1<<8) +#define MLX_CF2_HPSUPP (1<<9) +#define MLX_CF2_NODISCN (1<<10) +#define MLX_CF2_ARM (1<<13) +#define MLX_CF2_OFM (1<<15) +#define MLX_CF2_AEMI (MLX_CF2_ARM | MLX_CF2_OFM) + u_int8_t cf_oemid; + u_int8_t cf_oem_model; + u_int8_t cf_physical_sector; + u_int8_t cf_logical_sector; + u_int8_t cf_blockfactor; + u_int8_t cf_flags2; +#define MLX_CF2_READAH (1<<0) +#define MLX_CF2_BIOSDLY (1<<1) +#define MLX_CF2_REASS1S (1<<4) +#define MLX_CF2_FUAENABL (1<<6) +#define MLX_CF2_R5ALLS (1<<7) + u_int8_t cf_rcrate; + u_int8_t cf_res1; + u_int8_t cf_blocks_per_cache_line; + u_int8_t cf_blocks_per_stripe; + u_int8_t cf_scsi_param_0; + u_int8_t cf_scsi_param_1; + u_int8_t cf_scsi_param_2; + u_int8_t cf_scsi_param_3; + u_int8_t cf_scsi_param_4; + u_int8_t cf_scsi_param_5; + u_int8_t cf_scsi_initiator_id; + u_int8_t cf_res2; + u_int8_t cf_startup_mode; + u_int8_t cf_simultaneous_spinup_devices; + u_int8_t cf_delay_between_spinups; + u_int8_t cf_res3; + u_int16_t cf_checksum; +} __attribute__ ((packed)); + +struct mlx_sys_drv_span +{ + u_int32_t sp_start_lba; + u_int32_t sp_nblks; + u_int8_t sp_arm[8]; +} __attribute__ ((packed)); + +struct mlx_sys_drv +{ + u_int8_t sd_status; + u_int8_t sd_ext_status; + u_int8_t sd_mod1; + u_int8_t sd_mod2; + u_int8_t sd_raidlevel; +#define MLX_SYS_DRV_WRITEBACK (1<<7) +#define MLX_SYS_DRV_RAID0 0 +#define MLX_SYS_DRV_RAID1 1 +#define MLX_SYS_DRV_RAID3 3 +#define MLX_SYS_DRV_RAID5 5 +#define MLX_SYS_DRV_RAID6 6 +#define MLX_SYS_DRV_JBOD 7 + u_int8_t sd_valid_arms; + u_int8_t sd_valid_spans; + u_int8_t sd_init_state; +#define MLX_SYS_DRV_INITTED 0x81; + struct mlx_sys_drv_span sd_span[4]; +} __attribute__ ((packed)); + +struct mlx_phys_drv +{ + u_int8_t pd_flags1; +#define MLX_PHYS_DRV_PRESENT (1<<0) + u_int8_t pd_flags2; +#define MLX_PHYS_DRV_OTHER 0x00 +#define MLX_PHYS_DRV_DISK 0x01 +#define MLX_PHYS_DRV_SEQUENTIAL 0x02 +#define MLX_PHYS_DRV_CDROM 0x03 +#define MLX_PHYS_DRV_FAST20 (1<<3) +#define MLX_PHYS_DRV_SYNC (1<<4) +#define MLX_PHYS_DRV_FAST (1<<5) +#define MLX_PHYS_DRV_WIDE (1<<6) +#define MLX_PHYS_DRV_TAG (1<<7) + u_int8_t pd_status; +#define MLX_PHYS_DRV_DEAD 0x00 +#define MLX_PHYS_DRV_WRONLY 0x02 +#define MLX_PHYS_DRV_ONLINE 0x03 +#define MLX_PHYS_DRV_STANDBY 0x10 + u_int8_t pd_res1; + u_int8_t pd_period; + u_int8_t pd_offset; + u_int32_t pd_config_size; +} __attribute__ ((packed)); + +struct mlx_core_cfg +{ + u_int8_t cc_num_sys_drives; + u_int8_t cc_res1[3]; + struct mlx_sys_drv cc_sys_drives[32]; + struct mlx_phys_drv cc_phys_drives[5 * 16]; +} __attribute__ ((packed)); + +struct mlx_dcdb +{ + u_int8_t dcdb_target:4; + u_int8_t dcdb_channel:4; + u_int8_t dcdb_flags; +#define MLX_DCDB_NO_DATA 0x00 +#define MLX_DCDB_DATA_IN 0x01 +#define MLX_DCDB_DATA_OUT 0x02 +#define MLX_DCDB_EARLY_STATUS (1<<2) +#define MLX_DCDB_TIMEOUT_10S 0x10 +#define MLX_DCDB_TIMEOUT_60S 0x20 +#define MLX_DCDB_TIMEOUT_20M 0x30 +#define MLX_DCDB_TIMEOUT_24H 0x40 +#define MLX_DCDB_NO_AUTO_SENSE (1<<6) +#define MLX_DCDB_DISCONNECT (1<<7) + u_int16_t dcdb_datasize; + u_int32_t dcdb_physaddr; + u_int8_t dcdb_cdb_length:4; + u_int8_t dcdb_datasize_high:4; + u_int8_t dcdb_sense_length; + u_int8_t dcdb_cdb[12]; + u_int8_t dcdb_sense[64]; + u_int8_t dcdb_status; + u_int8_t res1; +} __attribute__ ((packed)); + +#ifdef _KERNEL /* * Inlines to build various command structures */ @@ -406,3 +605,5 @@ mlx_make_type5(struct mlx_command *mc, mc->mc_mailbox[0xb] = (f4 >> 24) & 0xff; mc->mc_mailbox[0xc] = f5; } + +#endif /* _KERNEL */ diff --git a/sys/dev/mlx/mlxvar.h b/sys/dev/mlx/mlxvar.h index 34653588b759..7847ca2466b0 100644 --- a/sys/dev/mlx/mlxvar.h +++ b/sys/dev/mlx/mlxvar.h @@ -27,14 +27,30 @@ */ /* - * We could actually use all 33 segments, but using only 32 means that - * each scatter/gather map is 256 bytes in size, and thus we don't have to worry about + * Debugging levels: + * 0 - quiet, only emit warnings + * 1 - noisy, emit major function points and things done + * 2 - extremely noisy, emit trace items in loops, etc. + */ +#ifdef MLX_DEBUG +#define debug(level, fmt, args...) do { if (level <= MLX_DEBUG) printf("%s: " fmt "\n", __FUNCTION__ , ##args); } while(0) +#define debug_called(level) do { if (level <= MLX_DEBUG) printf(__FUNCTION__ ": called\n"); } while(0) +#else +#define debug(level, fmt, args...) +#define debug_called(level) +#endif + + +/* + * We could actually use all 17/33 segments, but using only 16/32 means that + * each scatter/gather map is 128/256 bytes in size, and thus we don't have to worry about * maps crossing page boundaries. */ -#define MLX_NSEG 32 /* max scatter/gather segments we use */ +#define MLX_NSEG_OLD 16 /* max scatter/gather segments we use 3.x and earlier */ +#define MLX_NSEG_NEW 32 /* max scatter/gather segments we use 4.x and later */ #define MLX_NSLOTS 256 /* max number of command slots */ -#define MLX_MAXDRIVES 32 +#define MLX_MAXDRIVES 32 /* max number of system drives */ /* * Structure describing a System Drive as attached to the controller. @@ -90,6 +106,7 @@ struct mlx_softc { /* bus connections */ device_t mlx_dev; + dev_t mlx_dev_t; struct resource *mlx_mem; /* mailbox interface window */ bus_space_handle_t mlx_bhandle; /* bus space handle */ bus_space_tag_t mlx_btag; /* bus space tag */ @@ -103,6 +120,7 @@ struct mlx_softc u_int32_t mlx_sgbusaddr; /* s/g table base address in bus space */ bus_dma_tag_t mlx_sg_dmat; /* s/g buffer DMA tag */ bus_dmamap_t mlx_sg_dmamap; /* map for s/g buffers */ + int mlx_sg_nseg; /* max number of s/g entries */ /* controller limits and features */ struct mlx_enquiry2 *mlx_enq2; @@ -138,15 +156,19 @@ struct mlx_softc struct mlx_pause mlx_pause; /* pending pause operation details */ int mlx_locks; /* reentrancy avoidance */ + int mlx_flags; +#define MLX_SPINUP_REPORTED (1<<0) /* "spinning up drives" message displayed */ /* interface-specific accessor functions */ int mlx_iftype; /* interface protocol */ +#define MLX_IFTYPE_2 2 #define MLX_IFTYPE_3 3 #define MLX_IFTYPE_4 4 #define MLX_IFTYPE_5 5 int (* mlx_tryqueue)(struct mlx_softc *sc, struct mlx_command *mc); int (* mlx_findcomplete)(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); void (* mlx_intaction)(struct mlx_softc *sc, int action); + int (* mlx_fw_handshake)(struct mlx_softc *sc, int *error, int *param1, int *param2); #define MLX_INTACTION_DISABLE 0 #define MLX_INTACTION_ENABLE 1 };