xref: /freebsd/sys/dev/mlx/mlx.c (revision 7ac40f5f59dea6e9ab4869e974bdd4026274e921)
11ac4b82bSMike Smith /*-
21ac4b82bSMike Smith  * Copyright (c) 1999 Michael Smith
31ac4b82bSMike Smith  * All rights reserved.
41ac4b82bSMike Smith  *
51ac4b82bSMike Smith  * Redistribution and use in source and binary forms, with or without
61ac4b82bSMike Smith  * modification, are permitted provided that the following conditions
71ac4b82bSMike Smith  * are met:
81ac4b82bSMike Smith  * 1. Redistributions of source code must retain the above copyright
91ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer.
101ac4b82bSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
111ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer in the
121ac4b82bSMike Smith  *    documentation and/or other materials provided with the distribution.
131ac4b82bSMike Smith  *
141ac4b82bSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151ac4b82bSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161ac4b82bSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171ac4b82bSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181ac4b82bSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191ac4b82bSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201ac4b82bSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211ac4b82bSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221ac4b82bSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231ac4b82bSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241ac4b82bSMike Smith  * SUCH DAMAGE.
251ac4b82bSMike Smith  *
261ac4b82bSMike Smith  *	$FreeBSD$
271ac4b82bSMike Smith  */
281ac4b82bSMike Smith 
291ac4b82bSMike Smith /*
301ac4b82bSMike Smith  * Driver for the Mylex DAC960 family of RAID controllers.
311ac4b82bSMike Smith  */
321ac4b82bSMike Smith 
331ac4b82bSMike Smith #include <sys/param.h>
341ac4b82bSMike Smith #include <sys/systm.h>
351ac4b82bSMike Smith #include <sys/malloc.h>
361ac4b82bSMike Smith #include <sys/kernel.h>
371ac4b82bSMike Smith 
381ac4b82bSMike Smith #include <sys/bus.h>
391ac4b82bSMike Smith #include <sys/conf.h>
401ac4b82bSMike Smith #include <sys/devicestat.h>
411ac4b82bSMike Smith #include <sys/disk.h>
42da8bb3a3SMike Smith #include <sys/stat.h>
431ac4b82bSMike Smith 
441ac4b82bSMike Smith #include <machine/resource.h>
45786cd128SMike Smith #include <machine/bus_memio.h>
46786cd128SMike Smith #include <machine/bus_pio.h>
471ac4b82bSMike Smith #include <machine/bus.h>
481ac4b82bSMike Smith #include <machine/clock.h>
491ac4b82bSMike Smith #include <sys/rman.h>
501ac4b82bSMike Smith 
5115fd5d22SMike Smith #include <dev/mlx/mlx_compat.h>
521ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
531ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
541ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
551ac4b82bSMike Smith 
561ac4b82bSMike Smith #define MLX_CDEV_MAJOR	130
571ac4b82bSMike Smith 
581ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
597ac40f5fSPoul-Henning Kamp 	.d_open =	mlx_open,
607ac40f5fSPoul-Henning Kamp 	.d_close =	mlx_close,
617ac40f5fSPoul-Henning Kamp 	.d_ioctl =	mlx_ioctl,
627ac40f5fSPoul-Henning Kamp 	.d_name =	"mlx",
637ac40f5fSPoul-Henning Kamp 	.d_maj =	MLX_CDEV_MAJOR,
641ac4b82bSMike Smith };
651ac4b82bSMike Smith 
661ac4b82bSMike Smith devclass_t	mlx_devclass;
671ac4b82bSMike Smith 
681ac4b82bSMike Smith /*
691ac4b82bSMike Smith  * Per-interface accessor methods
701ac4b82bSMike Smith  */
711ac4b82bSMike Smith static int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
721ac4b82bSMike Smith static int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
731ac4b82bSMike Smith static void			mlx_v3_intaction(struct mlx_softc *sc, int action);
74da8bb3a3SMike Smith static int			mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
751ac4b82bSMike Smith 
76f6b84b08SMike Smith static int			mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
77f6b84b08SMike Smith static int			mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
78f6b84b08SMike Smith static void			mlx_v4_intaction(struct mlx_softc *sc, int action);
79da8bb3a3SMike Smith static int			mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
80f6b84b08SMike Smith 
815792b7feSMike Smith static int			mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
825792b7feSMike Smith static int			mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
835792b7feSMike Smith static void			mlx_v5_intaction(struct mlx_softc *sc, int action);
84da8bb3a3SMike Smith static int			mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
855792b7feSMike Smith 
861ac4b82bSMike Smith /*
871ac4b82bSMike Smith  * Status monitoring
881ac4b82bSMike Smith  */
891ac4b82bSMike Smith static void			mlx_periodic(void *data);
901ac4b82bSMike Smith static void			mlx_periodic_enquiry(struct mlx_command *mc);
911ac4b82bSMike Smith static void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
921ac4b82bSMike Smith static void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
931ac4b82bSMike Smith static void			mlx_periodic_rebuild(struct mlx_command *mc);
941ac4b82bSMike Smith 
951ac4b82bSMike Smith /*
961ac4b82bSMike Smith  * Channel Pause
971ac4b82bSMike Smith  */
981ac4b82bSMike Smith static void			mlx_pause_action(struct mlx_softc *sc);
991ac4b82bSMike Smith static void			mlx_pause_done(struct mlx_command *mc);
1001ac4b82bSMike Smith 
1011ac4b82bSMike Smith /*
1021ac4b82bSMike Smith  * Command submission.
1031ac4b82bSMike Smith  */
1041ac4b82bSMike Smith static void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
1051ac4b82bSMike Smith 					     void (*complete)(struct mlx_command *mc));
1061ac4b82bSMike Smith static int			mlx_flush(struct mlx_softc *sc);
107421f2f7dSMike Smith static int			mlx_check(struct mlx_softc *sc, int drive);
1081ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1091ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1101ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1111ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1121ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1131ac4b82bSMike Smith static int			mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu);
1141ac4b82bSMike Smith 
1151ac4b82bSMike Smith /*
1161ac4b82bSMike Smith  * Command buffer allocation.
1171ac4b82bSMike Smith  */
1181ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1191ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1201ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1211ac4b82bSMike Smith 
1221ac4b82bSMike Smith /*
1231ac4b82bSMike Smith  * Command management.
1241ac4b82bSMike Smith  */
1251ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1261ac4b82bSMike Smith static void			mlx_mapcmd(struct mlx_command *mc);
1271ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1281ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1291ac4b82bSMike Smith static int			mlx_done(struct mlx_softc *sc);
1301ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1311ac4b82bSMike Smith 
1321ac4b82bSMike Smith /*
1331ac4b82bSMike Smith  * Debugging.
1341ac4b82bSMike Smith  */
1351ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1369eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
137da8bb3a3SMike Smith static int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
1381ac4b82bSMike Smith 
1391ac4b82bSMike Smith /*
1401ac4b82bSMike Smith  * Utility functions.
1411ac4b82bSMike Smith  */
1421ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1431ac4b82bSMike Smith 
1441ac4b82bSMike Smith /********************************************************************************
1451ac4b82bSMike Smith  ********************************************************************************
1461ac4b82bSMike Smith                                                                 Public Interfaces
1471ac4b82bSMike Smith  ********************************************************************************
1481ac4b82bSMike Smith  ********************************************************************************/
1491ac4b82bSMike Smith 
1501ac4b82bSMike Smith /********************************************************************************
1511ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1521ac4b82bSMike Smith  *
1531ac4b82bSMike Smith  * Should not be called if the controller is active.
1541ac4b82bSMike Smith  */
1551ac4b82bSMike Smith void
1561ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1571ac4b82bSMike Smith {
1581ac4b82bSMike Smith     struct mlx_command	*mc;
1591ac4b82bSMike Smith 
160da8bb3a3SMike Smith     debug_called(1);
1611ac4b82bSMike Smith 
1621ac4b82bSMike Smith     /* cancel status timeout */
1631ac4b82bSMike Smith     untimeout(mlx_periodic, sc, sc->mlx_timeout);
1641ac4b82bSMike Smith 
1651ac4b82bSMike Smith     /* throw away any command buffers */
1661ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1671ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1681ac4b82bSMike Smith 	mlx_freecmd(mc);
1691ac4b82bSMike Smith     }
1701ac4b82bSMike Smith 
1711ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1721ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1731ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1741ac4b82bSMike Smith 
1751ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
1761ac4b82bSMike Smith     if (sc->mlx_sgtable)
1771ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1781ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1791ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1801ac4b82bSMike Smith 
1811ac4b82bSMike Smith     /* disconnect the interrupt handler */
1821ac4b82bSMike Smith     if (sc->mlx_intr)
1831ac4b82bSMike Smith 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1841ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
1851ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
1861ac4b82bSMike Smith 
1871ac4b82bSMike Smith     /* destroy the parent DMA tag */
1881ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
1891ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
1901ac4b82bSMike Smith 
1911ac4b82bSMike Smith     /* release the register window mapping */
1921ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
1939b11c7baSMatthew N. Dodd 	bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem);
1949eee27f1SMike Smith 
1959eee27f1SMike Smith     /* free controller enquiry data */
1969eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
1979eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
198da8bb3a3SMike Smith 
199da8bb3a3SMike Smith     /* destroy control device */
200da8bb3a3SMike Smith     if (sc->mlx_dev_t != (dev_t)NULL)
201da8bb3a3SMike Smith 	destroy_dev(sc->mlx_dev_t);
2021ac4b82bSMike Smith }
2031ac4b82bSMike Smith 
2041ac4b82bSMike Smith /********************************************************************************
2051ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2061ac4b82bSMike Smith  */
2071ac4b82bSMike Smith static void
2081ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2091ac4b82bSMike Smith {
2101ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2111ac4b82bSMike Smith 
212da8bb3a3SMike Smith     debug_called(1);
2131ac4b82bSMike Smith 
2141ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2151ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2161ac4b82bSMike Smith }
2171ac4b82bSMike Smith 
2181ac4b82bSMike Smith static int
2191ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2201ac4b82bSMike Smith {
2211ac4b82bSMike Smith     size_t	segsize;
222baff09dbSMike Smith     int		error, ncmd;
2231ac4b82bSMike Smith 
224da8bb3a3SMike Smith     debug_called(1);
2251ac4b82bSMike Smith 
2261ac4b82bSMike Smith     /* destroy any existing mappings */
2271ac4b82bSMike Smith     if (sc->mlx_sgtable)
2281ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2291ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2301ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2311ac4b82bSMike Smith 
2321ac4b82bSMike Smith     /*
2331ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
234baff09dbSMike Smith      * the s/g lists we will need.  If we're called early on, we don't know how
235baff09dbSMike Smith      * many commands we're going to be asked to support, so only allocate enough
236baff09dbSMike Smith      * for a couple.
2371ac4b82bSMike Smith      */
238baff09dbSMike Smith     if (sc->mlx_enq2 == NULL) {
239baff09dbSMike Smith 	ncmd = 2;
240baff09dbSMike Smith     } else {
241baff09dbSMike Smith 	ncmd = sc->mlx_enq2->me_max_commands;
242baff09dbSMike Smith     }
243baff09dbSMike Smith     segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd;
2441ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2451ac4b82bSMike Smith 			       1, 0, 			/* alignment, boundary */
2461ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2471ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2481ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2491ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2501ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2511ac4b82bSMike Smith 			       0,			/* flags */
2521ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2531ac4b82bSMike Smith     if (error != 0) {
2541ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2551ac4b82bSMike Smith 	return(ENOMEM);
2561ac4b82bSMike Smith     }
2571ac4b82bSMike Smith 
2581ac4b82bSMike Smith     /*
2591ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2601ac4b82bSMike Smith      * controller-visible space.
2611ac4b82bSMike Smith      *
2621ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
2631ac4b82bSMike Smith      * contiguous slab.  We may need to switch to a more complex arrangement where
2641ac4b82bSMike Smith      * we allocate in smaller chunks and keep a lookup table from slot to bus address.
2651ac4b82bSMike Smith      */
2661ac4b82bSMike Smith     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2671ac4b82bSMike Smith     if (error) {
2681ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2691ac4b82bSMike Smith 	return(ENOMEM);
2701ac4b82bSMike Smith     }
2711ac4b82bSMike Smith     bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0);
2721ac4b82bSMike Smith     return(0);
2731ac4b82bSMike Smith }
2741ac4b82bSMike Smith 
2751ac4b82bSMike Smith /********************************************************************************
2761ac4b82bSMike Smith  * Initialise the controller and softc
2771ac4b82bSMike Smith  */
2781ac4b82bSMike Smith int
2791ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
2801ac4b82bSMike Smith {
281da8bb3a3SMike Smith     struct mlx_enquiry_old	*meo;
282da8bb3a3SMike Smith     int				rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
2831ac4b82bSMike Smith 
284da8bb3a3SMike Smith     debug_called(1);
2851ac4b82bSMike Smith 
2861ac4b82bSMike Smith     /*
2871ac4b82bSMike Smith      * Initialise per-controller queues.
2881ac4b82bSMike Smith      */
2894b006d7bSMike Smith     TAILQ_INIT(&sc->mlx_work);
2901ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
29115fd5d22SMike Smith     MLX_BIO_QINIT(sc->mlx_bioq);
2921ac4b82bSMike Smith 
2931ac4b82bSMike Smith     /*
2941ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
2951ac4b82bSMike Smith      */
2961ac4b82bSMike Smith     switch(sc->mlx_iftype) {
297da8bb3a3SMike Smith     case MLX_IFTYPE_2:
2981ac4b82bSMike Smith     case MLX_IFTYPE_3:
2991ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
3001ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
3011ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
302da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v3_fw_handshake;
3031ac4b82bSMike Smith 	break;
304f6b84b08SMike Smith     case MLX_IFTYPE_4:
305f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
306f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
307f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
308da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
309f6b84b08SMike Smith 	break;
3105792b7feSMike Smith     case MLX_IFTYPE_5:
3115792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3125792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3135792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
314da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
3155792b7feSMike Smith 	break;
3161ac4b82bSMike Smith     default:
3175d278f5cSMike Smith 	mlx_free(sc);
3181ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3191ac4b82bSMike Smith     }
3201ac4b82bSMike Smith 
3211ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3221ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3231ac4b82bSMike Smith 
3241ac4b82bSMike Smith     /*
325da8bb3a3SMike Smith      * Wait for the controller to come ready, handshake with the firmware if required.
326da8bb3a3SMike Smith      * This is typically only necessary on platforms where the controller BIOS does not
327da8bb3a3SMike Smith      * run.
328da8bb3a3SMike Smith      */
329da8bb3a3SMike Smith     hsmsg = 0;
330da8bb3a3SMike Smith     DELAY(1000);
331da8bb3a3SMike Smith     while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) {
332da8bb3a3SMike Smith 	/* report first time around... */
333da8bb3a3SMike Smith 	if (hsmsg == 0) {
334da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
335da8bb3a3SMike Smith 	    hsmsg = 1;
336da8bb3a3SMike Smith 	}
337da8bb3a3SMike Smith 	/* did we get a real message? */
338da8bb3a3SMike Smith 	if (hscode == 2) {
339da8bb3a3SMike Smith 	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
340da8bb3a3SMike Smith 	    /* fatal initialisation error? */
341da8bb3a3SMike Smith 	    if (hscode != 0) {
342da8bb3a3SMike Smith 		mlx_free(sc);
343da8bb3a3SMike Smith 		return(ENXIO);
344da8bb3a3SMike Smith 	    }
345da8bb3a3SMike Smith 	}
346da8bb3a3SMike Smith     }
347da8bb3a3SMike Smith     if (hsmsg == 1)
348da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "initialisation complete.\n");
349da8bb3a3SMike Smith 
350da8bb3a3SMike Smith     /*
3511ac4b82bSMike Smith      * Allocate and connect our interrupt.
3521ac4b82bSMike Smith      */
3531ac4b82bSMike Smith     rid = 0;
3541ac4b82bSMike Smith     sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
3551ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
356421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't allocate interrupt\n");
3571ac4b82bSMike Smith 	mlx_free(sc);
3581ac4b82bSMike Smith 	return(ENXIO);
3591ac4b82bSMike Smith     }
360ed34d0adSMark Murray     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY,  mlx_intr, sc, &sc->mlx_intr);
3611ac4b82bSMike Smith     if (error) {
362421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't set up interrupt\n");
3631ac4b82bSMike Smith 	mlx_free(sc);
3641ac4b82bSMike Smith 	return(ENXIO);
3651ac4b82bSMike Smith     }
3661ac4b82bSMike Smith 
3671ac4b82bSMike Smith     /*
3681ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3691ac4b82bSMike Smith      */
3701ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 		/* parent */
3711ac4b82bSMike Smith 			       1, 0, 				/* alignment, boundary */
3721ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,		/* lowaddr */
3731ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 		/* highaddr */
3741ac4b82bSMike Smith 			       NULL, NULL, 			/* filter, filterarg */
375baff09dbSMike Smith 			       MAXBSIZE, MLX_NSEG,		/* maxsize, nsegments */
3761ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,		/* maxsegsize */
3771ac4b82bSMike Smith 			       0,				/* flags */
3781ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
3791ac4b82bSMike Smith     if (error != 0) {
3801ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
3815d278f5cSMike Smith 	mlx_free(sc);
3821ac4b82bSMike Smith 	return(ENOMEM);
3831ac4b82bSMike Smith     }
3841ac4b82bSMike Smith 
3851ac4b82bSMike Smith     /*
386baff09dbSMike Smith      * Create some initial scatter/gather mappings so we can run the probe commands.
3871ac4b82bSMike Smith      */
3881ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3891ac4b82bSMike Smith     if (error != 0) {
390421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
3915d278f5cSMike Smith 	mlx_free(sc);
3921ac4b82bSMike Smith 	return(error);
3931ac4b82bSMike Smith     }
3941ac4b82bSMike Smith 
395baff09dbSMike Smith     /*
396baff09dbSMike Smith      * We don't (yet) know where the event log is up to.
397baff09dbSMike Smith      */
398baff09dbSMike Smith     sc->mlx_currevent = -1;
399baff09dbSMike Smith 
400baff09dbSMike Smith     /*
401baff09dbSMike Smith      * Obtain controller feature information
402baff09dbSMike Smith      */
4039eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
4041ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
4055d278f5cSMike Smith 	mlx_free(sc);
4061ac4b82bSMike Smith 	return(ENXIO);
4071ac4b82bSMike Smith     }
4081ac4b82bSMike Smith 
4099eee27f1SMike Smith     /*
4101ac4b82bSMike Smith      * Do quirk/feature related things.
4111ac4b82bSMike Smith      */
4129eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
4131ac4b82bSMike Smith     switch(sc->mlx_iftype) {
414da8bb3a3SMike Smith     case MLX_IFTYPE_2:
415da8bb3a3SMike Smith 	/* These controllers don't report the firmware version in the ENQUIRY2 response */
416da8bb3a3SMike Smith 	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
417da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
4185d278f5cSMike Smith 	    mlx_free(sc);
419da8bb3a3SMike Smith 	    return(ENXIO);
420da8bb3a3SMike Smith 	}
421da8bb3a3SMike Smith 	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
422da8bb3a3SMike Smith 	free(meo, M_DEVBUF);
423da8bb3a3SMike Smith 
424da8bb3a3SMike Smith 	/* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */
425da8bb3a3SMike Smith 	if (meo->me_fwminor < 42) {
426da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
427da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
428da8bb3a3SMike Smith 	}
429da8bb3a3SMike Smith 	break;
4301ac4b82bSMike Smith     case MLX_IFTYPE_3:
431f6b84b08SMike Smith 	/* XXX certify 3.52? */
4329eee27f1SMike Smith 	if (fwminor < 51) {
4334b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4344b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
4351ac4b82bSMike Smith 	}
4361ac4b82bSMike Smith 	break;
437f6b84b08SMike Smith     case MLX_IFTYPE_4:
438f6b84b08SMike Smith 	/* XXX certify firmware versions? */
4399eee27f1SMike Smith 	if (fwminor < 6) {
4404b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4414b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
442f6b84b08SMike Smith 	}
443f6b84b08SMike Smith 	break;
4445792b7feSMike Smith     case MLX_IFTYPE_5:
4459eee27f1SMike Smith 	if (fwminor < 7) {
4465792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4475792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
4485792b7feSMike Smith 	}
4495792b7feSMike Smith 	break;
4501ac4b82bSMike Smith     default:
4515d278f5cSMike Smith 	mlx_free(sc);
4521ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4531ac4b82bSMike Smith     }
4541ac4b82bSMike Smith 
4551ac4b82bSMike Smith     /*
456baff09dbSMike Smith      * Create the final scatter/gather mappings now that we have characterised the controller.
4571ac4b82bSMike Smith      */
4581ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4591ac4b82bSMike Smith     if (error != 0) {
460baff09dbSMike Smith 	device_printf(sc->mlx_dev, "can't make final s/g list mapping\n");
4615d278f5cSMike Smith 	mlx_free(sc);
4621ac4b82bSMike Smith 	return(error);
4631ac4b82bSMike Smith     }
4641ac4b82bSMike Smith 
4651ac4b82bSMike Smith     /*
466421f2f7dSMike Smith      * No user-requested background operation is in progress.
4671ac4b82bSMike Smith      */
468421f2f7dSMike Smith     sc->mlx_background = 0;
469421f2f7dSMike Smith     sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
4701ac4b82bSMike Smith 
4711ac4b82bSMike Smith     /*
472da8bb3a3SMike Smith      * Create the control device.
4731ac4b82bSMike Smith      */
474da8bb3a3SMike Smith     sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR,
475da8bb3a3SMike Smith 			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
4761ac4b82bSMike Smith 
4771ac4b82bSMike Smith     /*
4781ac4b82bSMike Smith      * Start the timeout routine.
4791ac4b82bSMike Smith      */
4801ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
4811ac4b82bSMike Smith 
482da8bb3a3SMike Smith     /* print a little information about the controller */
483da8bb3a3SMike Smith     mlx_describe_controller(sc);
484da8bb3a3SMike Smith 
4851ac4b82bSMike Smith     return(0);
4861ac4b82bSMike Smith }
4871ac4b82bSMike Smith 
4881ac4b82bSMike Smith /********************************************************************************
4891ac4b82bSMike Smith  * Locate disk resources and attach children to them.
4901ac4b82bSMike Smith  */
4911ac4b82bSMike Smith void
4921ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
4931ac4b82bSMike Smith {
4941ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
4951ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
4961ac4b82bSMike Smith     int				i, error;
4971ac4b82bSMike Smith 
498da8bb3a3SMike Smith     debug_called(1);
4991ac4b82bSMike Smith 
5001ac4b82bSMike Smith     /*
5011ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
5021ac4b82bSMike Smith      * don't currently have them.
5031ac4b82bSMike Smith      */
5041ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
5051ac4b82bSMike Smith     if (mes == NULL) {
5069eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
5071ac4b82bSMike Smith 	return;
5081ac4b82bSMike Smith     }
5091ac4b82bSMike Smith 
5101ac4b82bSMike Smith     /* iterate over drives returned */
5111ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
5121ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
5131ac4b82bSMike Smith 	 i++, dr++) {
5141ac4b82bSMike Smith 	/* are we already attached to this drive? */
5151ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
5161ac4b82bSMike Smith 	    /* pick up drive information */
5171ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
518f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
5191ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
5201ac4b82bSMike Smith 
5211ac4b82bSMike Smith 	    /* generate geometry information */
5221ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
5231ac4b82bSMike Smith 		dr->ms_heads = 128;
5241ac4b82bSMike Smith 		dr->ms_sectors = 32;
5251ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
5261ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
5271ac4b82bSMike Smith 		dr->ms_heads = 255;
5281ac4b82bSMike Smith 		dr->ms_sectors = 63;
5291ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
5301ac4b82bSMike Smith 	    }
531fe0d4089SMatthew N. Dodd 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
5321ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
5331ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
534fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
5351ac4b82bSMike Smith 	}
5361ac4b82bSMike Smith     }
5371ac4b82bSMike Smith     free(mes, M_DEVBUF);
5381ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
5391ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
5401ac4b82bSMike Smith 
5411ac4b82bSMike Smith     /* mark controller back up */
5421ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5431ac4b82bSMike Smith 
5441ac4b82bSMike Smith     /* enable interrupts */
5451ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
5461ac4b82bSMike Smith }
5471ac4b82bSMike Smith 
5481ac4b82bSMike Smith /********************************************************************************
5491ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
5501ac4b82bSMike Smith  */
5511ac4b82bSMike Smith int
5521ac4b82bSMike Smith mlx_detach(device_t dev)
5531ac4b82bSMike Smith {
5541ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5555792b7feSMike Smith     struct mlxd_softc	*mlxd;
5565792b7feSMike Smith     int			i, s, error;
5571ac4b82bSMike Smith 
558da8bb3a3SMike Smith     debug_called(1);
5591ac4b82bSMike Smith 
5605792b7feSMike Smith     error = EBUSY;
5615792b7feSMike Smith     s = splbio();
5621ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
5635792b7feSMike Smith 	goto out;
5641ac4b82bSMike Smith 
5655792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5665792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5675792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
5685792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
5695792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
5705792b7feSMike Smith 		goto out;
5715792b7feSMike Smith 	    }
5725792b7feSMike Smith 	}
5735792b7feSMike Smith     }
5741ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
5755792b7feSMike Smith 	goto out;
5761ac4b82bSMike Smith 
5771ac4b82bSMike Smith     mlx_free(sc);
5781ac4b82bSMike Smith 
5795792b7feSMike Smith     error = 0;
5805792b7feSMike Smith  out:
5815792b7feSMike Smith     splx(s);
5825792b7feSMike Smith     return(error);
5831ac4b82bSMike Smith }
5841ac4b82bSMike Smith 
5851ac4b82bSMike Smith /********************************************************************************
5861ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
5871ac4b82bSMike Smith  *
5881ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
5891ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
5901ac4b82bSMike Smith  * resume normal operation.)
5911ac4b82bSMike Smith  *
5928177437dSPoul-Henning Kamp  * Note that we can assume that the bioq on the controller is empty, as we won't
5931ac4b82bSMike Smith  * allow shutdown if any device is open.
5941ac4b82bSMike Smith  */
5951ac4b82bSMike Smith int
5961ac4b82bSMike Smith mlx_shutdown(device_t dev)
5971ac4b82bSMike Smith {
5981ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5991ac4b82bSMike Smith     int			i, s, error;
6001ac4b82bSMike Smith 
601da8bb3a3SMike Smith     debug_called(1);
6021ac4b82bSMike Smith 
6031ac4b82bSMike Smith     s = splbio();
6041ac4b82bSMike Smith     error = 0;
6051ac4b82bSMike Smith 
6061ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
6075792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6081ac4b82bSMike Smith 
6091ac4b82bSMike Smith     /* flush controller */
6101ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6111ac4b82bSMike Smith     if (mlx_flush(sc)) {
6121ac4b82bSMike Smith 	printf("failed\n");
6131ac4b82bSMike Smith     } else {
6141ac4b82bSMike Smith 	printf("done\n");
6151ac4b82bSMike Smith     }
6161ac4b82bSMike Smith 
6171ac4b82bSMike Smith     /* delete all our child devices */
6181ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6191ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6201ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
6211ac4b82bSMike Smith 		goto out;
6221ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
6231ac4b82bSMike Smith 	}
6241ac4b82bSMike Smith     }
6251ac4b82bSMike Smith 
6261ac4b82bSMike Smith  out:
6271ac4b82bSMike Smith     splx(s);
6281ac4b82bSMike Smith     return(error);
6291ac4b82bSMike Smith }
6301ac4b82bSMike Smith 
6311ac4b82bSMike Smith /********************************************************************************
6321ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
6331ac4b82bSMike Smith  */
6341ac4b82bSMike Smith int
6351ac4b82bSMike Smith mlx_suspend(device_t dev)
6361ac4b82bSMike Smith {
6371ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6381ac4b82bSMike Smith     int			s;
6391ac4b82bSMike Smith 
640da8bb3a3SMike Smith     debug_called(1);
6411ac4b82bSMike Smith 
6421ac4b82bSMike Smith     s = splbio();
6431ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
6441ac4b82bSMike Smith 
6451ac4b82bSMike Smith     /* flush controller */
6461ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6471ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6481ac4b82bSMike Smith 
6491ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6501ac4b82bSMike Smith     splx(s);
6511ac4b82bSMike Smith 
6521ac4b82bSMike Smith     return(0);
6531ac4b82bSMike Smith }
6541ac4b82bSMike Smith 
6551ac4b82bSMike Smith /********************************************************************************
6561ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
6571ac4b82bSMike Smith  */
6581ac4b82bSMike Smith int
6591ac4b82bSMike Smith mlx_resume(device_t dev)
6601ac4b82bSMike Smith {
6611ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6621ac4b82bSMike Smith 
663da8bb3a3SMike Smith     debug_called(1);
6641ac4b82bSMike Smith 
6651ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
6661ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
6671ac4b82bSMike Smith 
6681ac4b82bSMike Smith     return(0);
6691ac4b82bSMike Smith }
6701ac4b82bSMike Smith 
6711ac4b82bSMike Smith /*******************************************************************************
6721ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
6731ac4b82bSMike Smith  * status.
6741ac4b82bSMike Smith  */
6751ac4b82bSMike Smith void
6761ac4b82bSMike Smith mlx_intr(void *arg)
6771ac4b82bSMike Smith {
6781ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
6791ac4b82bSMike Smith 
680da8bb3a3SMike Smith     debug_called(1);
6811ac4b82bSMike Smith 
6825792b7feSMike Smith     /* collect finished commands, queue anything waiting */
6835792b7feSMike Smith     mlx_done(sc);
6841ac4b82bSMike Smith };
6851ac4b82bSMike Smith 
6861ac4b82bSMike Smith /*******************************************************************************
6871ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
6881ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
6891ac4b82bSMike Smith  */
6901ac4b82bSMike Smith int
69115fd5d22SMike Smith mlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp)
6921ac4b82bSMike Smith {
6934b006d7bSMike Smith     int		s;
6944b006d7bSMike Smith 
695da8bb3a3SMike Smith     debug_called(1);
6961ac4b82bSMike Smith 
6974b006d7bSMike Smith     s = splbio();
69815fd5d22SMike Smith     MLX_BIO_QINSERT(sc->mlx_bioq, bp);
6991ac4b82bSMike Smith     sc->mlx_waitbufs++;
7004b006d7bSMike Smith     splx(s);
7011ac4b82bSMike Smith     mlx_startio(sc);
7021ac4b82bSMike Smith     return(0);
7031ac4b82bSMike Smith }
7041ac4b82bSMike Smith 
7051ac4b82bSMike Smith /********************************************************************************
7061ac4b82bSMike Smith  * Accept an open operation on the control device.
7071ac4b82bSMike Smith  */
7081ac4b82bSMike Smith int
709b40ce416SJulian Elischer mlx_open(dev_t dev, int flags, int fmt, struct thread *td)
7101ac4b82bSMike Smith {
7111ac4b82bSMike Smith     int			unit = minor(dev);
7121ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7131ac4b82bSMike Smith 
7141ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
7151ac4b82bSMike Smith     return(0);
7161ac4b82bSMike Smith }
7171ac4b82bSMike Smith 
7181ac4b82bSMike Smith /********************************************************************************
7191ac4b82bSMike Smith  * Accept the last close on the control device.
7201ac4b82bSMike Smith  */
7211ac4b82bSMike Smith int
722b40ce416SJulian Elischer mlx_close(dev_t dev, int flags, int fmt, struct thread *td)
7231ac4b82bSMike Smith {
7241ac4b82bSMike Smith     int			unit = minor(dev);
7251ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7261ac4b82bSMike Smith 
7271ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
7281ac4b82bSMike Smith     return (0);
7291ac4b82bSMike Smith }
7301ac4b82bSMike Smith 
7311ac4b82bSMike Smith /********************************************************************************
7321ac4b82bSMike Smith  * Handle controller-specific control operations.
7331ac4b82bSMike Smith  */
7341ac4b82bSMike Smith int
735b40ce416SJulian Elischer mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
7361ac4b82bSMike Smith {
7371ac4b82bSMike Smith     int				unit = minor(dev);
7381ac4b82bSMike Smith     struct mlx_softc		*sc = devclass_get_softc(mlx_devclass, unit);
739421f2f7dSMike Smith     struct mlx_rebuild_request	*rb = (struct mlx_rebuild_request *)addr;
740421f2f7dSMike Smith     struct mlx_rebuild_status	*rs = (struct mlx_rebuild_status *)addr;
7411ac4b82bSMike Smith     int				*arg = (int *)addr;
7421ac4b82bSMike Smith     struct mlx_pause		*mp;
7431ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
7441ac4b82bSMike Smith     struct mlxd_softc		*mlxd;
7451ac4b82bSMike Smith     int				i, error;
7461ac4b82bSMike Smith 
7471ac4b82bSMike Smith     switch(cmd) {
7481ac4b82bSMike Smith 	/*
7491ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
7501ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
7511ac4b82bSMike Smith 	 * a valid unit on this controller.
7521ac4b82bSMike Smith 	 */
7531ac4b82bSMike Smith     case MLX_NEXT_CHILD:
7541ac4b82bSMike Smith 	/* search system drives */
7551ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
7561ac4b82bSMike Smith 	    /* is this one attached? */
7571ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
7581ac4b82bSMike Smith 		/* looking for the next one we come across? */
7591ac4b82bSMike Smith 		if (*arg == -1) {
760421f2f7dSMike Smith 		    *arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk);
7611ac4b82bSMike Smith 		    return(0);
7621ac4b82bSMike Smith 		}
7631ac4b82bSMike Smith 		/* we want the one after this one */
7641ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
7651ac4b82bSMike Smith 		    *arg = -1;
7661ac4b82bSMike Smith 	    }
7671ac4b82bSMike Smith 	}
7681ac4b82bSMike Smith 	return(ENOENT);
7691ac4b82bSMike Smith 
7701ac4b82bSMike Smith 	/*
7711ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
7721ac4b82bSMike Smith 	 */
7731ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
7741ac4b82bSMike Smith 	mlx_startup(sc);
7751ac4b82bSMike Smith 	return(0);
7761ac4b82bSMike Smith 
7771ac4b82bSMike Smith 	/*
7781ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
7791ac4b82bSMike Smith 	 * away.
7801ac4b82bSMike Smith 	 */
7811ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
7821ac4b82bSMike Smith 
7831ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
7841ac4b82bSMike Smith 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
7851ac4b82bSMike Smith 	    return(ENOENT);
7861ac4b82bSMike Smith 
7871ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
7881ac4b82bSMike Smith 	error = 0;
7891ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
7901ac4b82bSMike Smith 	    error = EBUSY;
7911ac4b82bSMike Smith 	    goto detach_out;
7921ac4b82bSMike Smith 	}
7931ac4b82bSMike Smith 
7941ac4b82bSMike Smith 	/* flush controller */
7951ac4b82bSMike Smith 	if (mlx_flush(sc)) {
7961ac4b82bSMike Smith 	    error = EBUSY;
7971ac4b82bSMike Smith 	    goto detach_out;
7981ac4b82bSMike Smith 	}
7991ac4b82bSMike Smith 
8001ac4b82bSMike Smith 	/* nuke drive */
8011ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
8021ac4b82bSMike Smith 	    goto detach_out;
8031ac4b82bSMike Smith 	dr->ms_disk = 0;
8041ac4b82bSMike Smith 
8051ac4b82bSMike Smith     detach_out:
8061ac4b82bSMike Smith 	if (error) {
8071ac4b82bSMike Smith 	    printf("failed\n");
8081ac4b82bSMike Smith 	} else {
8091ac4b82bSMike Smith 	    printf("done\n");
8101ac4b82bSMike Smith 	}
8111ac4b82bSMike Smith 	return(error);
8121ac4b82bSMike Smith 
8131ac4b82bSMike Smith 	/*
8141ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
8151ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
8161ac4b82bSMike Smith 	 *
8171ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
8181ac4b82bSMike Smith 	 * to do this right.
8191ac4b82bSMike Smith 	 */
8201ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
8211ac4b82bSMike Smith 	/* Does this command work on this firmware? */
8221ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
8231ac4b82bSMike Smith 	    return(EOPNOTSUPP);
8241ac4b82bSMike Smith 
8251ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
8261ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
8271ac4b82bSMike Smith 	    /* cancel a pending pause operation */
8281ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
8291ac4b82bSMike Smith 	} else {
8301ac4b82bSMike Smith 	    /* fix for legal channels */
8319eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
8321ac4b82bSMike Smith 	    /* check time values */
8331ac4b82bSMike Smith 	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
8341ac4b82bSMike Smith 		return(EINVAL);
8351ac4b82bSMike Smith 	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
8361ac4b82bSMike Smith 		return(EINVAL);
8371ac4b82bSMike Smith 
8381ac4b82bSMike Smith 	    /* check for a pause currently running */
8391ac4b82bSMike Smith 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
8401ac4b82bSMike Smith 		return(EBUSY);
8411ac4b82bSMike Smith 
8421ac4b82bSMike Smith 	    /* looks ok, go with it */
8431ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
8441ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
8451ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
8461ac4b82bSMike Smith 	}
8471ac4b82bSMike Smith 	return(0);
8481ac4b82bSMike Smith 
8491ac4b82bSMike Smith 	/*
8501ac4b82bSMike Smith 	 * Accept a command passthrough-style.
8511ac4b82bSMike Smith 	 */
8521ac4b82bSMike Smith     case MLX_COMMAND:
8531ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
8541ac4b82bSMike Smith 
855421f2f7dSMike Smith 	/*
856421f2f7dSMike Smith 	 * Start a rebuild on a given SCSI disk
857421f2f7dSMike Smith 	 */
858421f2f7dSMike Smith     case MLX_REBUILDASYNC:
859421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
860421f2f7dSMike Smith 	    rb->rr_status = 0x0106;
861421f2f7dSMike Smith 	    return(EBUSY);
862421f2f7dSMike Smith 	}
863421f2f7dSMike Smith 	rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
864421f2f7dSMike Smith 	switch (rb->rr_status) {
865421f2f7dSMike Smith 	case 0:
866421f2f7dSMike Smith 	    error = 0;
867421f2f7dSMike Smith 	    break;
868421f2f7dSMike Smith 	case 0x10000:
869421f2f7dSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
870421f2f7dSMike Smith 	    break;
871421f2f7dSMike Smith 	case 0x0002:
872421f2f7dSMike Smith 	    error = EBUSY;
873421f2f7dSMike Smith 	    break;
874421f2f7dSMike Smith 	case 0x0104:
875421f2f7dSMike Smith 	    error = EIO;
876421f2f7dSMike Smith 	    break;
877421f2f7dSMike Smith 	case 0x0105:
878421f2f7dSMike Smith 	    error = ERANGE;
879421f2f7dSMike Smith 	    break;
880421f2f7dSMike Smith 	case 0x0106:
881421f2f7dSMike Smith 	    error = EBUSY;
882421f2f7dSMike Smith 	    break;
883421f2f7dSMike Smith 	default:
884421f2f7dSMike Smith 	    error = EINVAL;
885421f2f7dSMike Smith 	    break;
886421f2f7dSMike Smith 	}
887421f2f7dSMike Smith 	if (error == 0)
888421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_REBUILD;
889421f2f7dSMike Smith 	return(error);
890421f2f7dSMike Smith 
891421f2f7dSMike Smith 	/*
892421f2f7dSMike Smith 	 * Get the status of the current rebuild or consistency check.
893421f2f7dSMike Smith 	 */
894421f2f7dSMike Smith     case MLX_REBUILDSTAT:
895421f2f7dSMike Smith 	*rs = sc->mlx_rebuildstat;
896421f2f7dSMike Smith 	return(0);
897421f2f7dSMike Smith 
898421f2f7dSMike Smith 	/*
899421f2f7dSMike Smith 	 * Return the per-controller system drive number matching the
900421f2f7dSMike Smith 	 * disk device number in (arg), if it happens to belong to us.
901421f2f7dSMike Smith 	 */
902421f2f7dSMike Smith     case MLX_GET_SYSDRIVE:
903421f2f7dSMike Smith 	error = ENOENT;
904421f2f7dSMike Smith 	mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg);
905421f2f7dSMike Smith 	if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
906421f2f7dSMike Smith 	    (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
907421f2f7dSMike Smith 	    error = 0;
908421f2f7dSMike Smith 	    *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
909421f2f7dSMike Smith 	}
910421f2f7dSMike Smith 	return(error);
911421f2f7dSMike Smith 
9121ac4b82bSMike Smith     default:
9131ac4b82bSMike Smith 	return(ENOTTY);
9141ac4b82bSMike Smith     }
9151ac4b82bSMike Smith }
9161ac4b82bSMike Smith 
9171ac4b82bSMike Smith /********************************************************************************
9181ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
9191ac4b82bSMike Smith  */
9201ac4b82bSMike Smith int
9211ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
922b40ce416SJulian Elischer 		caddr_t addr, int32_t flag, struct thread *td)
9231ac4b82bSMike Smith {
9241ac4b82bSMike Smith     int				*arg = (int *)addr;
925421f2f7dSMike Smith     int				error, result;
9261ac4b82bSMike Smith 
9271ac4b82bSMike Smith     switch(cmd) {
9281ac4b82bSMike Smith 	/*
9291ac4b82bSMike Smith 	 * Return the current status of this drive.
9301ac4b82bSMike Smith 	 */
9311ac4b82bSMike Smith     case MLXD_STATUS:
9321ac4b82bSMike Smith 	*arg = drive->ms_state;
9331ac4b82bSMike Smith 	return(0);
9341ac4b82bSMike Smith 
9351ac4b82bSMike Smith 	/*
936421f2f7dSMike Smith 	 * Start a background consistency check on this drive.
9371ac4b82bSMike Smith 	 */
938421f2f7dSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
939421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
940421f2f7dSMike Smith 	    *arg = 0x0106;
9411ac4b82bSMike Smith 	    return(EBUSY);
942421f2f7dSMike Smith 	}
943421f2f7dSMike Smith 	result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
944421f2f7dSMike Smith 	switch (result) {
9451ac4b82bSMike Smith 	case 0:
9461ac4b82bSMike Smith 	    error = 0;
9471ac4b82bSMike Smith 	    break;
9481ac4b82bSMike Smith 	case 0x10000:
9491ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
9501ac4b82bSMike Smith 	    break;
9511ac4b82bSMike Smith 	case 0x0002:
9521ac4b82bSMike Smith 	    error = EIO;
9531ac4b82bSMike Smith 	    break;
9541ac4b82bSMike Smith 	case 0x0105:
9551ac4b82bSMike Smith 	    error = ERANGE;
9561ac4b82bSMike Smith 	    break;
957421f2f7dSMike Smith 	case 0x0106:
958421f2f7dSMike Smith 	    error = EBUSY;
959421f2f7dSMike Smith 	    break;
9601ac4b82bSMike Smith 	default:
9611ac4b82bSMike Smith 	    error = EINVAL;
9621ac4b82bSMike Smith 	    break;
9631ac4b82bSMike Smith 	}
964421f2f7dSMike Smith 	if (error == 0)
965421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_CHECK;
966421f2f7dSMike Smith 	*arg = result;
9671ac4b82bSMike Smith 	return(error);
9681ac4b82bSMike Smith 
9691ac4b82bSMike Smith     }
9701ac4b82bSMike Smith     return(ENOIOCTL);
9711ac4b82bSMike Smith }
9721ac4b82bSMike Smith 
9731ac4b82bSMike Smith 
9741ac4b82bSMike Smith /********************************************************************************
9751ac4b82bSMike Smith  ********************************************************************************
9761ac4b82bSMike Smith                                                                 Status Monitoring
9771ac4b82bSMike Smith  ********************************************************************************
9781ac4b82bSMike Smith  ********************************************************************************/
9791ac4b82bSMike Smith 
9801ac4b82bSMike Smith /********************************************************************************
9811ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
9821ac4b82bSMike Smith  */
9831ac4b82bSMike Smith static void
9841ac4b82bSMike Smith mlx_periodic(void *data)
9851ac4b82bSMike Smith {
9861ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
9871ac4b82bSMike Smith 
988da8bb3a3SMike Smith     debug_called(1);
9891ac4b82bSMike Smith 
9901ac4b82bSMike Smith     /*
9911ac4b82bSMike Smith      * Run a bus pause?
9921ac4b82bSMike Smith      */
9931ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
9941ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
9951ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
9961ac4b82bSMike Smith 
9971ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
9981ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
9991ac4b82bSMike Smith 	sysbeep(500, hz);
10001ac4b82bSMike Smith 
10011ac4b82bSMike Smith 	/*
10021ac4b82bSMike Smith 	 * Bus pause still running?
10031ac4b82bSMike Smith 	 */
10041ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
10051ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
10061ac4b82bSMike Smith 
10071ac4b82bSMike Smith 	/* time to stop bus pause? */
10081ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
10091ac4b82bSMike Smith 	    mlx_pause_action(sc);
10101ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
10111ac4b82bSMike Smith 	    sysbeep(500, hz);
10121ac4b82bSMike Smith 	} else {
10131ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
10141ac4b82bSMike Smith 	}
10151ac4b82bSMike Smith 
10161ac4b82bSMike Smith 	/*
10171ac4b82bSMike Smith 	 * Run normal periodic activities?
10181ac4b82bSMike Smith 	 */
10195792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
10201ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
10211ac4b82bSMike Smith 
10221ac4b82bSMike Smith 	/*
10231ac4b82bSMike Smith 	 * Check controller status.
10245792b7feSMike Smith 	 *
10255792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
10261ac4b82bSMike Smith 	 */
1027da8bb3a3SMike Smith 	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
1028da8bb3a3SMike Smith 		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
10291ac4b82bSMike Smith 
10301ac4b82bSMike Smith 	/*
10311ac4b82bSMike Smith 	 * Check system drive status.
10321ac4b82bSMike Smith 	 *
10331ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
10341ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
10351ac4b82bSMike Smith 	 *     online.
10361ac4b82bSMike Smith 	 */
10371ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
10381ac4b82bSMike Smith 			mlx_periodic_enquiry);
10391ac4b82bSMike Smith 
10401ac4b82bSMike Smith     }
10411ac4b82bSMike Smith 
1042421f2f7dSMike Smith     /* get drive rebuild/check status */
1043421f2f7dSMike Smith     /* XXX should check sc->mlx_background if this is only valid while in progress */
1044421f2f7dSMike Smith     mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
1045421f2f7dSMike Smith 
10465792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
10475792b7feSMike Smith     mlx_done(sc);
10481ac4b82bSMike Smith 
10491ac4b82bSMike Smith     /* reschedule another poll next second or so */
10501ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
10511ac4b82bSMike Smith }
10521ac4b82bSMike Smith 
10531ac4b82bSMike Smith /********************************************************************************
10541ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
10551ac4b82bSMike Smith  */
10561ac4b82bSMike Smith static void
10571ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
10581ac4b82bSMike Smith {
10591ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
10601ac4b82bSMike Smith 
1061da8bb3a3SMike Smith     debug_called(1);
10621ac4b82bSMike Smith 
10631ac4b82bSMike Smith     /* Command completed OK? */
10641ac4b82bSMike Smith     if (mc->mc_status != 0) {
1065da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
10661ac4b82bSMike Smith 	goto out;
10671ac4b82bSMike Smith     }
10681ac4b82bSMike Smith 
10691ac4b82bSMike Smith     /* respond to command */
10701ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
10711ac4b82bSMike Smith 	/*
1072da8bb3a3SMike Smith 	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
1073da8bb3a3SMike Smith 	 * pointer yet.
1074da8bb3a3SMike Smith 	 */
1075da8bb3a3SMike Smith     case MLX_CMD_ENQUIRY_OLD:
1076da8bb3a3SMike Smith     {
1077da8bb3a3SMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
1078da8bb3a3SMike Smith 	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
1079da8bb3a3SMike Smith 	int				i;
1080da8bb3a3SMike Smith 
1081da8bb3a3SMike Smith 	/* convert data in-place to new format */
1082da8bb3a3SMike Smith 	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
1083da8bb3a3SMike Smith 	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1084da8bb3a3SMike Smith 	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1085da8bb3a3SMike Smith 	}
1086da8bb3a3SMike Smith 	me->me_misc_flags        = 0;
1087da8bb3a3SMike Smith 	me->me_rebuild_count     = meo->me_rebuild_count;
1088da8bb3a3SMike Smith 	me->me_dead_count        = meo->me_dead_count;
1089da8bb3a3SMike Smith 	me->me_critical_sd_count = meo->me_critical_sd_count;
1090da8bb3a3SMike Smith 	me->me_event_log_seq_num = 0;
1091da8bb3a3SMike Smith 	me->me_offline_sd_count  = meo->me_offline_sd_count;
1092da8bb3a3SMike Smith 	me->me_max_commands      = meo->me_max_commands;
1093da8bb3a3SMike Smith 	me->me_rebuild_flag      = meo->me_rebuild_flag;
1094da8bb3a3SMike Smith 	me->me_fwmajor           = meo->me_fwmajor;
1095da8bb3a3SMike Smith 	me->me_fwminor           = meo->me_fwminor;
1096da8bb3a3SMike Smith 	me->me_status_flags      = meo->me_status_flags;
1097da8bb3a3SMike Smith 	me->me_flash_age         = meo->me_flash_age;
1098da8bb3a3SMike Smith 	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
1099da8bb3a3SMike Smith 	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
1100da8bb3a3SMike Smith 		me->me_drvsize[i] = 0;		/* drive beyond supported range */
1101da8bb3a3SMike Smith 	    } else {
1102da8bb3a3SMike Smith 		me->me_drvsize[i] = meo->me_drvsize[i];
1103da8bb3a3SMike Smith 	    }
1104da8bb3a3SMike Smith 	}
1105da8bb3a3SMike Smith 	me->me_num_sys_drvs = meo->me_num_sys_drvs;
1106da8bb3a3SMike Smith     }
1107da8bb3a3SMike Smith     /* FALLTHROUGH */
1108da8bb3a3SMike Smith 
1109da8bb3a3SMike Smith 	/*
11101ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
11111ac4b82bSMike Smith 	 * checking the event log.
11121ac4b82bSMike Smith 	 */
11131ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
11141ac4b82bSMike Smith     {
11151ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
11161ac4b82bSMike Smith 
1117421f2f7dSMike Smith 	if (sc->mlx_currevent == -1) {
11189eee27f1SMike Smith 	    /* initialise our view of the event log */
11199eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
11205d278f5cSMike Smith 	} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
11211ac4b82bSMike Smith 	    /* record where current events are up to */
11221ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
1123da8bb3a3SMike Smith 	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
11241ac4b82bSMike Smith 
11255d278f5cSMike Smith 	    /* mark the event log as busy */
11265d278f5cSMike Smith 	    atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
11275d278f5cSMike Smith 
11289eee27f1SMike Smith 	    /* drain new eventlog entries */
11291ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
11301ac4b82bSMike Smith 	}
11311ac4b82bSMike Smith 	break;
11321ac4b82bSMike Smith     }
11331ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
11341ac4b82bSMike Smith     {
11351ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
11361ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
11371ac4b82bSMike Smith 	int				i;
11381ac4b82bSMike Smith 
11391ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
11401ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
11411ac4b82bSMike Smith 	     i++) {
11421ac4b82bSMike Smith 
11431ac4b82bSMike Smith 	    /* has state been changed by controller? */
11441ac4b82bSMike Smith 	    if (dr->ms_state != mes[i].sd_state) {
11451ac4b82bSMike Smith 		switch(mes[i].sd_state) {
11461ac4b82bSMike Smith 		case MLX_SYSD_OFFLINE:
11471ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive offline\n");
11481ac4b82bSMike Smith 		    break;
11491ac4b82bSMike Smith 		case MLX_SYSD_ONLINE:
11501ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive online\n");
11511ac4b82bSMike Smith 		    break;
11521ac4b82bSMike Smith 		case MLX_SYSD_CRITICAL:
11531ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive critical\n");
11541ac4b82bSMike Smith 		    break;
11551ac4b82bSMike Smith 		}
11561ac4b82bSMike Smith 		/* save new state */
11571ac4b82bSMike Smith 		dr->ms_state = mes[i].sd_state;
11581ac4b82bSMike Smith 	    }
11591ac4b82bSMike Smith 	}
11601ac4b82bSMike Smith 	break;
11611ac4b82bSMike Smith     }
11621ac4b82bSMike Smith     default:
11636e551fb6SDavid E. O'Brien 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]);
11641ac4b82bSMike Smith 	break;
11651ac4b82bSMike Smith     }
11661ac4b82bSMike Smith 
11671ac4b82bSMike Smith  out:
11681ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
11691ac4b82bSMike Smith     mlx_releasecmd(mc);
11701ac4b82bSMike Smith }
11711ac4b82bSMike Smith 
11721ac4b82bSMike Smith /********************************************************************************
11731ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
11741ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
11751ac4b82bSMike Smith  */
11761ac4b82bSMike Smith static void
11771ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
11781ac4b82bSMike Smith {
11791ac4b82bSMike Smith     struct mlx_command	*mc;
11801ac4b82bSMike Smith     void		*result = NULL;
11811ac4b82bSMike Smith     int			error;
11821ac4b82bSMike Smith 
1183da8bb3a3SMike Smith     debug_called(1);
11841ac4b82bSMike Smith 
11851ac4b82bSMike Smith     /* get ourselves a command buffer */
11861ac4b82bSMike Smith     error = 1;
11871ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
11881ac4b82bSMike Smith 	goto out;
11891ac4b82bSMike Smith     /* allocate the response structure */
119033c8cb18SMike Smith     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL)
11911ac4b82bSMike Smith 	goto out;
11921ac4b82bSMike Smith     /* get a command slot */
11931ac4b82bSMike Smith     if (mlx_getslot(mc))
11941ac4b82bSMike Smith 	goto out;
11951ac4b82bSMike Smith 
11961ac4b82bSMike Smith     /* map the command so the controller can see it */
11971ac4b82bSMike Smith     mc->mc_data = result;
119833c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
11991ac4b82bSMike Smith     mlx_mapcmd(mc);
12001ac4b82bSMike Smith 
12011ac4b82bSMike Smith     /* build the command to get one entry */
12021ac4b82bSMike Smith     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
12031ac4b82bSMike Smith     mc->mc_complete = mlx_periodic_eventlog_respond;
12041ac4b82bSMike Smith     mc->mc_private = mc;
12051ac4b82bSMike Smith 
12061ac4b82bSMike Smith     /* start the command */
12071ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
12081ac4b82bSMike Smith 	goto out;
12091ac4b82bSMike Smith 
12101ac4b82bSMike Smith     error = 0;			/* success */
12111ac4b82bSMike Smith  out:
121233c8cb18SMike Smith     if (error != 0) {
12131ac4b82bSMike Smith 	if (mc != NULL)
12141ac4b82bSMike Smith 	    mlx_releasecmd(mc);
121533c8cb18SMike Smith 	if (result != NULL)
12161ac4b82bSMike Smith 	    free(result, M_DEVBUF);
12171ac4b82bSMike Smith     }
121833c8cb18SMike Smith }
12191ac4b82bSMike Smith 
12201ac4b82bSMike Smith /********************************************************************************
12211ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
12221ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
12231ac4b82bSMike Smith  */
12241ac4b82bSMike Smith static char *mlx_sense_messages[] = {
12251ac4b82bSMike Smith     "because write recovery failed",
12261ac4b82bSMike Smith     "because of SCSI bus reset failure",
12271ac4b82bSMike Smith     "because of double check condition",
12281ac4b82bSMike Smith     "because it was removed",
12291ac4b82bSMike Smith     "because of gross error on SCSI chip",
12301ac4b82bSMike Smith     "because of bad tag returned from drive",
12311ac4b82bSMike Smith     "because of timeout on SCSI command",
12321ac4b82bSMike Smith     "because of reset SCSI command issued from system",
12331ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
12341ac4b82bSMike Smith     "because of 'kill drive' command from system",
12351ac4b82bSMike Smith     "because of selection timeout",
12361ac4b82bSMike Smith     "due to SCSI phase sequence error",
12371ac4b82bSMike Smith     "due to unknown status"
12381ac4b82bSMike Smith };
12391ac4b82bSMike Smith 
12401ac4b82bSMike Smith static void
12411ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
12421ac4b82bSMike Smith {
12431ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
12441ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
12451ac4b82bSMike Smith     char			*reason;
12461ac4b82bSMike Smith 
1247da8bb3a3SMike Smith     debug_called(1);
12481ac4b82bSMike Smith 
12495792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
12501ac4b82bSMike Smith     if (mc->mc_status == 0) {
12511ac4b82bSMike Smith 
12521ac4b82bSMike Smith 	/* handle event log message */
12531ac4b82bSMike Smith 	switch(el->el_type) {
12541ac4b82bSMike Smith 	    /*
12551ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
12561ac4b82bSMike Smith 	     * The tests here are probably incomplete.
12571ac4b82bSMike Smith 	     */
12581ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
12591ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
12601ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
12611ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
12621ac4b82bSMike Smith 		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
12631ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
12641ac4b82bSMike Smith 		} else {
12651ac4b82bSMike Smith 		    reason = "for unknown reason";
12661ac4b82bSMike Smith 		}
12671ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
12681ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
12691ac4b82bSMike Smith 	    }
12701ac4b82bSMike Smith 	    /* SCSI drive was reset? */
12711ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
12721ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
12731ac4b82bSMike Smith 			      el->el_channel, el->el_target);
12741ac4b82bSMike Smith 	    }
12751ac4b82bSMike Smith 	    /* SCSI drive error? */
12761ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
12771ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
12781ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
12791ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
12801ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
12811ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
12821ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
12831ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
12841ac4b82bSMike Smith 	    }
12851ac4b82bSMike Smith 	    break;
12861ac4b82bSMike Smith 
12871ac4b82bSMike Smith 	default:
12881ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
12891ac4b82bSMike Smith 	    break;
12901ac4b82bSMike Smith 	}
12911ac4b82bSMike Smith     } else {
12921ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
12935d278f5cSMike Smith 	/* give up on all the outstanding messages, as we may have come unsynched */
12945d278f5cSMike Smith 	sc->mlx_lastevent = sc->mlx_currevent;
12951ac4b82bSMike Smith     }
12961ac4b82bSMike Smith 
12971ac4b82bSMike Smith     /* dispose of command and data */
12981ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12991ac4b82bSMike Smith     mlx_releasecmd(mc);
13001ac4b82bSMike Smith 
13011ac4b82bSMike Smith     /* is there another message to obtain? */
13025d278f5cSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent) {
13031ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
13045d278f5cSMike Smith     } else {
13055d278f5cSMike Smith 	/* clear log-busy status */
13065d278f5cSMike Smith 	atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
13075d278f5cSMike Smith     }
13081ac4b82bSMike Smith }
13091ac4b82bSMike Smith 
13101ac4b82bSMike Smith /********************************************************************************
1311421f2f7dSMike Smith  * Handle check/rebuild operations in progress.
13121ac4b82bSMike Smith  */
13131ac4b82bSMike Smith static void
13141ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
13151ac4b82bSMike Smith {
13161ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
1317421f2f7dSMike Smith     struct mlx_rebuild_status	*mr = (struct mlx_rebuild_status *)mc->mc_data;
13181ac4b82bSMike Smith 
13191ac4b82bSMike Smith     switch(mc->mc_status) {
1320421f2f7dSMike Smith     case 0:				/* operation running, update stats */
1321421f2f7dSMike Smith 	sc->mlx_rebuildstat = *mr;
1322421f2f7dSMike Smith 
1323421f2f7dSMike Smith 	/* spontaneous rebuild/check? */
1324421f2f7dSMike Smith 	if (sc->mlx_background == 0) {
1325421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
1326421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
1327421f2f7dSMike Smith 	}
13281ac4b82bSMike Smith 	break;
13291ac4b82bSMike Smith 
1330421f2f7dSMike Smith     case 0x0105:			/* nothing running, finalise stats and report */
1331421f2f7dSMike Smith 	switch(sc->mlx_background) {
1332421f2f7dSMike Smith 	case MLX_BACKGROUND_CHECK:
1333421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");	/* XXX print drive? */
1334421f2f7dSMike Smith 	    break;
1335421f2f7dSMike Smith 	case MLX_BACKGROUND_REBUILD:
1336421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "drive rebuild completed\n");	/* XXX print channel/target? */
1337421f2f7dSMike Smith 	    break;
1338421f2f7dSMike Smith 	case MLX_BACKGROUND_SPONTANEOUS:
1339421f2f7dSMike Smith 	default:
1340421f2f7dSMike Smith 	    /* if we have previously been non-idle, report the transition */
1341421f2f7dSMike Smith 	    if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
1342421f2f7dSMike Smith 		device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
13431ac4b82bSMike Smith 	    }
1344421f2f7dSMike Smith 	}
1345421f2f7dSMike Smith 	sc->mlx_background = 0;
1346421f2f7dSMike Smith 	sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
13471ac4b82bSMike Smith 	break;
13481ac4b82bSMike Smith     }
13491ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
13501ac4b82bSMike Smith     mlx_releasecmd(mc);
13511ac4b82bSMike Smith }
13521ac4b82bSMike Smith 
13531ac4b82bSMike Smith /********************************************************************************
13541ac4b82bSMike Smith  ********************************************************************************
13551ac4b82bSMike Smith                                                                     Channel Pause
13561ac4b82bSMike Smith  ********************************************************************************
13571ac4b82bSMike Smith  ********************************************************************************/
13581ac4b82bSMike Smith 
13591ac4b82bSMike Smith /********************************************************************************
13601ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
13611ac4b82bSMike Smith  * the pause.
13621ac4b82bSMike Smith  */
13631ac4b82bSMike Smith static void
13641ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
13651ac4b82bSMike Smith {
13661ac4b82bSMike Smith     struct mlx_command	*mc;
13671ac4b82bSMike Smith     int			failsafe, i, command;
13681ac4b82bSMike Smith 
13691ac4b82bSMike Smith     /* What are we doing here? */
13701ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
13711ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
13721ac4b82bSMike Smith 	failsafe = 0;
13731ac4b82bSMike Smith 
13741ac4b82bSMike Smith     } else {
13751ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
13761ac4b82bSMike Smith 
13771ac4b82bSMike Smith 	/*
13781ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
13791ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
13801ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
13811ac4b82bSMike Smith 	 */
13821ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
13831ac4b82bSMike Smith 	if (failsafe > 0xf) {
13841ac4b82bSMike Smith 	    failsafe = 0xf;
13851ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
13861ac4b82bSMike Smith 	}
13871ac4b82bSMike Smith     }
13881ac4b82bSMike Smith 
13891ac4b82bSMike Smith     /* build commands for every channel requested */
13909eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
13911ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
13921ac4b82bSMike Smith 
13931ac4b82bSMike Smith 	    /* get ourselves a command buffer */
13941ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
13951ac4b82bSMike Smith 		goto fail;
13961ac4b82bSMike Smith 	    /* get a command slot */
13971ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
13981ac4b82bSMike Smith 	    if (mlx_getslot(mc))
13991ac4b82bSMike Smith 		goto fail;
14001ac4b82bSMike Smith 
14011ac4b82bSMike Smith 	    /* build the command */
14021ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
14031ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
14041ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
14051ac4b82bSMike Smith 	    if (mlx_start(mc))
14061ac4b82bSMike Smith 		goto fail;
14071ac4b82bSMike Smith 	    /* command submitted OK */
14081ac4b82bSMike Smith 	    return;
14091ac4b82bSMike Smith 
14101ac4b82bSMike Smith 	fail:
14111ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
14121ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
14131ac4b82bSMike Smith 	    if (mc != NULL)
14141ac4b82bSMike Smith 		mlx_releasecmd(mc);
14151ac4b82bSMike Smith 	}
14161ac4b82bSMike Smith     }
14171ac4b82bSMike Smith }
14181ac4b82bSMike Smith 
14191ac4b82bSMike Smith static void
14201ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
14211ac4b82bSMike Smith {
14221ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
14231ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
14241ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
14251ac4b82bSMike Smith 
14261ac4b82bSMike Smith     if (mc->mc_status != 0) {
14271ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
14281ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
14291ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
14301ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
143172c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
14321ac4b82bSMike Smith     } else {
14331ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
14341ac4b82bSMike Smith     }
14351ac4b82bSMike Smith     mlx_releasecmd(mc);
14361ac4b82bSMike Smith }
14371ac4b82bSMike Smith 
14381ac4b82bSMike Smith /********************************************************************************
14391ac4b82bSMike Smith  ********************************************************************************
14401ac4b82bSMike Smith                                                                Command Submission
14411ac4b82bSMike Smith  ********************************************************************************
14421ac4b82bSMike Smith  ********************************************************************************/
14431ac4b82bSMike Smith 
14441ac4b82bSMike Smith /********************************************************************************
14451ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
14461ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
14471ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
14481ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
14491ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
14501ac4b82bSMike Smith  */
14511ac4b82bSMike Smith static void *
14521ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
14531ac4b82bSMike Smith {
14541ac4b82bSMike Smith     struct mlx_command	*mc;
14551ac4b82bSMike Smith     void		*result;
14561ac4b82bSMike Smith     int			error;
14571ac4b82bSMike Smith 
1458da8bb3a3SMike Smith     debug_called(1);
14591ac4b82bSMike Smith 
14601ac4b82bSMike Smith     /* get ourselves a command buffer */
14611ac4b82bSMike Smith     error = 1;
14621ac4b82bSMike Smith     result = NULL;
14631ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14641ac4b82bSMike Smith 	goto out;
14651ac4b82bSMike Smith     /* allocate the response structure */
14661ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
14671ac4b82bSMike Smith 	goto out;
14681ac4b82bSMike Smith     /* get a command slot */
14691ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
14701ac4b82bSMike Smith     if (mlx_getslot(mc))
14711ac4b82bSMike Smith 	goto out;
14721ac4b82bSMike Smith 
14731ac4b82bSMike Smith     /* map the command so the controller can see it */
14741ac4b82bSMike Smith     mc->mc_data = result;
14751ac4b82bSMike Smith     mc->mc_length = bufsize;
14761ac4b82bSMike Smith     mlx_mapcmd(mc);
14771ac4b82bSMike Smith 
14781ac4b82bSMike Smith     /* build an enquiry command */
14791ac4b82bSMike Smith     mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
14801ac4b82bSMike Smith 
14811ac4b82bSMike Smith     /* do we want a completion callback? */
14821ac4b82bSMike Smith     if (complete != NULL) {
14831ac4b82bSMike Smith 	mc->mc_complete = complete;
14841ac4b82bSMike Smith 	mc->mc_private = mc;
14851ac4b82bSMike Smith 	if ((error = mlx_start(mc)) != 0)
14861ac4b82bSMike Smith 	    goto out;
14871ac4b82bSMike Smith     } else {
14881ac4b82bSMike Smith 	/* run the command in either polled or wait mode */
14891ac4b82bSMike Smith 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
14901ac4b82bSMike Smith 	    goto out;
14911ac4b82bSMike Smith 
14921ac4b82bSMike Smith 	/* command completed OK? */
14931ac4b82bSMike Smith 	if (mc->mc_status != 0) {
14941ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc));
14951ac4b82bSMike Smith 	    goto out;
14961ac4b82bSMike Smith 	}
14971ac4b82bSMike Smith     }
14981ac4b82bSMike Smith     error = 0;			/* success */
14991ac4b82bSMike Smith  out:
15001ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
15011ac4b82bSMike Smith     if ((complete == NULL) && (mc != NULL))
15021ac4b82bSMike Smith 	mlx_releasecmd(mc);
150333c8cb18SMike Smith     /* we got an error, and we allocated a result */
15041ac4b82bSMike Smith     if ((error != 0) && (result != NULL)) {
15051ac4b82bSMike Smith 	free(result, M_DEVBUF);
15061ac4b82bSMike Smith 	result = NULL;
15071ac4b82bSMike Smith     }
15081ac4b82bSMike Smith     return(result);
15091ac4b82bSMike Smith }
15101ac4b82bSMike Smith 
15111ac4b82bSMike Smith 
15121ac4b82bSMike Smith /********************************************************************************
15131ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
15141ac4b82bSMike Smith  *
15151ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
15161ac4b82bSMike Smith  * the flush operation completes or fails.
15171ac4b82bSMike Smith  */
15181ac4b82bSMike Smith static int
15191ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
15201ac4b82bSMike Smith {
15211ac4b82bSMike Smith     struct mlx_command	*mc;
15221ac4b82bSMike Smith     int			error;
15231ac4b82bSMike Smith 
1524da8bb3a3SMike Smith     debug_called(1);
15251ac4b82bSMike Smith 
15261ac4b82bSMike Smith     /* get ourselves a command buffer */
15271ac4b82bSMike Smith     error = 1;
15281ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
15291ac4b82bSMike Smith 	goto out;
15301ac4b82bSMike Smith     /* get a command slot */
15311ac4b82bSMike Smith     if (mlx_getslot(mc))
15321ac4b82bSMike Smith 	goto out;
15331ac4b82bSMike Smith 
15341ac4b82bSMike Smith     /* build a flush command */
15351ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
15361ac4b82bSMike Smith 
15375792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
15385792b7feSMike Smith     if (mlx_poll_command(mc))
15391ac4b82bSMike Smith 	goto out;
15401ac4b82bSMike Smith 
15411ac4b82bSMike Smith     /* command completed OK? */
15421ac4b82bSMike Smith     if (mc->mc_status != 0) {
15431ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
15441ac4b82bSMike Smith 	goto out;
15451ac4b82bSMike Smith     }
15461ac4b82bSMike Smith 
15471ac4b82bSMike Smith     error = 0;			/* success */
15481ac4b82bSMike Smith  out:
15491ac4b82bSMike Smith     if (mc != NULL)
15501ac4b82bSMike Smith 	mlx_releasecmd(mc);
15511ac4b82bSMike Smith     return(error);
15521ac4b82bSMike Smith }
15531ac4b82bSMike Smith 
15541ac4b82bSMike Smith /********************************************************************************
1555421f2f7dSMike Smith  * Start a background consistency check on (drive).
1556421f2f7dSMike Smith  *
1557421f2f7dSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
1558421f2f7dSMike Smith  * operation has started or been refused.
1559421f2f7dSMike Smith  */
1560421f2f7dSMike Smith static int
1561421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive)
1562421f2f7dSMike Smith {
1563421f2f7dSMike Smith     struct mlx_command	*mc;
1564421f2f7dSMike Smith     int			error;
1565421f2f7dSMike Smith 
1566421f2f7dSMike Smith     debug_called(1);
1567421f2f7dSMike Smith 
1568421f2f7dSMike Smith     /* get ourselves a command buffer */
1569421f2f7dSMike Smith     error = 0x10000;
1570421f2f7dSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
1571421f2f7dSMike Smith 	goto out;
1572421f2f7dSMike Smith     /* get a command slot */
1573421f2f7dSMike Smith     if (mlx_getslot(mc))
1574421f2f7dSMike Smith 	goto out;
1575421f2f7dSMike Smith 
1576421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
1577421f2f7dSMike Smith     mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
1578421f2f7dSMike Smith 
1579421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1580421f2f7dSMike Smith     if (mlx_wait_command(mc))
1581421f2f7dSMike Smith 	goto out;
1582421f2f7dSMike Smith 
1583421f2f7dSMike Smith     /* command completed OK? */
1584421f2f7dSMike Smith     if (mc->mc_status != 0) {
1585421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
1586421f2f7dSMike Smith     } else {
1587421f2f7dSMike Smith 	device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
1588421f2f7dSMike Smith     }
1589421f2f7dSMike Smith     error = mc->mc_status;
1590421f2f7dSMike Smith 
1591421f2f7dSMike Smith  out:
1592421f2f7dSMike Smith     if (mc != NULL)
1593421f2f7dSMike Smith 	mlx_releasecmd(mc);
1594421f2f7dSMike Smith     return(error);
1595421f2f7dSMike Smith }
1596421f2f7dSMike Smith 
1597421f2f7dSMike Smith /********************************************************************************
1598421f2f7dSMike Smith  * Start a background rebuild of the physical drive at (channel),(target).
15991ac4b82bSMike Smith  *
16001ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
16011ac4b82bSMike Smith  * operation has started or been refused.
16021ac4b82bSMike Smith  */
16031ac4b82bSMike Smith static int
16041ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
16051ac4b82bSMike Smith {
16061ac4b82bSMike Smith     struct mlx_command	*mc;
16071ac4b82bSMike Smith     int			error;
16081ac4b82bSMike Smith 
1609da8bb3a3SMike Smith     debug_called(1);
16101ac4b82bSMike Smith 
16111ac4b82bSMike Smith     /* get ourselves a command buffer */
16121ac4b82bSMike Smith     error = 0x10000;
16131ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16141ac4b82bSMike Smith 	goto out;
16151ac4b82bSMike Smith     /* get a command slot */
16161ac4b82bSMike Smith     if (mlx_getslot(mc))
16171ac4b82bSMike Smith 	goto out;
16181ac4b82bSMike Smith 
1619421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
16201ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
16211ac4b82bSMike Smith 
1622421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1623421f2f7dSMike Smith     if (mlx_wait_command(mc))
16241ac4b82bSMike Smith 	goto out;
16251ac4b82bSMike Smith 
16261ac4b82bSMike Smith     /* command completed OK? */
16271ac4b82bSMike Smith     if (mc->mc_status != 0) {
16281ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
16291ac4b82bSMike Smith     } else {
1630421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
16311ac4b82bSMike Smith     }
16321ac4b82bSMike Smith     error = mc->mc_status;
16331ac4b82bSMike Smith 
16341ac4b82bSMike Smith  out:
16351ac4b82bSMike Smith     if (mc != NULL)
16361ac4b82bSMike Smith 	mlx_releasecmd(mc);
16371ac4b82bSMike Smith     return(error);
16381ac4b82bSMike Smith }
16391ac4b82bSMike Smith 
16401ac4b82bSMike Smith /********************************************************************************
16411ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
16421ac4b82bSMike Smith  *
16431ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
16441ac4b82bSMike Smith  */
16451ac4b82bSMike Smith static int
16461ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
16471ac4b82bSMike Smith {
16481ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16491ac4b82bSMike Smith     int			error, count;
16501ac4b82bSMike Smith 
1651da8bb3a3SMike Smith     debug_called(1);
16521ac4b82bSMike Smith 
16531ac4b82bSMike Smith     mc->mc_complete = NULL;
16541ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
16551ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
16561ac4b82bSMike Smith 	return(error);
16571ac4b82bSMike Smith 
16581ac4b82bSMike Smith     count = 0;
16591ac4b82bSMike Smith     /* XXX better timeout? */
16601ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
16611ac4b82bSMike Smith 	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
16621ac4b82bSMike Smith     }
16631ac4b82bSMike Smith 
16641ac4b82bSMike Smith     if (mc->mc_status != 0) {
1665da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
16661ac4b82bSMike Smith 	return(EIO);
16671ac4b82bSMike Smith     }
16681ac4b82bSMike Smith     return(0);
16691ac4b82bSMike Smith }
16701ac4b82bSMike Smith 
16711ac4b82bSMike Smith 
16721ac4b82bSMike Smith /********************************************************************************
16731ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
16741ac4b82bSMike Smith  *
1675da8bb3a3SMike Smith  * Should only be used when interrupts can't be relied upon. Returns 0 on
16761ac4b82bSMike Smith  * success, nonzero on error.
16771ac4b82bSMike Smith  * Successfully completed commands are dequeued.
16781ac4b82bSMike Smith  */
16791ac4b82bSMike Smith static int
16801ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
16811ac4b82bSMike Smith {
16821ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16831ac4b82bSMike Smith     int			error, count, s;
16841ac4b82bSMike Smith 
1685da8bb3a3SMike Smith     debug_called(1);
16861ac4b82bSMike Smith 
16871ac4b82bSMike Smith     mc->mc_complete = NULL;
16881ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
16891ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
16901ac4b82bSMike Smith 	return(error);
16911ac4b82bSMike Smith 
16921ac4b82bSMike Smith     count = 0;
16931ac4b82bSMike Smith     do {
16941ac4b82bSMike Smith 	/* poll for completion */
16951ac4b82bSMike Smith 	mlx_done(mc->mc_sc);
1696da8bb3a3SMike Smith 
1697da8bb3a3SMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
16981ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
16991ac4b82bSMike Smith 	s = splbio();
17004b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
17011ac4b82bSMike Smith 	splx(s);
17021ac4b82bSMike Smith 	return(0);
17031ac4b82bSMike Smith     }
1704421f2f7dSMike Smith     device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
17051ac4b82bSMike Smith     return(EIO);
17061ac4b82bSMike Smith }
17071ac4b82bSMike Smith 
17081ac4b82bSMike Smith /********************************************************************************
17091ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
17101ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
17111ac4b82bSMike Smith  *
17121ac4b82bSMike Smith  * Must be called at splbio or in an equivalent fashion that prevents
17138177437dSPoul-Henning Kamp  * reentry or activity on the bioq.
17141ac4b82bSMike Smith  */
17151ac4b82bSMike Smith static void
17161ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
17171ac4b82bSMike Smith {
17181ac4b82bSMike Smith     struct mlx_command	*mc;
17191ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
172015fd5d22SMike Smith     mlx_bio		*bp;
17211ac4b82bSMike Smith     int			blkcount;
17221ac4b82bSMike Smith     int			driveno;
17231ac4b82bSMike Smith     int			cmd;
17244b006d7bSMike Smith     int			s;
17251ac4b82bSMike Smith 
17265792b7feSMike Smith     /* avoid reentrancy */
17275792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_STARTING))
17285792b7feSMike Smith 	return;
17295792b7feSMike Smith 
17301ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
17314b006d7bSMike Smith     s = splbio();
17321ac4b82bSMike Smith     for (;;) {
17331ac4b82bSMike Smith 
17341ac4b82bSMike Smith 	/* see if there's work to be done */
173515fd5d22SMike Smith 	if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL)
17361ac4b82bSMike Smith 	    break;
17371ac4b82bSMike Smith 	/* get a command */
17381ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
17391ac4b82bSMike Smith 	    break;
17401ac4b82bSMike Smith 	/* get a slot for the command */
17411ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
17421ac4b82bSMike Smith 	    mlx_releasecmd(mc);
17431ac4b82bSMike Smith 	    break;
17441ac4b82bSMike Smith 	}
17451ac4b82bSMike Smith 	/* get the buf containing our work */
174615fd5d22SMike Smith 	MLX_BIO_QREMOVE(sc->mlx_bioq, bp);
17471ac4b82bSMike Smith 	sc->mlx_waitbufs--;
17484b006d7bSMike Smith 	splx(s);
17491ac4b82bSMike Smith 
17501ac4b82bSMike Smith 	/* connect the buf to the command */
17511ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
17521ac4b82bSMike Smith 	mc->mc_private = bp;
175315fd5d22SMike Smith 	mc->mc_data = MLX_BIO_DATA(bp);
175415fd5d22SMike Smith 	mc->mc_length = MLX_BIO_LENGTH(bp);
175515fd5d22SMike Smith 	if (MLX_BIO_IS_READ(bp)) {
17561ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAIN;
1757da8bb3a3SMike Smith 	    cmd = MLX_CMD_READSG;
17581ac4b82bSMike Smith 	} else {
17591ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAOUT;
1760da8bb3a3SMike Smith 	    cmd = MLX_CMD_WRITESG;
17611ac4b82bSMike Smith 	}
17621ac4b82bSMike Smith 
17631ac4b82bSMike Smith 	/* map the command so the controller can work with it */
17641ac4b82bSMike Smith 	mlx_mapcmd(mc);
17651ac4b82bSMike Smith 
17661ac4b82bSMike Smith 	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
176715fd5d22SMike Smith 	mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp);
17685792b7feSMike Smith 	driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
176915fd5d22SMike Smith 	blkcount = (MLX_BIO_LENGTH(bp) + MLX_BLKSIZE - 1) / MLX_BLKSIZE;
17701ac4b82bSMike Smith 
177115fd5d22SMike Smith 	if ((MLX_BIO_LBA(bp) + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
177207e929daSBruce Evans 	    device_printf(sc->mlx_dev,
177307e929daSBruce Evans 			  "I/O beyond end of unit (%lld,%d > %lu)\n",
177407e929daSBruce Evans 			  (long long)MLX_BIO_LBA(bp), blkcount,
177507e929daSBruce Evans 			  (u_long)sc->mlx_sysdrive[driveno].ms_size);
17761ac4b82bSMike Smith 
17771ac4b82bSMike Smith 	/*
17781ac4b82bSMike Smith 	 * Build the I/O command.  Note that the SG list type bits are set to zero,
17791ac4b82bSMike Smith 	 * denoting the format of SG list that we are using.
17801ac4b82bSMike Smith 	 */
1781da8bb3a3SMike Smith 	if (sc->mlx_iftype == MLX_IFTYPE_2) {
1782da8bb3a3SMike Smith 	    mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD,
1783da8bb3a3SMike Smith 			   blkcount & 0xff, 				/* xfer length low byte */
178415fd5d22SMike Smith 			   MLX_BIO_LBA(bp),				/* physical block number */
1785da8bb3a3SMike Smith 			   driveno,					/* target drive number */
1786da8bb3a3SMike Smith 			   mc->mc_sgphys,				/* location of SG list */
1787da8bb3a3SMike Smith 			   mc->mc_nsgent & 0x3f);			/* size of SG list (top 3 bits clear) */
1788da8bb3a3SMike Smith 	} else {
17891ac4b82bSMike Smith 	    mlx_make_type5(mc, cmd,
17901ac4b82bSMike Smith 			   blkcount & 0xff, 				/* xfer length low byte */
1791f01f2af6SMike Smith 			   (driveno << 3) | ((blkcount >> 8) & 0x07),	/* target and length high 3 bits */
179215fd5d22SMike Smith 			   MLX_BIO_LBA(bp),				/* physical block number */
17931ac4b82bSMike Smith 			   mc->mc_sgphys,				/* location of SG list */
1794da8bb3a3SMike Smith 			   mc->mc_nsgent & 0x3f);			/* size of SG list (top 3 bits clear) */
1795da8bb3a3SMike Smith 	}
17961ac4b82bSMike Smith 
17971ac4b82bSMike Smith 	/* try to give command to controller */
17981ac4b82bSMike Smith 	if (mlx_start(mc) != 0) {
17991ac4b82bSMike Smith 	    /* fail the command */
18001ac4b82bSMike Smith 	    mc->mc_status = MLX_STATUS_WEDGED;
18011ac4b82bSMike Smith 	    mlx_completeio(mc);
18021ac4b82bSMike Smith 	}
18034b006d7bSMike Smith 	s = splbio();
18041ac4b82bSMike Smith     }
18054b006d7bSMike Smith     splx(s);
18065792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_STARTING);
18071ac4b82bSMike Smith }
18081ac4b82bSMike Smith 
18091ac4b82bSMike Smith /********************************************************************************
18101ac4b82bSMike Smith  * Handle completion of an I/O command.
18111ac4b82bSMike Smith  */
18121ac4b82bSMike Smith static void
18131ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
18141ac4b82bSMike Smith {
18151ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
181615fd5d22SMike Smith     mlx_bio		*bp = (mlx_bio *)mc->mc_private;
181715fd5d22SMike Smith     struct mlxd_softc	*mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp);
18181ac4b82bSMike Smith 
18191ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
182015fd5d22SMike Smith 	MLX_BIO_SET_ERROR(bp, EIO);
18211ac4b82bSMike Smith 
18221ac4b82bSMike Smith 	switch(mc->mc_status) {
18231ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
18241ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1825f6b84b08SMike Smith 	    /* should signal this with a return code */
18261ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
18271ac4b82bSMike Smith 	    break;
18281ac4b82bSMike Smith 
18291ac4b82bSMike Smith 	default:				/* other I/O error */
18301ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
18311ac4b82bSMike Smith #if 0
1832cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
183315fd5d22SMike Smith 			  MLX_BIO_LENGTH(bp), MLX_BIO_LENGTH(bp) / MLX_BLKSIZE, MLX_BIO_LBA(bp));
18341ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
18351ac4b82bSMike Smith #endif
18361ac4b82bSMike Smith 	    break;
18371ac4b82bSMike Smith 	}
18381ac4b82bSMike Smith     }
18391ac4b82bSMike Smith     mlx_releasecmd(mc);
18401ac4b82bSMike Smith     mlxd_intr(bp);
18411ac4b82bSMike Smith }
18421ac4b82bSMike Smith 
18431ac4b82bSMike Smith /********************************************************************************
18441ac4b82bSMike Smith  * Take a command from user-space and try to run it.
1845da8bb3a3SMike Smith  *
1846da8bb3a3SMike Smith  * XXX Note that this can't perform very much in the way of error checking, and
1847da8bb3a3SMike Smith  *     as such, applications _must_ be considered trustworthy.
1848da8bb3a3SMike Smith  * XXX Commands using S/G for data are not supported.
18491ac4b82bSMike Smith  */
18501ac4b82bSMike Smith static int
18511ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
18521ac4b82bSMike Smith {
18531ac4b82bSMike Smith     struct mlx_command	*mc;
1854da8bb3a3SMike Smith     struct mlx_dcdb	*dcdb;
18551ac4b82bSMike Smith     void		*kbuf;
18561ac4b82bSMike Smith     int			error;
18571ac4b82bSMike Smith 
1858da8bb3a3SMike Smith     debug_called(0);
1859da8bb3a3SMike Smith 
18601ac4b82bSMike Smith     kbuf = NULL;
18611ac4b82bSMike Smith     mc = NULL;
1862da8bb3a3SMike Smith     dcdb = NULL;
18631ac4b82bSMike Smith     error = ENOMEM;
1864da8bb3a3SMike Smith 
1865da8bb3a3SMike Smith     /* get ourselves a command and copy in from user space */
18661ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
18671ac4b82bSMike Smith 	goto out;
18681ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
1869da8bb3a3SMike Smith     debug(0, "got command buffer");
1870da8bb3a3SMike Smith 
1871da8bb3a3SMike Smith     /* if we need a buffer for data transfer, allocate one and copy in its initial contents */
1872da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1873bf61e266SKris Kennaway 	if (mu->mu_datasize > MAXPHYS)
1874bf61e266SKris Kennaway 	    return (EINVAL);
1875a163d034SWarner Losh 	if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
1876da8bb3a3SMike Smith 	    (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))
18771ac4b82bSMike Smith 	    goto out;
1878da8bb3a3SMike Smith 	debug(0, "got kernel buffer");
1879da8bb3a3SMike Smith     }
18801ac4b82bSMike Smith 
18811ac4b82bSMike Smith     /* get a command slot */
18821ac4b82bSMike Smith     if (mlx_getslot(mc))
18831ac4b82bSMike Smith 	goto out;
1884da8bb3a3SMike Smith     debug(0, "got a slot");
18851ac4b82bSMike Smith 
18861ac4b82bSMike Smith     /* map the command so the controller can see it */
18871ac4b82bSMike Smith     mc->mc_data = kbuf;
18881ac4b82bSMike Smith     mc->mc_length = mu->mu_datasize;
18891ac4b82bSMike Smith     mlx_mapcmd(mc);
1890da8bb3a3SMike Smith     debug(0, "mapped");
18911ac4b82bSMike Smith 
1892da8bb3a3SMike Smith     /*
1893da8bb3a3SMike Smith      * If this is a passthrough SCSI command, the DCDB is packed at the
1894da8bb3a3SMike Smith      * beginning of the data area.  Fix up the DCDB to point to the correct physical
1895da8bb3a3SMike Smith      * address and override any bufptr supplied by the caller since we know
1896da8bb3a3SMike Smith      * what it's meant to be.
1897da8bb3a3SMike Smith      */
1898da8bb3a3SMike Smith     if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
1899da8bb3a3SMike Smith 	dcdb = (struct mlx_dcdb *)kbuf;
1900da8bb3a3SMike Smith 	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
1901da8bb3a3SMike Smith 	mu->mu_bufptr = 8;
19021ac4b82bSMike Smith     }
19031ac4b82bSMike Smith 
1904da8bb3a3SMike Smith     /*
1905da8bb3a3SMike Smith      * If there's a data buffer, fix up the command's buffer pointer.
1906da8bb3a3SMike Smith      */
1907da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1908da8bb3a3SMike Smith 
1909da8bb3a3SMike Smith 	/* range check the pointer to physical buffer address */
1910da8bb3a3SMike Smith 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - sizeof(u_int32_t)))) {
1911da8bb3a3SMike Smith 	    error = EINVAL;
1912da8bb3a3SMike Smith 	    goto out;
1913da8bb3a3SMike Smith 	}
1914da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
1915da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
1916da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
1917da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
1918da8bb3a3SMike Smith     }
1919da8bb3a3SMike Smith     debug(0, "command fixup");
1920da8bb3a3SMike Smith 
19211ac4b82bSMike Smith     /* submit the command and wait */
19221ac4b82bSMike Smith     if ((error = mlx_wait_command(mc)) != 0)
19231ac4b82bSMike Smith 	goto out;
19241ac4b82bSMike Smith 
19251ac4b82bSMike Smith     /* copy out status and data */
19261ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
19271ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
19281ac4b82bSMike Smith 	goto out;
19291ac4b82bSMike Smith     error = 0;
19301ac4b82bSMike Smith 
19311ac4b82bSMike Smith  out:
19321ac4b82bSMike Smith     mlx_releasecmd(mc);
19331ac4b82bSMike Smith     if (kbuf != NULL)
19341ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
19351ac4b82bSMike Smith     return(error);
19361ac4b82bSMike Smith }
19371ac4b82bSMike Smith 
19381ac4b82bSMike Smith /********************************************************************************
19391ac4b82bSMike Smith  ********************************************************************************
19401ac4b82bSMike Smith                                                         Command I/O to Controller
19411ac4b82bSMike Smith  ********************************************************************************
19421ac4b82bSMike Smith  ********************************************************************************/
19431ac4b82bSMike Smith 
19441ac4b82bSMike Smith /********************************************************************************
19451ac4b82bSMike Smith  * Find a free command slot for (mc).
19461ac4b82bSMike Smith  *
19471ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
19481ac4b82bSMike Smith  * 4 slots free for priority commands.
19491ac4b82bSMike Smith  */
19501ac4b82bSMike Smith static int
19511ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
19521ac4b82bSMike Smith {
19531ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
1954baff09dbSMike Smith     int			s, slot, limit;
19551ac4b82bSMike Smith 
1956da8bb3a3SMike Smith     debug_called(1);
19571ac4b82bSMike Smith 
1958baff09dbSMike Smith     /*
1959baff09dbSMike Smith      * Enforce slot-usage limit, if we have the required information.
1960baff09dbSMike Smith      */
1961baff09dbSMike Smith     if (sc->mlx_enq2 != NULL) {
1962baff09dbSMike Smith 	limit = sc->mlx_enq2->me_max_commands;
1963baff09dbSMike Smith     } else {
1964baff09dbSMike Smith 	limit = 2;
1965baff09dbSMike Smith     }
1966baff09dbSMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4))
19671ac4b82bSMike Smith 	return(EBUSY);
19681ac4b82bSMike Smith 
19691ac4b82bSMike Smith     /*
19701ac4b82bSMike Smith      * Allocate an outstanding command slot
19711ac4b82bSMike Smith      *
19721ac4b82bSMike Smith      * XXX linear search is slow
19731ac4b82bSMike Smith      */
19741ac4b82bSMike Smith     s = splbio();
1975baff09dbSMike Smith     for (slot = 0; slot < limit; slot++) {
1976da8bb3a3SMike Smith 	debug(2, "try slot %d", slot);
19771ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
19781ac4b82bSMike Smith 	    break;
19791ac4b82bSMike Smith     }
1980baff09dbSMike Smith     if (slot < limit) {
19811ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
19821ac4b82bSMike Smith 	sc->mlx_busycmds++;
19831ac4b82bSMike Smith     }
19841ac4b82bSMike Smith     splx(s);
19851ac4b82bSMike Smith 
19861ac4b82bSMike Smith     /* out of slots? */
1987baff09dbSMike Smith     if (slot >= limit)
19881ac4b82bSMike Smith 	return(EBUSY);
19891ac4b82bSMike Smith 
1990da8bb3a3SMike Smith     debug(2, "got slot %d", slot);
19911ac4b82bSMike Smith     mc->mc_slot = slot;
19921ac4b82bSMike Smith     return(0);
19931ac4b82bSMike Smith }
19941ac4b82bSMike Smith 
19951ac4b82bSMike Smith /********************************************************************************
19961ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
19971ac4b82bSMike Smith  */
19981ac4b82bSMike Smith static void
19991ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
20001ac4b82bSMike Smith {
20011ac4b82bSMike Smith     struct mlx_command	*mc = (struct mlx_command *)arg;
20021ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20031ac4b82bSMike Smith     struct mlx_sgentry	*sg;
20041ac4b82bSMike Smith     int			i;
20051ac4b82bSMike Smith 
2006da8bb3a3SMike Smith     debug_called(1);
20071ac4b82bSMike Smith 
2008baff09dbSMike Smith     /* XXX should be unnecessary */
2009baff09dbSMike Smith     if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
2010baff09dbSMike Smith 	panic("MLX: too many s/g segments (%d, max %d)", nsegments, sc->mlx_enq2->me_max_sg);
2011baff09dbSMike Smith 
20121ac4b82bSMike Smith     /* get base address of s/g table */
2013baff09dbSMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
20141ac4b82bSMike Smith 
20151ac4b82bSMike Smith     /* save s/g table information in command */
20161ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
2017baff09dbSMike Smith     mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
20181ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
20191ac4b82bSMike Smith 
20201ac4b82bSMike Smith     /* populate s/g table */
20211ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
20221ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
20231ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
20241ac4b82bSMike Smith     }
20251ac4b82bSMike Smith }
20261ac4b82bSMike Smith 
20271ac4b82bSMike Smith static void
20281ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc)
20291ac4b82bSMike Smith {
20301ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20311ac4b82bSMike Smith 
2032da8bb3a3SMike Smith     debug_called(1);
20331ac4b82bSMike Smith 
20341ac4b82bSMike Smith     /* if the command involves data at all */
20351ac4b82bSMike Smith     if (mc->mc_data != NULL) {
20361ac4b82bSMike Smith 
20371ac4b82bSMike Smith 	/* map the data buffer into bus space and build the s/g list */
20381ac4b82bSMike Smith 	bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length,
20391ac4b82bSMike Smith 			mlx_setup_dmamap, mc, 0);
20401ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
20411ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD);
20421ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
20431ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE);
20441ac4b82bSMike Smith     }
20451ac4b82bSMike Smith }
20461ac4b82bSMike Smith 
20471ac4b82bSMike Smith static void
20481ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
20491ac4b82bSMike Smith {
20501ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20511ac4b82bSMike Smith 
2052da8bb3a3SMike Smith     debug_called(1);
20531ac4b82bSMike Smith 
20541ac4b82bSMike Smith     /* if the command involved data at all */
20551ac4b82bSMike Smith     if (mc->mc_data != NULL) {
20561ac4b82bSMike Smith 
20571ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
20581ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
20591ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
20601ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
20611ac4b82bSMike Smith 
20621ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
20631ac4b82bSMike Smith     }
20641ac4b82bSMike Smith }
20651ac4b82bSMike Smith 
20661ac4b82bSMike Smith /********************************************************************************
20675792b7feSMike Smith  * Try to deliver (mc) to the controller.
20681ac4b82bSMike Smith  *
20691ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
20701ac4b82bSMike Smith  */
20711ac4b82bSMike Smith static int
20721ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
20731ac4b82bSMike Smith {
20741ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20755792b7feSMike Smith     int			i, s, done;
20761ac4b82bSMike Smith 
2077da8bb3a3SMike Smith     debug_called(1);
20781ac4b82bSMike Smith 
20791ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
20801ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
20811ac4b82bSMike Smith 
20824b006d7bSMike Smith     /* mark the command as currently being processed */
20831ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
20841ac4b82bSMike Smith 
20855792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
20865792b7feSMike Smith     mc->mc_timeout = time_second + 60;
20871ac4b82bSMike Smith 
20881ac4b82bSMike Smith     /* spin waiting for the mailbox */
20891ac4b82bSMike Smith     for (i = 100000, done = 0; (i > 0) && !done; i--) {
20901ac4b82bSMike Smith 	s = splbio();
20914b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
20924b006d7bSMike Smith 	    done = 1;
20934b006d7bSMike Smith 	    /* move command to work queue */
20944b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
20954b006d7bSMike Smith 	}
20965792b7feSMike Smith 	splx(s);	/* drop spl to allow completion interrupts */
20971ac4b82bSMike Smith     }
20981ac4b82bSMike Smith 
20991ac4b82bSMike Smith     /* command is enqueued */
21001ac4b82bSMike Smith     if (done)
21011ac4b82bSMike Smith 	return(0);
21021ac4b82bSMike Smith 
21031ac4b82bSMike Smith     /*
21041ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
21051ac4b82bSMike Smith      * that the command was given and return it with a bad status.
21061ac4b82bSMike Smith      */
21071ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
21081ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
21091ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
21105792b7feSMike Smith     mlx_complete(sc);
21111ac4b82bSMike Smith     return(EIO);
21121ac4b82bSMike Smith }
21131ac4b82bSMike Smith 
21141ac4b82bSMike Smith /********************************************************************************
21155792b7feSMike Smith  * Poll the controller (sc) for completed commands.
21165792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
21175792b7feSMike Smith  * new commands may be posted.
21181ac4b82bSMike Smith  *
21195792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
21201ac4b82bSMike Smith  */
21211ac4b82bSMike Smith static int
21221ac4b82bSMike Smith mlx_done(struct mlx_softc *sc)
21231ac4b82bSMike Smith {
21241ac4b82bSMike Smith     struct mlx_command	*mc;
21255792b7feSMike Smith     int			s, result;
21261ac4b82bSMike Smith     u_int8_t		slot;
21271ac4b82bSMike Smith     u_int16_t		status;
21281ac4b82bSMike Smith 
2129da8bb3a3SMike Smith     debug_called(2);
21301ac4b82bSMike Smith 
21315792b7feSMike Smith     result = 0;
21321ac4b82bSMike Smith 
21335792b7feSMike Smith     /* loop collecting completed commands */
21344b006d7bSMike Smith     s = splbio();
21355792b7feSMike Smith     for (;;) {
21365792b7feSMike Smith 	/* poll for a completed command's identifier and status */
21371ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
21385792b7feSMike Smith 	    result = 1;
21391ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
21401ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
21411ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
21421ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
21431ac4b82bSMike Smith 
21441ac4b82bSMike Smith 		    /* free slot for reuse */
21451ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
21461ac4b82bSMike Smith 		    sc->mlx_busycmds--;
21471ac4b82bSMike Smith 		} else {
21481ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
21491ac4b82bSMike Smith 		}
21501ac4b82bSMike Smith 	    } else {
21511ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
21521ac4b82bSMike Smith 	    }
21535792b7feSMike Smith 	} else {
21545792b7feSMike Smith 	    break;
21551ac4b82bSMike Smith 	}
21565792b7feSMike Smith     }
2157baff09dbSMike Smith     splx(s);
21581ac4b82bSMike Smith 
21595792b7feSMike Smith     /* if we've completed any commands, try posting some more */
21605792b7feSMike Smith     if (result)
21615792b7feSMike Smith 	mlx_startio(sc);
21625792b7feSMike Smith 
21635792b7feSMike Smith     /* handle completion and timeouts */
21645792b7feSMike Smith     mlx_complete(sc);
21655792b7feSMike Smith 
21665792b7feSMike Smith     return(result);
21671ac4b82bSMike Smith }
21681ac4b82bSMike Smith 
21691ac4b82bSMike Smith /********************************************************************************
21705792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
21711ac4b82bSMike Smith  */
21721ac4b82bSMike Smith static void
21731ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
21741ac4b82bSMike Smith {
21751ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
21761ac4b82bSMike Smith     int			s, count;
21771ac4b82bSMike Smith 
2178da8bb3a3SMike Smith     debug_called(2);
21791ac4b82bSMike Smith 
21805792b7feSMike Smith     /* avoid reentrancy  XXX might want to signal and request a restart */
21815792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
21825792b7feSMike Smith 	return;
21835792b7feSMike Smith 
21841ac4b82bSMike Smith     s = splbio();
21851ac4b82bSMike Smith     count = 0;
21861ac4b82bSMike Smith 
21875792b7feSMike Smith     /* scan the list of busy/done commands */
21884b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
21891ac4b82bSMike Smith     while (mc != NULL) {
21901ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
21911ac4b82bSMike Smith 
21925792b7feSMike Smith 	/* Command has been completed in some fashion */
21934b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
21944b006d7bSMike Smith 
21955792b7feSMike Smith 	    /* unmap the command's data buffer */
21965792b7feSMike Smith 	    mlx_unmapcmd(mc);
21971ac4b82bSMike Smith 	    /*
21981ac4b82bSMike Smith 	     * Does the command have a completion handler?
21991ac4b82bSMike Smith 	     */
22001ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
22011ac4b82bSMike Smith 		/* remove from list and give to handler */
22024b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
22031ac4b82bSMike Smith 		mc->mc_complete(mc);
22041ac4b82bSMike Smith 
22051ac4b82bSMike Smith 		/*
22061ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
22071ac4b82bSMike Smith 		 */
22081ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
22091ac4b82bSMike Smith 
22101ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
22114b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
22121ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
22131ac4b82bSMike Smith 
22141ac4b82bSMike Smith 		/*
22151ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
22161ac4b82bSMike Smith 		 */
22171ac4b82bSMike Smith 	    } else {
22181ac4b82bSMike Smith 	    }
22194b006d7bSMike Smith 	}
22201ac4b82bSMike Smith 	mc = nc;
22211ac4b82bSMike Smith     }
22221ac4b82bSMike Smith     splx(s);
22231ac4b82bSMike Smith 
22245792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_COMPLETING);
22251ac4b82bSMike Smith }
22261ac4b82bSMike Smith 
22271ac4b82bSMike Smith /********************************************************************************
22281ac4b82bSMike Smith  ********************************************************************************
22291ac4b82bSMike Smith                                                         Command Buffer Management
22301ac4b82bSMike Smith  ********************************************************************************
22311ac4b82bSMike Smith  ********************************************************************************/
22321ac4b82bSMike Smith 
22331ac4b82bSMike Smith /********************************************************************************
22341ac4b82bSMike Smith  * Get a new command buffer.
22351ac4b82bSMike Smith  *
22361ac4b82bSMike Smith  * This may return NULL in low-memory cases.
22371ac4b82bSMike Smith  *
22381ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
22391ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
22401ac4b82bSMike Smith  *
22411ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
22421ac4b82bSMike Smith  *
22431ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
22441ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
22451ac4b82bSMike Smith  *     using a buffer.
22461ac4b82bSMike Smith  */
22471ac4b82bSMike Smith static struct mlx_command *
22481ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
22491ac4b82bSMike Smith {
22501ac4b82bSMike Smith     struct mlx_command	*mc;
22511ac4b82bSMike Smith     int			error;
22521ac4b82bSMike Smith     int			s;
22531ac4b82bSMike Smith 
2254da8bb3a3SMike Smith     debug_called(1);
22551ac4b82bSMike Smith 
22561ac4b82bSMike Smith     s = splbio();
22571ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
22581ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
22591ac4b82bSMike Smith     splx(s);
22601ac4b82bSMike Smith 
22611ac4b82bSMike Smith     /* allocate a new command buffer? */
22621ac4b82bSMike Smith     if (mc == NULL) {
2263ca89ee27SDavid Malone 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO);
22641ac4b82bSMike Smith 	if (mc != NULL) {
22651ac4b82bSMike Smith 	    mc->mc_sc = sc;
22661ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
22671ac4b82bSMike Smith 	    if (error) {
22681ac4b82bSMike Smith 		free(mc, M_DEVBUF);
22691ac4b82bSMike Smith 		return(NULL);
22701ac4b82bSMike Smith 	    }
22711ac4b82bSMike Smith 	}
22721ac4b82bSMike Smith     }
22731ac4b82bSMike Smith     return(mc);
22741ac4b82bSMike Smith }
22751ac4b82bSMike Smith 
22761ac4b82bSMike Smith /********************************************************************************
22771ac4b82bSMike Smith  * Release a command buffer for recycling.
22781ac4b82bSMike Smith  *
22791ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
22801ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
22811ac4b82bSMike Smith  */
22821ac4b82bSMike Smith static void
22831ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
22841ac4b82bSMike Smith {
22851ac4b82bSMike Smith     int		s;
22861ac4b82bSMike Smith 
2287da8bb3a3SMike Smith     debug_called(1);
22881ac4b82bSMike Smith 
22891ac4b82bSMike Smith     s = splbio();
22901ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
22911ac4b82bSMike Smith     splx(s);
22921ac4b82bSMike Smith }
22931ac4b82bSMike Smith 
22941ac4b82bSMike Smith /********************************************************************************
22951ac4b82bSMike Smith  * Permanently discard a command buffer.
22961ac4b82bSMike Smith  */
22971ac4b82bSMike Smith static void
22981ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
22991ac4b82bSMike Smith {
23001ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
23011ac4b82bSMike Smith 
2302da8bb3a3SMike Smith     debug_called(1);
23031ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
23041ac4b82bSMike Smith     free(mc, M_DEVBUF);
23051ac4b82bSMike Smith }
23061ac4b82bSMike Smith 
23071ac4b82bSMike Smith 
23081ac4b82bSMike Smith /********************************************************************************
23091ac4b82bSMike Smith  ********************************************************************************
23101ac4b82bSMike Smith                                                 Type 3 interface accessor methods
23111ac4b82bSMike Smith  ********************************************************************************
23121ac4b82bSMike Smith  ********************************************************************************/
23131ac4b82bSMike Smith 
23141ac4b82bSMike Smith /********************************************************************************
23151ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
23161ac4b82bSMike Smith  * (the controller is not ready to take a command).
23171ac4b82bSMike Smith  *
23181ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23191ac4b82bSMike Smith  */
23201ac4b82bSMike Smith static int
23211ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
23221ac4b82bSMike Smith {
23231ac4b82bSMike Smith     int		i;
23241ac4b82bSMike Smith 
2325da8bb3a3SMike Smith     debug_called(2);
23261ac4b82bSMike Smith 
23271ac4b82bSMike Smith     /* ready for our command? */
23281ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
23291ac4b82bSMike Smith 	/* copy mailbox data to window */
23301ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
23311ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
23321ac4b82bSMike Smith 
23331ac4b82bSMike Smith 	/* post command */
2334f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
23351ac4b82bSMike Smith 	return(1);
23361ac4b82bSMike Smith     }
23371ac4b82bSMike Smith     return(0);
23381ac4b82bSMike Smith }
23391ac4b82bSMike Smith 
23401ac4b82bSMike Smith /********************************************************************************
23411ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
23421ac4b82bSMike Smith  * and recover the slot number and status code.
23431ac4b82bSMike Smith  *
23441ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23451ac4b82bSMike Smith  */
23461ac4b82bSMike Smith static int
23471ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
23481ac4b82bSMike Smith {
23491ac4b82bSMike Smith 
2350da8bb3a3SMike Smith     debug_called(2);
23511ac4b82bSMike Smith 
23521ac4b82bSMike Smith     /* status available? */
23531ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
23541ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
23551ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
23561ac4b82bSMike Smith 
23571ac4b82bSMike Smith 	/* acknowledge completion */
2358f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2359f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
23601ac4b82bSMike Smith 	return(1);
23611ac4b82bSMike Smith     }
23621ac4b82bSMike Smith     return(0);
23631ac4b82bSMike Smith }
23641ac4b82bSMike Smith 
23651ac4b82bSMike Smith /********************************************************************************
23661ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
23671ac4b82bSMike Smith  *
23681ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23691ac4b82bSMike Smith  */
23701ac4b82bSMike Smith static void
23711ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
23721ac4b82bSMike Smith {
2373da8bb3a3SMike Smith     debug_called(1);
23741ac4b82bSMike Smith 
23751ac4b82bSMike Smith     switch(action) {
23761ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
23771ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
23781ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
23791ac4b82bSMike Smith 	break;
23801ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
23811ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
23821ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
23831ac4b82bSMike Smith 	break;
23841ac4b82bSMike Smith     }
23851ac4b82bSMike Smith }
23861ac4b82bSMike Smith 
2387da8bb3a3SMike Smith /********************************************************************************
2388da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2389da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2390da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2391da8bb3a3SMike Smith  */
2392da8bb3a3SMike Smith static int
2393da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2394da8bb3a3SMike Smith {
2395da8bb3a3SMike Smith     u_int8_t	fwerror;
2396da8bb3a3SMike Smith     static int	initted = 0;
2397da8bb3a3SMike Smith 
2398da8bb3a3SMike Smith     debug_called(2);
2399da8bb3a3SMike Smith 
2400da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2401da8bb3a3SMike Smith     if (!initted) {
2402da8bb3a3SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
2403da8bb3a3SMike Smith 	DELAY(1000);
2404da8bb3a3SMike Smith 	initted = 1;
2405da8bb3a3SMike Smith     }
2406da8bb3a3SMike Smith 
2407da8bb3a3SMike Smith     /* init in progress? */
2408da8bb3a3SMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
2409da8bb3a3SMike Smith 	return(0);
2410da8bb3a3SMike Smith 
2411da8bb3a3SMike Smith     /* test error value */
2412da8bb3a3SMike Smith     fwerror = MLX_V3_GET_FWERROR(sc);
2413da8bb3a3SMike Smith     if (!(fwerror & MLX_V3_FWERROR_PEND))
2414da8bb3a3SMike Smith 	return(1);
2415da8bb3a3SMike Smith 
2416da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2417da8bb3a3SMike Smith     *error = fwerror & ~MLX_V3_FWERROR_PEND;
2418da8bb3a3SMike Smith     *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
2419da8bb3a3SMike Smith     *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
2420da8bb3a3SMike Smith 
2421da8bb3a3SMike Smith     /* acknowledge */
2422da8bb3a3SMike Smith     MLX_V3_PUT_FWERROR(sc, 0);
2423da8bb3a3SMike Smith 
2424da8bb3a3SMike Smith     return(2);
2425da8bb3a3SMike Smith }
24261ac4b82bSMike Smith 
24271ac4b82bSMike Smith /********************************************************************************
24281ac4b82bSMike Smith  ********************************************************************************
2429f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2430f6b84b08SMike Smith  ********************************************************************************
2431f6b84b08SMike Smith  ********************************************************************************/
2432f6b84b08SMike Smith 
2433f6b84b08SMike Smith /********************************************************************************
2434f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2435f6b84b08SMike Smith  * (the controller is not ready to take a command).
2436f6b84b08SMike Smith  *
2437f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2438f6b84b08SMike Smith  */
2439f6b84b08SMike Smith static int
2440f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2441f6b84b08SMike Smith {
2442f6b84b08SMike Smith     int		i;
2443f6b84b08SMike Smith 
2444da8bb3a3SMike Smith     debug_called(2);
2445f6b84b08SMike Smith 
2446f6b84b08SMike Smith     /* ready for our command? */
2447f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2448f6b84b08SMike Smith 	/* copy mailbox data to window */
2449f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2450f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2451f6b84b08SMike Smith 
2452da8bb3a3SMike Smith 	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
2453da8bb3a3SMike Smith 	bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
2454da8bb3a3SMike Smith 			  BUS_SPACE_BARRIER_WRITE);
2455da8bb3a3SMike Smith 
2456f6b84b08SMike Smith 	/* post command */
2457f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2458f6b84b08SMike Smith 	return(1);
2459f6b84b08SMike Smith     }
2460f6b84b08SMike Smith     return(0);
2461f6b84b08SMike Smith }
2462f6b84b08SMike Smith 
2463f6b84b08SMike Smith /********************************************************************************
2464f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2465f6b84b08SMike Smith  * and recover the slot number and status code.
2466f6b84b08SMike Smith  *
2467f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2468f6b84b08SMike Smith  */
2469f6b84b08SMike Smith static int
2470f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2471f6b84b08SMike Smith {
2472f6b84b08SMike Smith 
2473da8bb3a3SMike Smith     debug_called(2);
2474f6b84b08SMike Smith 
2475f6b84b08SMike Smith     /* status available? */
2476f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2477f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2478f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2479f6b84b08SMike Smith 
2480f6b84b08SMike Smith 	/* acknowledge completion */
2481f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2482f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2483f6b84b08SMike Smith 	return(1);
2484f6b84b08SMike Smith     }
2485f6b84b08SMike Smith     return(0);
2486f6b84b08SMike Smith }
2487f6b84b08SMike Smith 
2488f6b84b08SMike Smith /********************************************************************************
2489f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2490f6b84b08SMike Smith  *
2491f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2492f6b84b08SMike Smith  */
2493f6b84b08SMike Smith static void
2494f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2495f6b84b08SMike Smith {
2496da8bb3a3SMike Smith     debug_called(1);
2497f6b84b08SMike Smith 
2498f6b84b08SMike Smith     switch(action) {
2499f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2500f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2501f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2502f6b84b08SMike Smith 	break;
2503f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2504f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2505f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2506f6b84b08SMike Smith 	break;
2507f6b84b08SMike Smith     }
2508f6b84b08SMike Smith }
2509f6b84b08SMike Smith 
2510da8bb3a3SMike Smith /********************************************************************************
2511da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2512da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2513da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2514da8bb3a3SMike Smith  */
2515da8bb3a3SMike Smith static int
2516da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2517da8bb3a3SMike Smith {
2518da8bb3a3SMike Smith     u_int8_t	fwerror;
2519da8bb3a3SMike Smith     static int	initted = 0;
2520da8bb3a3SMike Smith 
2521da8bb3a3SMike Smith     debug_called(2);
2522da8bb3a3SMike Smith 
2523da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2524da8bb3a3SMike Smith     if (!initted) {
2525da8bb3a3SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2526da8bb3a3SMike Smith 	DELAY(1000);
2527da8bb3a3SMike Smith 	initted = 1;
2528da8bb3a3SMike Smith     }
2529da8bb3a3SMike Smith 
2530da8bb3a3SMike Smith     /* init in progress? */
2531da8bb3a3SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
2532da8bb3a3SMike Smith 	return(0);
2533da8bb3a3SMike Smith 
2534da8bb3a3SMike Smith     /* test error value */
2535da8bb3a3SMike Smith     fwerror = MLX_V4_GET_FWERROR(sc);
2536da8bb3a3SMike Smith     if (!(fwerror & MLX_V4_FWERROR_PEND))
2537da8bb3a3SMike Smith 	return(1);
2538da8bb3a3SMike Smith 
2539da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2540da8bb3a3SMike Smith     *error = fwerror & ~MLX_V4_FWERROR_PEND;
2541da8bb3a3SMike Smith     *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
2542da8bb3a3SMike Smith     *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
2543da8bb3a3SMike Smith 
2544da8bb3a3SMike Smith     /* acknowledge */
2545da8bb3a3SMike Smith     MLX_V4_PUT_FWERROR(sc, 0);
2546da8bb3a3SMike Smith 
2547da8bb3a3SMike Smith     return(2);
2548da8bb3a3SMike Smith }
2549f6b84b08SMike Smith 
2550f6b84b08SMike Smith /********************************************************************************
2551f6b84b08SMike Smith  ********************************************************************************
25525792b7feSMike Smith                                                 Type 5 interface accessor methods
25535792b7feSMike Smith  ********************************************************************************
25545792b7feSMike Smith  ********************************************************************************/
25555792b7feSMike Smith 
25565792b7feSMike Smith /********************************************************************************
25575792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
25585792b7feSMike Smith  * (the controller is not ready to take a command).
25595792b7feSMike Smith  *
25605792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
25615792b7feSMike Smith  */
25625792b7feSMike Smith static int
25635792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
25645792b7feSMike Smith {
25655792b7feSMike Smith     int		i;
25665792b7feSMike Smith 
2567da8bb3a3SMike Smith     debug_called(2);
25685792b7feSMike Smith 
25695792b7feSMike Smith     /* ready for our command? */
25705792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
25715792b7feSMike Smith 	/* copy mailbox data to window */
25725792b7feSMike Smith 	for (i = 0; i < 13; i++)
25735792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
25745792b7feSMike Smith 
25755792b7feSMike Smith 	/* post command */
25765792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
25775792b7feSMike Smith 	return(1);
25785792b7feSMike Smith     }
25795792b7feSMike Smith     return(0);
25805792b7feSMike Smith }
25815792b7feSMike Smith 
25825792b7feSMike Smith /********************************************************************************
25835792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
25845792b7feSMike Smith  * and recover the slot number and status code.
25855792b7feSMike Smith  *
25865792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
25875792b7feSMike Smith  */
25885792b7feSMike Smith static int
25895792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
25905792b7feSMike Smith {
25915792b7feSMike Smith 
2592da8bb3a3SMike Smith     debug_called(2);
25935792b7feSMike Smith 
25945792b7feSMike Smith     /* status available? */
25955792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
25965792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
25975792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
25985792b7feSMike Smith 
25995792b7feSMike Smith 	/* acknowledge completion */
26005792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
26015792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
26025792b7feSMike Smith 	return(1);
26035792b7feSMike Smith     }
26045792b7feSMike Smith     return(0);
26055792b7feSMike Smith }
26065792b7feSMike Smith 
26075792b7feSMike Smith /********************************************************************************
26085792b7feSMike Smith  * Enable/disable interrupts as requested.
26095792b7feSMike Smith  *
26105792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
26115792b7feSMike Smith  */
26125792b7feSMike Smith static void
26135792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
26145792b7feSMike Smith {
2615da8bb3a3SMike Smith     debug_called(1);
26165792b7feSMike Smith 
26175792b7feSMike Smith     switch(action) {
26185792b7feSMike Smith     case MLX_INTACTION_DISABLE:
2619da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
26205792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
26215792b7feSMike Smith 	break;
26225792b7feSMike Smith     case MLX_INTACTION_ENABLE:
2623da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
26245792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
26255792b7feSMike Smith 	break;
26265792b7feSMike Smith     }
26275792b7feSMike Smith }
26285792b7feSMike Smith 
2629da8bb3a3SMike Smith /********************************************************************************
2630da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2631da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2632da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2633da8bb3a3SMike Smith  */
2634da8bb3a3SMike Smith static int
2635da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2636da8bb3a3SMike Smith {
2637da8bb3a3SMike Smith     u_int8_t	fwerror;
2638da8bb3a3SMike Smith     static int	initted = 0;
2639da8bb3a3SMike Smith 
2640da8bb3a3SMike Smith     debug_called(2);
2641da8bb3a3SMike Smith 
2642da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2643da8bb3a3SMike Smith     if (!initted) {
2644da8bb3a3SMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
2645da8bb3a3SMike Smith 	DELAY(1000);
2646da8bb3a3SMike Smith 	initted = 1;
2647da8bb3a3SMike Smith     }
2648da8bb3a3SMike Smith 
2649da8bb3a3SMike Smith     /* init in progress? */
2650da8bb3a3SMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
2651da8bb3a3SMike Smith 	return(0);
2652da8bb3a3SMike Smith 
2653da8bb3a3SMike Smith     /* test for error value */
2654da8bb3a3SMike Smith     fwerror = MLX_V5_GET_FWERROR(sc);
2655da8bb3a3SMike Smith     if (!(fwerror & MLX_V5_FWERROR_PEND))
2656da8bb3a3SMike Smith 	return(1);
2657da8bb3a3SMike Smith 
2658da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2659da8bb3a3SMike Smith     *error = fwerror & ~MLX_V5_FWERROR_PEND;
2660da8bb3a3SMike Smith     *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
2661da8bb3a3SMike Smith     *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
2662da8bb3a3SMike Smith 
2663da8bb3a3SMike Smith     /* acknowledge */
2664da8bb3a3SMike Smith     MLX_V5_PUT_FWERROR(sc, 0xff);
2665da8bb3a3SMike Smith 
2666da8bb3a3SMike Smith     return(2);
2667da8bb3a3SMike Smith }
26685792b7feSMike Smith 
26695792b7feSMike Smith /********************************************************************************
26705792b7feSMike Smith  ********************************************************************************
26711ac4b82bSMike Smith                                                                         Debugging
26721ac4b82bSMike Smith  ********************************************************************************
26731ac4b82bSMike Smith  ********************************************************************************/
26741ac4b82bSMike Smith 
26751ac4b82bSMike Smith /********************************************************************************
26761ac4b82bSMike Smith  * Return a status message describing (mc)
26771ac4b82bSMike Smith  */
26781ac4b82bSMike Smith static char *mlx_status_messages[] = {
26791ac4b82bSMike Smith     "normal completion",			/* 00 */
26801ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
26811ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
26821ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
26831ac4b82bSMike Smith     "bad data encountered",			/* 04 */
26841ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
26851ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
26861ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
26871ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
26881ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
26891ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
26901ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
26911ac4b82bSMike Smith     "channel is busy",				/* 12 */
26921ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
2693da8bb3a3SMike Smith     "rebuild successfully terminated",		/* 14 */
2694da8bb3a3SMike Smith     "unsupported command",			/* 15 */
2695da8bb3a3SMike Smith     "check condition received",			/* 16 */
2696da8bb3a3SMike Smith     "device is busy",				/* 17 */
2697da8bb3a3SMike Smith     "selection or command timeout",		/* 18 */
2698da8bb3a3SMike Smith     "command terminated abnormally",		/* 19 */
2699da8bb3a3SMike Smith     ""
27001ac4b82bSMike Smith };
27011ac4b82bSMike Smith 
27021ac4b82bSMike Smith static struct
27031ac4b82bSMike Smith {
27041ac4b82bSMike Smith     int		command;
27051ac4b82bSMike Smith     u_int16_t	status;
27061ac4b82bSMike Smith     int		msg;
27071ac4b82bSMike Smith } mlx_messages[] = {
2708da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0001,	 1},
2709da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0002,	 1},
2710da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0105,	 3},
2711da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x010c,	 4},
2712da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0001,	 1},
2713da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0002,	 1},
2714da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0105,	 3},
2715da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0001,	 1},
2716da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0002,	 1},
2717da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0105,	 3},
2718da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
2719da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
2720da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
27211ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
27221ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
27231ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
27241ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
27251ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
2726da8bb3a3SMike Smith     {MLX_CMD_REBUILDASYNC,	0x0107, 14},
27271ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
27281ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
27291ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
27301ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
27311ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
27321ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
27331ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
2734da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0002, 16},
2735da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0008, 17},
2736da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000e, 18},
2737da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000f, 19},
2738da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0105,  8},
2739da8bb3a3SMike Smith 
2740da8bb3a3SMike Smith     {0,				0x0104, 14},
27411ac4b82bSMike Smith     {-1, 0, 0}
27421ac4b82bSMike Smith };
27431ac4b82bSMike Smith 
27441ac4b82bSMike Smith static char *
27451ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
27461ac4b82bSMike Smith {
27471ac4b82bSMike Smith     static char	unkmsg[80];
27481ac4b82bSMike Smith     int		i;
27491ac4b82bSMike Smith 
27501ac4b82bSMike Smith     /* look up message in table */
27511ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
2752da8bb3a3SMike Smith 	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
2753466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
27541ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
27551ac4b82bSMike Smith 
27561ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
27571ac4b82bSMike Smith     return(unkmsg);
27581ac4b82bSMike Smith }
27591ac4b82bSMike Smith 
27601ac4b82bSMike Smith /*******************************************************************************
2761da8bb3a3SMike Smith  * Print a string describing the controller (sc)
27621ac4b82bSMike Smith  */
27635792b7feSMike Smith static struct
27645792b7feSMike Smith {
27655792b7feSMike Smith     int		hwid;
27665792b7feSMike Smith     char	*name;
27675792b7feSMike Smith } mlx_controller_names[] = {
27685792b7feSMike Smith     {0x01,	"960P/PD"},
27695792b7feSMike Smith     {0x02,	"960PL"},
27705792b7feSMike Smith     {0x10,	"960PG"},
27715792b7feSMike Smith     {0x11,	"960PJ"},
27729eee27f1SMike Smith     {0x12,	"960PR"},
27739eee27f1SMike Smith     {0x13,	"960PT"},
27749eee27f1SMike Smith     {0x14,	"960PTL0"},
27759eee27f1SMike Smith     {0x15,	"960PRL"},
27769eee27f1SMike Smith     {0x16,	"960PTL1"},
27779eee27f1SMike Smith     {0x20,	"1164PVX"},
27785792b7feSMike Smith     {-1, NULL}
27795792b7feSMike Smith };
27805792b7feSMike Smith 
27819eee27f1SMike Smith static void
27829eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
27831ac4b82bSMike Smith {
27841ac4b82bSMike Smith     static char		buf[80];
27855792b7feSMike Smith     char		*model;
27869eee27f1SMike Smith     int			i;
27871ac4b82bSMike Smith 
27885792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
27899eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
27905792b7feSMike Smith 	    model = mlx_controller_names[i].name;
27911ac4b82bSMike Smith 	    break;
27921ac4b82bSMike Smith 	}
27935792b7feSMike Smith     }
27945792b7feSMike Smith     if (model == NULL) {
27959eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
27965792b7feSMike Smith 	model = buf;
27975792b7feSMike Smith     }
2798da8bb3a3SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
27999eee27f1SMike Smith 		  model,
28009eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
28019eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
28029eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
28039eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
28049eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2805b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
28069eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
28079eee27f1SMike Smith 
28089eee27f1SMike Smith     if (bootverbose) {
28099eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
28109eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
28119eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
28129eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
28139eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
28149eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
28159eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
28169eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
28179eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
28189eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
28199eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
28209eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
28219eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
28229eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
28239eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
28249eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
28259eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
28269eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
28279eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
28289eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
28299eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
28309eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
28319eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
28329eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
28339eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
28349eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
28359eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
28369eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
28379eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
28389eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
28399eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
28409eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
28419eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
28429eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
28439eee27f1SMike Smith 
28449eee27f1SMike Smith     }
28451ac4b82bSMike Smith }
28461ac4b82bSMike Smith 
2847da8bb3a3SMike Smith /*******************************************************************************
2848da8bb3a3SMike Smith  * Emit a string describing the firmware handshake status code, and return a flag
2849da8bb3a3SMike Smith  * indicating whether the code represents a fatal error.
2850da8bb3a3SMike Smith  *
2851da8bb3a3SMike Smith  * Error code interpretations are from the Linux driver, and don't directly match
2852da8bb3a3SMike Smith  * the messages printed by Mylex's BIOS.  This may change if documentation on the
2853da8bb3a3SMike Smith  * codes is forthcoming.
2854da8bb3a3SMike Smith  */
2855da8bb3a3SMike Smith static int
2856da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
2857da8bb3a3SMike Smith {
2858da8bb3a3SMike Smith     switch(error) {
2859da8bb3a3SMike Smith     case 0x00:
2860da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
2861da8bb3a3SMike Smith 	break;
2862da8bb3a3SMike Smith     case 0x08:
2863da8bb3a3SMike Smith 	/* we could be neater about this and give some indication when we receive more of them */
2864da8bb3a3SMike Smith 	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
2865da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "spinning up drives...\n");
2866da8bb3a3SMike Smith 	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
2867da8bb3a3SMike Smith 	}
2868da8bb3a3SMike Smith 	break;
2869da8bb3a3SMike Smith     case 0x30:
2870da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "configuration checksum error\n");
2871da8bb3a3SMike Smith 	break;
2872da8bb3a3SMike Smith     case 0x60:
2873da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
2874da8bb3a3SMike Smith 	break;
2875da8bb3a3SMike Smith     case 0x70:
2876da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
2877da8bb3a3SMike Smith 	break;
2878da8bb3a3SMike Smith     case 0x90:
2879da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
2880da8bb3a3SMike Smith 	break;
2881da8bb3a3SMike Smith     case 0xa0:
2882da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
2883da8bb3a3SMike Smith 	break;
2884da8bb3a3SMike Smith     case 0xb0:
2885da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
2886da8bb3a3SMike Smith 	break;
2887da8bb3a3SMike Smith     case 0xd0:
2888da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "new controller configuration found\n");
2889da8bb3a3SMike Smith 	break;
2890da8bb3a3SMike Smith     case 0xf0:
2891da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
2892da8bb3a3SMike Smith 	return(1);
2893da8bb3a3SMike Smith     default:
2894da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
2895da8bb3a3SMike Smith 	break;
2896da8bb3a3SMike Smith     }
2897da8bb3a3SMike Smith     return(0);
2898da8bb3a3SMike Smith }
2899da8bb3a3SMike Smith 
29001ac4b82bSMike Smith /********************************************************************************
29011ac4b82bSMike Smith  ********************************************************************************
29021ac4b82bSMike Smith                                                                 Utility Functions
29031ac4b82bSMike Smith  ********************************************************************************
29041ac4b82bSMike Smith  ********************************************************************************/
29051ac4b82bSMike Smith 
29061ac4b82bSMike Smith /********************************************************************************
29071ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
29081ac4b82bSMike Smith  */
29091ac4b82bSMike Smith static struct mlx_sysdrive *
29101ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
29111ac4b82bSMike Smith {
29121ac4b82bSMike Smith     int		i;
29131ac4b82bSMike Smith 
29141ac4b82bSMike Smith     /* search system drives */
29151ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
29161ac4b82bSMike Smith 	/* is this one attached? */
29171ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
29181ac4b82bSMike Smith 	    /* is this the one? */
29191ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
29201ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
29211ac4b82bSMike Smith 	}
29221ac4b82bSMike Smith     }
29231ac4b82bSMike Smith     return(NULL);
29241ac4b82bSMike Smith }
2925