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 };