xref: /freebsd/sys/dev/mlx/mlx.c (revision da8bb3a3bf71a0579594e2faa0dd92b821689e12)
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/buf.h>
391ac4b82bSMike Smith #include <sys/bus.h>
401ac4b82bSMike Smith #include <sys/conf.h>
411ac4b82bSMike Smith #include <sys/devicestat.h>
421ac4b82bSMike Smith #include <sys/disk.h>
43da8bb3a3SMike Smith #include <sys/stat.h>
441ac4b82bSMike Smith 
451ac4b82bSMike Smith #include <machine/resource.h>
461ac4b82bSMike Smith #include <machine/bus.h>
471ac4b82bSMike Smith #include <machine/clock.h>
481ac4b82bSMike Smith #include <sys/rman.h>
491ac4b82bSMike Smith 
501ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
511ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
521ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
531ac4b82bSMike Smith 
541ac4b82bSMike Smith #define MLX_CDEV_MAJOR	130
551ac4b82bSMike Smith 
561ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
571ac4b82bSMike Smith 		/* open */	mlx_open,
581ac4b82bSMike Smith 		/* close */	mlx_close,
591ac4b82bSMike Smith 		/* read */	noread,
601ac4b82bSMike Smith 		/* write */	nowrite,
611ac4b82bSMike Smith 		/* ioctl */	mlx_ioctl,
621ac4b82bSMike Smith 		/* poll */	nopoll,
631ac4b82bSMike Smith 		/* mmap */	nommap,
641ac4b82bSMike Smith 		/* strategy */	nostrategy,
651ac4b82bSMike Smith 		/* name */ 	"mlx",
661ac4b82bSMike Smith 		/* maj */	MLX_CDEV_MAJOR,
671ac4b82bSMike Smith 		/* dump */	nodump,
681ac4b82bSMike Smith 		/* psize */ 	nopsize,
691ac4b82bSMike Smith 		/* flags */	0,
70f6b84b08SMike Smith 		/* bmaj */	-1
711ac4b82bSMike Smith };
721ac4b82bSMike Smith 
731ac4b82bSMike Smith devclass_t	mlx_devclass;
741ac4b82bSMike Smith 
751ac4b82bSMike Smith /*
761ac4b82bSMike Smith  * Per-interface accessor methods
771ac4b82bSMike Smith  */
781ac4b82bSMike Smith static int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
791ac4b82bSMike Smith static int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
801ac4b82bSMike Smith static void			mlx_v3_intaction(struct mlx_softc *sc, int action);
81da8bb3a3SMike Smith static int			mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
821ac4b82bSMike Smith 
83f6b84b08SMike Smith static int			mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
84f6b84b08SMike Smith static int			mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
85f6b84b08SMike Smith static void			mlx_v4_intaction(struct mlx_softc *sc, int action);
86da8bb3a3SMike Smith static int			mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
87f6b84b08SMike Smith 
885792b7feSMike Smith static int			mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
895792b7feSMike Smith static int			mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
905792b7feSMike Smith static void			mlx_v5_intaction(struct mlx_softc *sc, int action);
91da8bb3a3SMike Smith static int			mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
925792b7feSMike Smith 
931ac4b82bSMike Smith /*
941ac4b82bSMike Smith  * Status monitoring
951ac4b82bSMike Smith  */
961ac4b82bSMike Smith static void			mlx_periodic(void *data);
971ac4b82bSMike Smith static void			mlx_periodic_enquiry(struct mlx_command *mc);
981ac4b82bSMike Smith static void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
991ac4b82bSMike Smith static void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
1001ac4b82bSMike Smith static void			mlx_periodic_rebuild(struct mlx_command *mc);
1011ac4b82bSMike Smith 
1021ac4b82bSMike Smith /*
1031ac4b82bSMike Smith  * Channel Pause
1041ac4b82bSMike Smith  */
1051ac4b82bSMike Smith static void			mlx_pause_action(struct mlx_softc *sc);
1061ac4b82bSMike Smith static void			mlx_pause_done(struct mlx_command *mc);
1071ac4b82bSMike Smith 
1081ac4b82bSMike Smith /*
1091ac4b82bSMike Smith  * Command submission.
1101ac4b82bSMike Smith  */
1111ac4b82bSMike Smith static void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
1121ac4b82bSMike Smith 					     void (*complete)(struct mlx_command *mc));
1131ac4b82bSMike Smith static int			mlx_flush(struct mlx_softc *sc);
1141ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1151ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1161ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1171ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1181ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1191ac4b82bSMike Smith static int			mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu);
1201ac4b82bSMike Smith 
1211ac4b82bSMike Smith /*
1221ac4b82bSMike Smith  * Command buffer allocation.
1231ac4b82bSMike Smith  */
1241ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1251ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1261ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1271ac4b82bSMike Smith 
1281ac4b82bSMike Smith /*
1291ac4b82bSMike Smith  * Command management.
1301ac4b82bSMike Smith  */
1311ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1321ac4b82bSMike Smith static void			mlx_mapcmd(struct mlx_command *mc);
1331ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1341ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1351ac4b82bSMike Smith static int			mlx_done(struct mlx_softc *sc);
1361ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1371ac4b82bSMike Smith 
1381ac4b82bSMike Smith /*
1391ac4b82bSMike Smith  * Debugging.
1401ac4b82bSMike Smith  */
1411ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1429eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
143da8bb3a3SMike Smith static int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
1441ac4b82bSMike Smith 
1451ac4b82bSMike Smith /*
1461ac4b82bSMike Smith  * Utility functions.
1471ac4b82bSMike Smith  */
1481ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1491ac4b82bSMike Smith 
1501ac4b82bSMike Smith /********************************************************************************
1511ac4b82bSMike Smith  ********************************************************************************
1521ac4b82bSMike Smith                                                                 Public Interfaces
1531ac4b82bSMike Smith  ********************************************************************************
1541ac4b82bSMike Smith  ********************************************************************************/
1551ac4b82bSMike Smith 
1561ac4b82bSMike Smith /********************************************************************************
1571ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1581ac4b82bSMike Smith  *
1591ac4b82bSMike Smith  * Should not be called if the controller is active.
1601ac4b82bSMike Smith  */
1611ac4b82bSMike Smith void
1621ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1631ac4b82bSMike Smith {
1641ac4b82bSMike Smith     struct mlx_command	*mc;
1651ac4b82bSMike Smith 
166da8bb3a3SMike Smith     debug_called(1);
1671ac4b82bSMike Smith 
1681ac4b82bSMike Smith     /* cancel status timeout */
1691ac4b82bSMike Smith     untimeout(mlx_periodic, sc, sc->mlx_timeout);
1701ac4b82bSMike Smith 
1711ac4b82bSMike Smith     /* throw away any command buffers */
1721ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1731ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1741ac4b82bSMike Smith 	mlx_freecmd(mc);
1751ac4b82bSMike Smith     }
1761ac4b82bSMike Smith 
1771ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1781ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1791ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1801ac4b82bSMike Smith 
1811ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
1821ac4b82bSMike Smith     if (sc->mlx_sgtable)
1831ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1841ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1851ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1861ac4b82bSMike Smith 
1871ac4b82bSMike Smith     /* disconnect the interrupt handler */
1881ac4b82bSMike Smith     if (sc->mlx_intr)
1891ac4b82bSMike Smith 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1901ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
1911ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
1921ac4b82bSMike Smith 
1931ac4b82bSMike Smith     /* destroy the parent DMA tag */
1941ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
1951ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
1961ac4b82bSMike Smith 
1971ac4b82bSMike Smith     /* release the register window mapping */
1981ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
1991ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY,
2001ac4b82bSMike Smith 			     (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem);
2019eee27f1SMike Smith 
2029eee27f1SMike Smith     /* free controller enquiry data */
2039eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
2049eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
205da8bb3a3SMike Smith 
206da8bb3a3SMike Smith     /* destroy control device */
207da8bb3a3SMike Smith     if (sc->mlx_dev_t != (dev_t)NULL)
208da8bb3a3SMike Smith 	destroy_dev(sc->mlx_dev_t);
2091ac4b82bSMike Smith }
2101ac4b82bSMike Smith 
2111ac4b82bSMike Smith /********************************************************************************
2121ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2131ac4b82bSMike Smith  */
2141ac4b82bSMike Smith static void
2151ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2161ac4b82bSMike Smith {
2171ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2181ac4b82bSMike Smith 
219da8bb3a3SMike Smith     debug_called(1);
2201ac4b82bSMike Smith 
2211ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2221ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2231ac4b82bSMike Smith }
2241ac4b82bSMike Smith 
2251ac4b82bSMike Smith static int
2261ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2271ac4b82bSMike Smith {
2281ac4b82bSMike Smith     size_t	segsize;
2291ac4b82bSMike Smith     int		error;
2301ac4b82bSMike Smith 
231da8bb3a3SMike Smith     debug_called(1);
2321ac4b82bSMike Smith 
2331ac4b82bSMike Smith     /* destroy any existing mappings */
2341ac4b82bSMike Smith     if (sc->mlx_sgtable)
2351ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2361ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2371ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2381ac4b82bSMike Smith 
2391ac4b82bSMike Smith     /*
2401ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
2411ac4b82bSMike Smith      * the s/g lists we will need.
2421ac4b82bSMike Smith      */
243da8bb3a3SMike Smith     segsize = sizeof(struct mlx_sgentry) * sc->mlx_sg_nseg * sc->mlx_maxiop;
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);
2911ac4b82bSMike Smith     bufq_init(&sc->mlx_bufq);
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;
303da8bb3a3SMike Smith 	sc->mlx_sg_nseg		= MLX_NSEG_OLD;
3041ac4b82bSMike Smith 	break;
305f6b84b08SMike Smith     case MLX_IFTYPE_4:
306f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
307f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
308f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
309da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
310da8bb3a3SMike Smith 	sc->mlx_sg_nseg		= MLX_NSEG_NEW;
311f6b84b08SMike Smith 	break;
3125792b7feSMike Smith     case MLX_IFTYPE_5:
3135792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3145792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3155792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
316da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
317da8bb3a3SMike Smith 	sc->mlx_sg_nseg		= MLX_NSEG_NEW;
3185792b7feSMike Smith 	break;
3191ac4b82bSMike Smith     default:
3201ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype);
3211ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3221ac4b82bSMike Smith     }
3231ac4b82bSMike Smith 
3241ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3251ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3261ac4b82bSMike Smith 
3271ac4b82bSMike Smith     /*
328da8bb3a3SMike Smith      * Wait for the controller to come ready, handshake with the firmware if required.
329da8bb3a3SMike Smith      * This is typically only necessary on platforms where the controller BIOS does not
330da8bb3a3SMike Smith      * run.
331da8bb3a3SMike Smith      */
332da8bb3a3SMike Smith     hsmsg = 0;
333da8bb3a3SMike Smith     DELAY(1000);
334da8bb3a3SMike Smith     while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) {
335da8bb3a3SMike Smith 	/* report first time around... */
336da8bb3a3SMike Smith 	if (hsmsg == 0) {
337da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
338da8bb3a3SMike Smith 	    hsmsg = 1;
339da8bb3a3SMike Smith 	}
340da8bb3a3SMike Smith 	/* did we get a real message? */
341da8bb3a3SMike Smith 	if (hscode == 2) {
342da8bb3a3SMike Smith 	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
343da8bb3a3SMike Smith 	    /* fatal initialisation error? */
344da8bb3a3SMike Smith 	    if (hscode != 0) {
345da8bb3a3SMike Smith 		mlx_free(sc);
346da8bb3a3SMike Smith 		return(ENXIO);
347da8bb3a3SMike Smith 	    }
348da8bb3a3SMike Smith 	}
349da8bb3a3SMike Smith     }
350da8bb3a3SMike Smith     if (hsmsg == 1)
351da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "initialisation complete.\n");
352da8bb3a3SMike Smith 
353da8bb3a3SMike Smith     /*
3541ac4b82bSMike Smith      * Allocate and connect our interrupt.
3551ac4b82bSMike Smith      */
3561ac4b82bSMike Smith     rid = 0;
3571ac4b82bSMike Smith     sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
3581ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
3591ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't allocate interrupt\n");
3601ac4b82bSMike Smith 	mlx_free(sc);
3611ac4b82bSMike Smith 	return(ENXIO);
3621ac4b82bSMike Smith     }
3631ac4b82bSMike Smith     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO,  mlx_intr, sc, &sc->mlx_intr);
3641ac4b82bSMike Smith     if (error) {
3651ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't set up interrupt\n");
3661ac4b82bSMike Smith 	mlx_free(sc);
3671ac4b82bSMike Smith 	return(ENXIO);
3681ac4b82bSMike Smith     }
3691ac4b82bSMike Smith 
3701ac4b82bSMike Smith     /*
3711ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3721ac4b82bSMike Smith      */
3731ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 		/* parent */
3741ac4b82bSMike Smith 			       1, 0, 				/* alignment, boundary */
3751ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,		/* lowaddr */
3761ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 		/* highaddr */
3771ac4b82bSMike Smith 			       NULL, NULL, 			/* filter, filterarg */
378da8bb3a3SMike Smith 			       MAXBSIZE, sc->mlx_sg_nseg,	/* maxsize, nsegments */
3791ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,		/* maxsegsize */
3801ac4b82bSMike Smith 			       0,				/* flags */
3811ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
3821ac4b82bSMike Smith     if (error != 0) {
3831ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
3841ac4b82bSMike Smith 	return(ENOMEM);
3851ac4b82bSMike Smith     }
3861ac4b82bSMike Smith 
3871ac4b82bSMike Smith     /*
3881ac4b82bSMike Smith      * Create an initial set of s/g mappings.
3891ac4b82bSMike Smith      */
3909eee27f1SMike Smith     sc->mlx_maxiop = 8;
3911ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3921ac4b82bSMike Smith     if (error != 0) {
3931ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
3941ac4b82bSMike Smith 	return(error);
3951ac4b82bSMike Smith     }
3961ac4b82bSMike Smith 
3971ac4b82bSMike Smith     /* send an ENQUIRY2 to the controller */
3989eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
3991ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
4001ac4b82bSMike Smith 	return(ENXIO);
4011ac4b82bSMike Smith     }
4021ac4b82bSMike Smith 
4039eee27f1SMike Smith     /*
4049eee27f1SMike Smith      * We don't (yet) know where the event log is up to.
4059eee27f1SMike Smith      */
4069eee27f1SMike Smith     sc->mlx_lastevent = -1;
4071ac4b82bSMike Smith 
4081ac4b82bSMike Smith     /*
4091ac4b82bSMike Smith      * Do quirk/feature related things.
4101ac4b82bSMike Smith      */
4119eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
4121ac4b82bSMike Smith     switch(sc->mlx_iftype) {
413da8bb3a3SMike Smith     case MLX_IFTYPE_2:
414da8bb3a3SMike Smith 	/* These controllers don't report the firmware version in the ENQUIRY2 response */
415da8bb3a3SMike Smith 	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
416da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
417da8bb3a3SMike Smith 	    return(ENXIO);
418da8bb3a3SMike Smith 	}
419da8bb3a3SMike Smith 	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
420da8bb3a3SMike Smith 	free(meo, M_DEVBUF);
421da8bb3a3SMike Smith 
422da8bb3a3SMike Smith 	/* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */
423da8bb3a3SMike Smith 	if (meo->me_fwminor < 42) {
424da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
425da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
426da8bb3a3SMike Smith 	}
427da8bb3a3SMike Smith 	break;
4281ac4b82bSMike Smith     case MLX_IFTYPE_3:
429f6b84b08SMike Smith 	/* XXX certify 3.52? */
4309eee27f1SMike Smith 	if (fwminor < 51) {
4314b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4324b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
4331ac4b82bSMike Smith 	}
4341ac4b82bSMike Smith 	break;
435f6b84b08SMike Smith     case MLX_IFTYPE_4:
436f6b84b08SMike Smith 	/* XXX certify firmware versions? */
4379eee27f1SMike Smith 	if (fwminor < 6) {
4384b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4394b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
440f6b84b08SMike Smith 	}
441f6b84b08SMike Smith 	break;
4425792b7feSMike Smith     case MLX_IFTYPE_5:
4439eee27f1SMike Smith 	if (fwminor < 7) {
4445792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4455792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
4465792b7feSMike Smith 	}
4475792b7feSMike Smith 	break;
4481ac4b82bSMike Smith     default:
4491ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "interface version corrupted to %d\n", sc->mlx_iftype);
4501ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4511ac4b82bSMike Smith     }
4521ac4b82bSMike Smith 
4531ac4b82bSMike Smith     /*
4541ac4b82bSMike Smith      * Create the final set of s/g mappings now that we know how many commands
4551ac4b82bSMike Smith      * the controller actually supports.
4561ac4b82bSMike Smith      */
4579eee27f1SMike Smith     sc->mlx_maxiop = sc->mlx_enq2->me_max_commands;
4581ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4591ac4b82bSMike Smith     if (error != 0) {
4601ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
4611ac4b82bSMike Smith 	return(error);
4621ac4b82bSMike Smith     }
4631ac4b82bSMike Smith 
4641ac4b82bSMike Smith     /*
4651ac4b82bSMike Smith      * No rebuild or check is in progress.
4661ac4b82bSMike Smith      */
4671ac4b82bSMike Smith     sc->mlx_rebuild = -1;
4681ac4b82bSMike Smith     sc->mlx_check = -1;
4691ac4b82bSMike Smith 
4701ac4b82bSMike Smith     /*
471da8bb3a3SMike Smith      * Create the control device.
4721ac4b82bSMike Smith      */
473da8bb3a3SMike Smith     sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR,
474da8bb3a3SMike Smith 			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
4751ac4b82bSMike Smith 
4761ac4b82bSMike Smith     /*
4771ac4b82bSMike Smith      * Start the timeout routine.
4781ac4b82bSMike Smith      */
4791ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
4801ac4b82bSMike Smith 
481da8bb3a3SMike Smith     /* print a little information about the controller */
482da8bb3a3SMike Smith     mlx_describe_controller(sc);
483da8bb3a3SMike Smith 
4841ac4b82bSMike Smith     return(0);
4851ac4b82bSMike Smith }
4861ac4b82bSMike Smith 
4871ac4b82bSMike Smith /********************************************************************************
4881ac4b82bSMike Smith  * Locate disk resources and attach children to them.
4891ac4b82bSMike Smith  */
4901ac4b82bSMike Smith void
4911ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
4921ac4b82bSMike Smith {
4931ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
4941ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
4951ac4b82bSMike Smith     int				i, error;
4961ac4b82bSMike Smith 
497da8bb3a3SMike Smith     debug_called(1);
4981ac4b82bSMike Smith 
4991ac4b82bSMike Smith     /*
5001ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
5011ac4b82bSMike Smith      * don't currently have them.
5021ac4b82bSMike Smith      */
5031ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
5041ac4b82bSMike Smith     if (mes == NULL) {
5059eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
5061ac4b82bSMike Smith 	return;
5071ac4b82bSMike Smith     }
5081ac4b82bSMike Smith 
5091ac4b82bSMike Smith     /* iterate over drives returned */
5101ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
5111ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
5121ac4b82bSMike Smith 	 i++, dr++) {
5131ac4b82bSMike Smith 	/* are we already attached to this drive? */
5141ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
5151ac4b82bSMike Smith 	    /* pick up drive information */
5161ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
517f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
5181ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
5191ac4b82bSMike Smith 
5201ac4b82bSMike Smith 	    /* generate geometry information */
5211ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
5221ac4b82bSMike Smith 		dr->ms_heads = 128;
5231ac4b82bSMike Smith 		dr->ms_sectors = 32;
5241ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
5251ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
5261ac4b82bSMike Smith 		dr->ms_heads = 255;
5271ac4b82bSMike Smith 		dr->ms_sectors = 63;
5281ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
5291ac4b82bSMike Smith 	    }
530fe0d4089SMatthew N. Dodd 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
5311ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
5321ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
533fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
5341ac4b82bSMike Smith 	}
5351ac4b82bSMike Smith     }
5361ac4b82bSMike Smith     free(mes, M_DEVBUF);
5371ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
5381ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
5391ac4b82bSMike Smith 
5401ac4b82bSMike Smith     /* mark controller back up */
5411ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5421ac4b82bSMike Smith 
5431ac4b82bSMike Smith     /* enable interrupts */
5441ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
5451ac4b82bSMike Smith }
5461ac4b82bSMike Smith 
5471ac4b82bSMike Smith /********************************************************************************
5481ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
5491ac4b82bSMike Smith  */
5501ac4b82bSMike Smith int
5511ac4b82bSMike Smith mlx_detach(device_t dev)
5521ac4b82bSMike Smith {
5531ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5545792b7feSMike Smith     struct mlxd_softc	*mlxd;
5555792b7feSMike Smith     int			i, s, error;
5561ac4b82bSMike Smith 
557da8bb3a3SMike Smith     debug_called(1);
5581ac4b82bSMike Smith 
5595792b7feSMike Smith     error = EBUSY;
5605792b7feSMike Smith     s = splbio();
5611ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
5625792b7feSMike Smith 	goto out;
5631ac4b82bSMike Smith 
5645792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5655792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5665792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
5675792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
5685792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
5695792b7feSMike Smith 		goto out;
5705792b7feSMike Smith 	    }
5715792b7feSMike Smith 	}
5725792b7feSMike Smith     }
5731ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
5745792b7feSMike Smith 	goto out;
5751ac4b82bSMike Smith 
5761ac4b82bSMike Smith     mlx_free(sc);
5771ac4b82bSMike Smith 
5785792b7feSMike Smith     error = 0;
5795792b7feSMike Smith  out:
5805792b7feSMike Smith     splx(s);
5815792b7feSMike Smith     return(error);
5821ac4b82bSMike Smith }
5831ac4b82bSMike Smith 
5841ac4b82bSMike Smith /********************************************************************************
5851ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
5861ac4b82bSMike Smith  *
5871ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
5881ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
5891ac4b82bSMike Smith  * resume normal operation.)
5901ac4b82bSMike Smith  *
5911ac4b82bSMike Smith  * Note that we can assume that the bufq on the controller is empty, as we won't
5921ac4b82bSMike Smith  * allow shutdown if any device is open.
5931ac4b82bSMike Smith  */
5941ac4b82bSMike Smith int
5951ac4b82bSMike Smith mlx_shutdown(device_t dev)
5961ac4b82bSMike Smith {
5971ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5981ac4b82bSMike Smith     int			i, s, error;
5991ac4b82bSMike Smith 
600da8bb3a3SMike Smith     debug_called(1);
6011ac4b82bSMike Smith 
6021ac4b82bSMike Smith     s = splbio();
6031ac4b82bSMike Smith     error = 0;
6041ac4b82bSMike Smith 
6051ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
6065792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6071ac4b82bSMike Smith 
6081ac4b82bSMike Smith     /* flush controller */
6091ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6101ac4b82bSMike Smith     if (mlx_flush(sc)) {
6111ac4b82bSMike Smith 	printf("failed\n");
6121ac4b82bSMike Smith     } else {
6131ac4b82bSMike Smith 	printf("done\n");
6141ac4b82bSMike Smith     }
6151ac4b82bSMike Smith 
6161ac4b82bSMike Smith     /* delete all our child devices */
6171ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6181ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6191ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
6201ac4b82bSMike Smith 		goto out;
6211ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
6221ac4b82bSMike Smith 	}
6231ac4b82bSMike Smith     }
6241ac4b82bSMike Smith 
6251ac4b82bSMike Smith  out:
6261ac4b82bSMike Smith     splx(s);
6271ac4b82bSMike Smith     return(error);
6281ac4b82bSMike Smith }
6291ac4b82bSMike Smith 
6301ac4b82bSMike Smith /********************************************************************************
6311ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
6321ac4b82bSMike Smith  */
6331ac4b82bSMike Smith int
6341ac4b82bSMike Smith mlx_suspend(device_t dev)
6351ac4b82bSMike Smith {
6361ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6371ac4b82bSMike Smith     int			s;
6381ac4b82bSMike Smith 
639da8bb3a3SMike Smith     debug_called(1);
6401ac4b82bSMike Smith 
6411ac4b82bSMike Smith     s = splbio();
6421ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
6431ac4b82bSMike Smith 
6441ac4b82bSMike Smith     /* flush controller */
6451ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6461ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6471ac4b82bSMike Smith 
6481ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6491ac4b82bSMike Smith     splx(s);
6501ac4b82bSMike Smith 
6511ac4b82bSMike Smith     return(0);
6521ac4b82bSMike Smith }
6531ac4b82bSMike Smith 
6541ac4b82bSMike Smith /********************************************************************************
6551ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
6561ac4b82bSMike Smith  */
6571ac4b82bSMike Smith int
6581ac4b82bSMike Smith mlx_resume(device_t dev)
6591ac4b82bSMike Smith {
6601ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6611ac4b82bSMike Smith 
662da8bb3a3SMike Smith     debug_called(1);
6631ac4b82bSMike Smith 
6641ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
6651ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
6661ac4b82bSMike Smith 
6671ac4b82bSMike Smith     return(0);
6681ac4b82bSMike Smith }
6691ac4b82bSMike Smith 
6701ac4b82bSMike Smith /*******************************************************************************
6711ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
6721ac4b82bSMike Smith  * status.
6731ac4b82bSMike Smith  */
6741ac4b82bSMike Smith void
6751ac4b82bSMike Smith mlx_intr(void *arg)
6761ac4b82bSMike Smith {
6771ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
6781ac4b82bSMike Smith 
679da8bb3a3SMike Smith     debug_called(1);
6801ac4b82bSMike Smith 
6815792b7feSMike Smith     /* collect finished commands, queue anything waiting */
6825792b7feSMike Smith     mlx_done(sc);
6831ac4b82bSMike Smith };
6841ac4b82bSMike Smith 
6851ac4b82bSMike Smith /*******************************************************************************
6861ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
6871ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
6881ac4b82bSMike Smith  */
6891ac4b82bSMike Smith int
6901ac4b82bSMike Smith mlx_submit_buf(struct mlx_softc *sc, struct buf *bp)
6911ac4b82bSMike Smith {
6924b006d7bSMike Smith     int		s;
6934b006d7bSMike Smith 
694da8bb3a3SMike Smith     debug_called(1);
6951ac4b82bSMike Smith 
6964b006d7bSMike Smith     s = splbio();
6971ac4b82bSMike Smith     bufq_insert_tail(&sc->mlx_bufq, bp);
6981ac4b82bSMike Smith     sc->mlx_waitbufs++;
6994b006d7bSMike Smith     splx(s);
7001ac4b82bSMike Smith     mlx_startio(sc);
7011ac4b82bSMike Smith     return(0);
7021ac4b82bSMike Smith }
7031ac4b82bSMike Smith 
7041ac4b82bSMike Smith /********************************************************************************
7051ac4b82bSMike Smith  * Accept an open operation on the control device.
7061ac4b82bSMike Smith  */
7071ac4b82bSMike Smith int
7081ac4b82bSMike Smith mlx_open(dev_t dev, int flags, int fmt, struct proc *p)
7091ac4b82bSMike Smith {
7101ac4b82bSMike Smith     int			unit = minor(dev);
7111ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7121ac4b82bSMike Smith 
7131ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
7141ac4b82bSMike Smith     return(0);
7151ac4b82bSMike Smith }
7161ac4b82bSMike Smith 
7171ac4b82bSMike Smith /********************************************************************************
7181ac4b82bSMike Smith  * Accept the last close on the control device.
7191ac4b82bSMike Smith  */
7201ac4b82bSMike Smith int
7211ac4b82bSMike Smith mlx_close(dev_t dev, int flags, int fmt, struct proc *p)
7221ac4b82bSMike Smith {
7231ac4b82bSMike Smith     int			unit = minor(dev);
7241ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7251ac4b82bSMike Smith 
7261ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
7271ac4b82bSMike Smith     return (0);
7281ac4b82bSMike Smith }
7291ac4b82bSMike Smith 
7301ac4b82bSMike Smith /********************************************************************************
7311ac4b82bSMike Smith  * Handle controller-specific control operations.
7321ac4b82bSMike Smith  */
7331ac4b82bSMike Smith int
7341ac4b82bSMike Smith mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
7351ac4b82bSMike Smith {
7361ac4b82bSMike Smith     int			unit = minor(dev);
7371ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7381ac4b82bSMike Smith     int			*arg = (int *)addr;
7391ac4b82bSMike Smith     struct mlx_pause	*mp;
7401ac4b82bSMike Smith     struct mlx_sysdrive	*dr;
7411ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
7421ac4b82bSMike Smith     int			i, error;
7431ac4b82bSMike Smith 
7441ac4b82bSMike Smith     switch(cmd) {
7451ac4b82bSMike Smith 	/*
7461ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
7471ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
7481ac4b82bSMike Smith 	 * a valid unit on this controller.
7491ac4b82bSMike Smith 	 */
7501ac4b82bSMike Smith     case MLX_NEXT_CHILD:
7511ac4b82bSMike Smith 	/* search system drives */
7521ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
7531ac4b82bSMike Smith 	    /* is this one attached? */
7541ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
7551ac4b82bSMike Smith 		/* looking for the next one we come across? */
7561ac4b82bSMike Smith 		if (*arg == -1) {
7571ac4b82bSMike Smith 		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
7581ac4b82bSMike Smith 		    return(0);
7591ac4b82bSMike Smith 		}
7601ac4b82bSMike Smith 		/* we want the one after this one */
7611ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
7621ac4b82bSMike Smith 		    *arg = -1;
7631ac4b82bSMike Smith 	    }
7641ac4b82bSMike Smith 	}
7651ac4b82bSMike Smith 	return(ENOENT);
7661ac4b82bSMike Smith 
7671ac4b82bSMike Smith 	/*
7681ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
7691ac4b82bSMike Smith 	 */
7701ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
7711ac4b82bSMike Smith 	mlx_startup(sc);
7721ac4b82bSMike Smith 	return(0);
7731ac4b82bSMike Smith 
7741ac4b82bSMike Smith 	/*
7751ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
7761ac4b82bSMike Smith 	 * away.
7771ac4b82bSMike Smith 	 */
7781ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
7791ac4b82bSMike Smith 
7801ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
7811ac4b82bSMike Smith 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
7821ac4b82bSMike Smith 	    return(ENOENT);
7831ac4b82bSMike Smith 
7841ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
7851ac4b82bSMike Smith 	error = 0;
7861ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
7871ac4b82bSMike Smith 	    error = EBUSY;
7881ac4b82bSMike Smith 	    goto detach_out;
7891ac4b82bSMike Smith 	}
7901ac4b82bSMike Smith 
7911ac4b82bSMike Smith 	/* flush controller */
7921ac4b82bSMike Smith 	if (mlx_flush(sc)) {
7931ac4b82bSMike Smith 	    error = EBUSY;
7941ac4b82bSMike Smith 	    goto detach_out;
7951ac4b82bSMike Smith 	}
7961ac4b82bSMike Smith 
7971ac4b82bSMike Smith 	/* nuke drive */
7981ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
7991ac4b82bSMike Smith 	    goto detach_out;
8001ac4b82bSMike Smith 	dr->ms_disk = 0;
8011ac4b82bSMike Smith 
8021ac4b82bSMike Smith     detach_out:
8031ac4b82bSMike Smith 	if (error) {
8041ac4b82bSMike Smith 	    printf("failed\n");
8051ac4b82bSMike Smith 	} else {
8061ac4b82bSMike Smith 	    printf("done\n");
8071ac4b82bSMike Smith 	}
8081ac4b82bSMike Smith 	return(error);
8091ac4b82bSMike Smith 
8101ac4b82bSMike Smith 	/*
8111ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
8121ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
8131ac4b82bSMike Smith 	 *
8141ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
8151ac4b82bSMike Smith 	 * to do this right.
8161ac4b82bSMike Smith 	 */
8171ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
8181ac4b82bSMike Smith 	/* Does this command work on this firmware? */
8191ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
8201ac4b82bSMike Smith 	    return(EOPNOTSUPP);
8211ac4b82bSMike Smith 
8221ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
8231ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
8241ac4b82bSMike Smith 	    /* cancel a pending pause operation */
8251ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
8261ac4b82bSMike Smith 	} else {
8271ac4b82bSMike Smith 	    /* fix for legal channels */
8289eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
8291ac4b82bSMike Smith 	    /* check time values */
8301ac4b82bSMike Smith 	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
8311ac4b82bSMike Smith 		return(EINVAL);
8321ac4b82bSMike Smith 	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
8331ac4b82bSMike Smith 		return(EINVAL);
8341ac4b82bSMike Smith 
8351ac4b82bSMike Smith 	    /* check for a pause currently running */
8361ac4b82bSMike Smith 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
8371ac4b82bSMike Smith 		return(EBUSY);
8381ac4b82bSMike Smith 
8391ac4b82bSMike Smith 	    /* looks ok, go with it */
8401ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
8411ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
8421ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
8431ac4b82bSMike Smith 	}
8441ac4b82bSMike Smith 	return(0);
8451ac4b82bSMike Smith 
8461ac4b82bSMike Smith 	/*
8471ac4b82bSMike Smith 	 * Accept a command passthrough-style.
8481ac4b82bSMike Smith 	 */
8491ac4b82bSMike Smith     case MLX_COMMAND:
8501ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
8511ac4b82bSMike Smith 
8521ac4b82bSMike Smith     default:
8531ac4b82bSMike Smith 	return(ENOTTY);
8541ac4b82bSMike Smith     }
8551ac4b82bSMike Smith }
8561ac4b82bSMike Smith 
8571ac4b82bSMike Smith /********************************************************************************
8581ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
8591ac4b82bSMike Smith  */
8601ac4b82bSMike Smith int
8611ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
8621ac4b82bSMike Smith 		caddr_t addr, int32_t flag, struct proc *p)
8631ac4b82bSMike Smith {
8641ac4b82bSMike Smith     struct mlxd_rebuild		*mr = (struct mlxd_rebuild *)addr;
8651ac4b82bSMike Smith     struct mlxd_rebuild_status	*mp = (struct mlxd_rebuild_status *)addr;
8661ac4b82bSMike Smith     int				*arg = (int *)addr;
8671ac4b82bSMike Smith     int				error;
8681ac4b82bSMike Smith 
8691ac4b82bSMike Smith     switch(cmd) {
8701ac4b82bSMike Smith 	/*
8711ac4b82bSMike Smith 	 * Return the current status of this drive.
8721ac4b82bSMike Smith 	 */
8731ac4b82bSMike Smith     case MLXD_STATUS:
8741ac4b82bSMike Smith 	*arg = drive->ms_state;
8751ac4b82bSMike Smith 	return(0);
8761ac4b82bSMike Smith 
8771ac4b82bSMike Smith 	/*
8781ac4b82bSMike Smith 	 * Start a background rebuild on this drive.
8791ac4b82bSMike Smith 	 */
8801ac4b82bSMike Smith     case MLXD_REBUILDASYNC:
8811ac4b82bSMike Smith 	/* XXX lock? */
8821ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0)
8831ac4b82bSMike Smith 	    return(EBUSY);
8841ac4b82bSMike Smith 	sc->mlx_rebuild = drive - &sc->mlx_sysdrive[0];
8851ac4b82bSMike Smith 
8861ac4b82bSMike Smith 	switch (mlx_rebuild(sc, mr->rb_channel, mr->rb_target)) {
8871ac4b82bSMike Smith 	case 0:
8881ac4b82bSMike Smith 	    drive->ms_state = MLX_SYSD_REBUILD;
8891ac4b82bSMike Smith 	    error = 0;
8901ac4b82bSMike Smith 	    break;
8911ac4b82bSMike Smith 	case 0x10000:
8921ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
8931ac4b82bSMike Smith 	    break;
8941ac4b82bSMike Smith 	case 0x0002:
8951ac4b82bSMike Smith 	case 0x0106:
8961ac4b82bSMike Smith 	    error = EBUSY;
8971ac4b82bSMike Smith 	    break;
8981ac4b82bSMike Smith 	case 0x0004:
8991ac4b82bSMike Smith 	    error = EIO;
9001ac4b82bSMike Smith 	    break;
9011ac4b82bSMike Smith 	case 0x0105:
9021ac4b82bSMike Smith 	    error = ERANGE;
9031ac4b82bSMike Smith 	    break;
9041ac4b82bSMike Smith 	default:
9051ac4b82bSMike Smith 	    error = EINVAL;
9061ac4b82bSMike Smith 	    break;
9071ac4b82bSMike Smith 	}
9081ac4b82bSMike Smith 	if (error != 0)
9091ac4b82bSMike Smith 	    sc->mlx_rebuild = -1;
9101ac4b82bSMike Smith 	return(error);
9111ac4b82bSMike Smith 
9121ac4b82bSMike Smith 	/*
9131ac4b82bSMike Smith 	 * Start a background consistency check on this drive.
9141ac4b82bSMike Smith 	 */
9151ac4b82bSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
9161ac4b82bSMike Smith 	/* XXX implement */
9171ac4b82bSMike Smith 	break;
9181ac4b82bSMike Smith 
9191ac4b82bSMike Smith 	/*
9201ac4b82bSMike Smith 	 * Get the status of the current rebuild or consistency check.
9211ac4b82bSMike Smith 	 */
9221ac4b82bSMike Smith     case MLXD_REBUILDSTAT:
9231ac4b82bSMike Smith 
9241ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {	/* may be a second or so out of date */
9251ac4b82bSMike Smith 	    mp->rs_drive = sc->mlx_rebuild;
9261ac4b82bSMike Smith 	    mp->rs_size = sc->mlx_sysdrive[sc->mlx_rebuild].ms_size;
9271ac4b82bSMike Smith 	    mp->rs_remaining = sc->mlx_rebuildstat;
9281ac4b82bSMike Smith 	    return(0);
9291ac4b82bSMike Smith 	} else if (sc->mlx_check >= 0) {
9301ac4b82bSMike Smith 	    /* XXX implement */
9311ac4b82bSMike Smith 	} else {
9321ac4b82bSMike Smith 	    /* XXX should return status of last completed operation? */
9331ac4b82bSMike Smith 	    return(EINVAL);
9341ac4b82bSMike Smith 	}
9351ac4b82bSMike Smith 
9361ac4b82bSMike Smith     }
9371ac4b82bSMike Smith     return(ENOIOCTL);
9381ac4b82bSMike Smith }
9391ac4b82bSMike Smith 
9401ac4b82bSMike Smith 
9411ac4b82bSMike Smith /********************************************************************************
9421ac4b82bSMike Smith  ********************************************************************************
9431ac4b82bSMike Smith                                                                 Status Monitoring
9441ac4b82bSMike Smith  ********************************************************************************
9451ac4b82bSMike Smith  ********************************************************************************/
9461ac4b82bSMike Smith 
9471ac4b82bSMike Smith /********************************************************************************
9481ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
9491ac4b82bSMike Smith  */
9501ac4b82bSMike Smith static void
9511ac4b82bSMike Smith mlx_periodic(void *data)
9521ac4b82bSMike Smith {
9531ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
9541ac4b82bSMike Smith 
955da8bb3a3SMike Smith     debug_called(1);
9561ac4b82bSMike Smith 
9571ac4b82bSMike Smith     /*
9581ac4b82bSMike Smith      * Run a bus pause?
9591ac4b82bSMike Smith      */
9601ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
9611ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
9621ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
9631ac4b82bSMike Smith 
9641ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
9651ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
9661ac4b82bSMike Smith 	sysbeep(500, hz);
9671ac4b82bSMike Smith 
9681ac4b82bSMike Smith 	/*
9691ac4b82bSMike Smith 	 * Bus pause still running?
9701ac4b82bSMike Smith 	 */
9711ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
9721ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
9731ac4b82bSMike Smith 
9741ac4b82bSMike Smith 	/* time to stop bus pause? */
9751ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
9761ac4b82bSMike Smith 	    mlx_pause_action(sc);
9771ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
9781ac4b82bSMike Smith 	    sysbeep(500, hz);
9791ac4b82bSMike Smith 	} else {
9801ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
9811ac4b82bSMike Smith 	}
9821ac4b82bSMike Smith 
9831ac4b82bSMike Smith 	/*
9841ac4b82bSMike Smith 	 * Run normal periodic activities?
9851ac4b82bSMike Smith 	 */
9865792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
9871ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
9881ac4b82bSMike Smith 
9891ac4b82bSMike Smith 	/*
9901ac4b82bSMike Smith 	 * Check controller status.
9915792b7feSMike Smith 	 *
9925792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
9931ac4b82bSMike Smith 	 */
994da8bb3a3SMike Smith 	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
995da8bb3a3SMike Smith 		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
9961ac4b82bSMike Smith 
9971ac4b82bSMike Smith 	/*
9981ac4b82bSMike Smith 	 * Check system drive status.
9991ac4b82bSMike Smith 	 *
10001ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
10011ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
10021ac4b82bSMike Smith 	 *     online.
10031ac4b82bSMike Smith 	 */
10041ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
10051ac4b82bSMike Smith 			mlx_periodic_enquiry);
10061ac4b82bSMike Smith 
10071ac4b82bSMike Smith 	/*
10081ac4b82bSMike Smith 	 * Get drive rebuild/check status
10091ac4b82bSMike Smith 	 */
10105792b7feSMike Smith 	if (sc->mlx_rebuild >= 0)
10111ac4b82bSMike Smith 	    mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
10121ac4b82bSMike Smith     }
10131ac4b82bSMike Smith 
10145792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
10155792b7feSMike Smith     mlx_done(sc);
10161ac4b82bSMike Smith 
10171ac4b82bSMike Smith     /* reschedule another poll next second or so */
10181ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
10191ac4b82bSMike Smith }
10201ac4b82bSMike Smith 
10211ac4b82bSMike Smith /********************************************************************************
10221ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
10231ac4b82bSMike Smith  */
10241ac4b82bSMike Smith static void
10251ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
10261ac4b82bSMike Smith {
10271ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
10281ac4b82bSMike Smith 
1029da8bb3a3SMike Smith     debug_called(1);
10301ac4b82bSMike Smith 
10311ac4b82bSMike Smith     /* Command completed OK? */
10321ac4b82bSMike Smith     if (mc->mc_status != 0) {
1033da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
10341ac4b82bSMike Smith 	goto out;
10351ac4b82bSMike Smith     }
10361ac4b82bSMike Smith 
10371ac4b82bSMike Smith     /* respond to command */
10381ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
10391ac4b82bSMike Smith 	/*
1040da8bb3a3SMike Smith 	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
1041da8bb3a3SMike Smith 	 * pointer yet.
1042da8bb3a3SMike Smith 	 */
1043da8bb3a3SMike Smith     case MLX_CMD_ENQUIRY_OLD:
1044da8bb3a3SMike Smith     {
1045da8bb3a3SMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
1046da8bb3a3SMike Smith 	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
1047da8bb3a3SMike Smith 	int				i;
1048da8bb3a3SMike Smith 
1049da8bb3a3SMike Smith 	/* convert data in-place to new format */
1050da8bb3a3SMike Smith 	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
1051da8bb3a3SMike Smith 	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1052da8bb3a3SMike Smith 	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1053da8bb3a3SMike Smith 	}
1054da8bb3a3SMike Smith 	me->me_misc_flags        = 0;
1055da8bb3a3SMike Smith 	me->me_rebuild_count     = meo->me_rebuild_count;
1056da8bb3a3SMike Smith 	me->me_dead_count        = meo->me_dead_count;
1057da8bb3a3SMike Smith 	me->me_critical_sd_count = meo->me_critical_sd_count;
1058da8bb3a3SMike Smith 	me->me_event_log_seq_num = 0;
1059da8bb3a3SMike Smith 	me->me_offline_sd_count  = meo->me_offline_sd_count;
1060da8bb3a3SMike Smith 	me->me_max_commands      = meo->me_max_commands;
1061da8bb3a3SMike Smith 	me->me_rebuild_flag      = meo->me_rebuild_flag;
1062da8bb3a3SMike Smith 	me->me_fwmajor           = meo->me_fwmajor;
1063da8bb3a3SMike Smith 	me->me_fwminor           = meo->me_fwminor;
1064da8bb3a3SMike Smith 	me->me_status_flags      = meo->me_status_flags;
1065da8bb3a3SMike Smith 	me->me_flash_age         = meo->me_flash_age;
1066da8bb3a3SMike Smith 	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
1067da8bb3a3SMike Smith 	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
1068da8bb3a3SMike Smith 		me->me_drvsize[i] = 0;		/* drive beyond supported range */
1069da8bb3a3SMike Smith 	    } else {
1070da8bb3a3SMike Smith 		me->me_drvsize[i] = meo->me_drvsize[i];
1071da8bb3a3SMike Smith 	    }
1072da8bb3a3SMike Smith 	}
1073da8bb3a3SMike Smith 	me->me_num_sys_drvs = meo->me_num_sys_drvs;
1074da8bb3a3SMike Smith     }
1075da8bb3a3SMike Smith     /* FALLTHROUGH */
1076da8bb3a3SMike Smith 
1077da8bb3a3SMike Smith 	/*
10781ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
10791ac4b82bSMike Smith 	 * checking the event log.
10801ac4b82bSMike Smith 	 */
10811ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
10821ac4b82bSMike Smith     {
10831ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
10841ac4b82bSMike Smith 
10859eee27f1SMike Smith 	if (sc->mlx_lastevent == -1) {
10869eee27f1SMike Smith 	    /* initialise our view of the event log */
10879eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
10889eee27f1SMike Smith 	} else if (me->me_event_log_seq_num != sc->mlx_lastevent) {
10891ac4b82bSMike Smith 	    /* record where current events are up to */
10901ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
1091da8bb3a3SMike Smith 	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
10921ac4b82bSMike Smith 
10939eee27f1SMike Smith 	    /* drain new eventlog entries */
10941ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
10951ac4b82bSMike Smith 	}
10961ac4b82bSMike Smith 	break;
10971ac4b82bSMike Smith     }
10981ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
10991ac4b82bSMike Smith     {
11001ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
11011ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
11021ac4b82bSMike Smith 	int				i;
11031ac4b82bSMike Smith 
11041ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
11051ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
11061ac4b82bSMike Smith 	     i++) {
11071ac4b82bSMike Smith 
11081ac4b82bSMike Smith 	    /* if disk is being rebuilt, we should not check it */
11091ac4b82bSMike Smith 	    if (dr->ms_state == MLX_SYSD_REBUILD) {
11101ac4b82bSMike Smith 		/* has state been changed by controller? */
11111ac4b82bSMike Smith 		if (dr->ms_state != mes[i].sd_state) {
11121ac4b82bSMike Smith 		    switch(mes[i].sd_state) {
11131ac4b82bSMike Smith 		    case MLX_SYSD_OFFLINE:
11141ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive offline\n");
11151ac4b82bSMike Smith 			break;
11161ac4b82bSMike Smith 		    case MLX_SYSD_ONLINE:
11171ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive online\n");
11181ac4b82bSMike Smith 			break;
11191ac4b82bSMike Smith 		    case MLX_SYSD_CRITICAL:
11201ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive critical\n");
11211ac4b82bSMike Smith 			break;
11221ac4b82bSMike Smith 		    }
11231ac4b82bSMike Smith 		    /* save new state */
11241ac4b82bSMike Smith 		    dr->ms_state = mes[i].sd_state;
11251ac4b82bSMike Smith 		}
11261ac4b82bSMike Smith 	    }
11271ac4b82bSMike Smith 	}
11281ac4b82bSMike Smith 	break;
11291ac4b82bSMike Smith     }
11301ac4b82bSMike Smith     default:
11315792b7feSMike Smith 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __FUNCTION__, mc->mc_mailbox[0]);
11321ac4b82bSMike Smith 	break;
11331ac4b82bSMike Smith     }
11341ac4b82bSMike Smith 
11351ac4b82bSMike Smith  out:
11361ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
11371ac4b82bSMike Smith     mlx_releasecmd(mc);
11381ac4b82bSMike Smith }
11391ac4b82bSMike Smith 
11401ac4b82bSMike Smith /********************************************************************************
11411ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
11421ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
11431ac4b82bSMike Smith  */
11441ac4b82bSMike Smith static void
11451ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
11461ac4b82bSMike Smith {
11471ac4b82bSMike Smith     struct mlx_command	*mc;
11481ac4b82bSMike Smith     void		*result = NULL;
11491ac4b82bSMike Smith     int			error;
11501ac4b82bSMike Smith 
1151da8bb3a3SMike Smith     debug_called(1);
11521ac4b82bSMike Smith 
11531ac4b82bSMike Smith     /* get ourselves a command buffer */
11541ac4b82bSMike Smith     error = 1;
11551ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
11561ac4b82bSMike Smith 	goto out;
11571ac4b82bSMike Smith     /* allocate the response structure */
115833c8cb18SMike Smith     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL)
11591ac4b82bSMike Smith 	goto out;
11601ac4b82bSMike Smith     /* get a command slot */
11611ac4b82bSMike Smith     if (mlx_getslot(mc))
11621ac4b82bSMike Smith 	goto out;
11631ac4b82bSMike Smith 
11641ac4b82bSMike Smith     /* map the command so the controller can see it */
11651ac4b82bSMike Smith     mc->mc_data = result;
116633c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
11671ac4b82bSMike Smith     mlx_mapcmd(mc);
11681ac4b82bSMike Smith 
11691ac4b82bSMike Smith     /* build the command to get one entry */
11701ac4b82bSMike Smith     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
11711ac4b82bSMike Smith     mc->mc_complete = mlx_periodic_eventlog_respond;
11721ac4b82bSMike Smith     mc->mc_private = mc;
11731ac4b82bSMike Smith 
11741ac4b82bSMike Smith     /* start the command */
11751ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
11761ac4b82bSMike Smith 	goto out;
11771ac4b82bSMike Smith 
11781ac4b82bSMike Smith     error = 0;			/* success */
11791ac4b82bSMike Smith  out:
118033c8cb18SMike Smith     if (error != 0) {
11811ac4b82bSMike Smith 	if (mc != NULL)
11821ac4b82bSMike Smith 	    mlx_releasecmd(mc);
118333c8cb18SMike Smith 	if (result != NULL)
11841ac4b82bSMike Smith 	    free(result, M_DEVBUF);
11851ac4b82bSMike Smith     }
118633c8cb18SMike Smith }
11871ac4b82bSMike Smith 
11881ac4b82bSMike Smith /********************************************************************************
11891ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
11901ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
11911ac4b82bSMike Smith  */
11921ac4b82bSMike Smith static char *mlx_sense_messages[] = {
11931ac4b82bSMike Smith     "because write recovery failed",
11941ac4b82bSMike Smith     "because of SCSI bus reset failure",
11951ac4b82bSMike Smith     "because of double check condition",
11961ac4b82bSMike Smith     "because it was removed",
11971ac4b82bSMike Smith     "because of gross error on SCSI chip",
11981ac4b82bSMike Smith     "because of bad tag returned from drive",
11991ac4b82bSMike Smith     "because of timeout on SCSI command",
12001ac4b82bSMike Smith     "because of reset SCSI command issued from system",
12011ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
12021ac4b82bSMike Smith     "because of 'kill drive' command from system",
12031ac4b82bSMike Smith     "because of selection timeout",
12041ac4b82bSMike Smith     "due to SCSI phase sequence error",
12051ac4b82bSMike Smith     "due to unknown status"
12061ac4b82bSMike Smith };
12071ac4b82bSMike Smith 
12081ac4b82bSMike Smith static void
12091ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
12101ac4b82bSMike Smith {
12111ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
12121ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
12131ac4b82bSMike Smith     char			*reason;
12141ac4b82bSMike Smith 
1215da8bb3a3SMike Smith     debug_called(1);
12161ac4b82bSMike Smith 
12175792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
12181ac4b82bSMike Smith     if (mc->mc_status == 0) {
12191ac4b82bSMike Smith 
12201ac4b82bSMike Smith 	/* handle event log message */
12211ac4b82bSMike Smith 	switch(el->el_type) {
12221ac4b82bSMike Smith 	    /*
12231ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
12241ac4b82bSMike Smith 	     * The tests here are probably incomplete.
12251ac4b82bSMike Smith 	     */
12261ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
12271ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
12281ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
12291ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
12301ac4b82bSMike Smith 		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
12311ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
12321ac4b82bSMike Smith 		} else {
12331ac4b82bSMike Smith 		    reason = "for unknown reason";
12341ac4b82bSMike Smith 		}
12351ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
12361ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
12371ac4b82bSMike Smith 	    }
12381ac4b82bSMike Smith 	    /* SCSI drive was reset? */
12391ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
12401ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
12411ac4b82bSMike Smith 			      el->el_channel, el->el_target);
12421ac4b82bSMike Smith 	    }
12431ac4b82bSMike Smith 	    /* SCSI drive error? */
12441ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
12451ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
12461ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
12471ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
12481ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
12491ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
12501ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
12511ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
12521ac4b82bSMike Smith 	    }
12531ac4b82bSMike Smith 	    break;
12541ac4b82bSMike Smith 
12551ac4b82bSMike Smith 	default:
12561ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
12571ac4b82bSMike Smith 	    break;
12581ac4b82bSMike Smith 	}
12591ac4b82bSMike Smith     } else {
12601ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
12611ac4b82bSMike Smith     }
12621ac4b82bSMike Smith 
12631ac4b82bSMike Smith     /* dispose of command and data */
12641ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12651ac4b82bSMike Smith     mlx_releasecmd(mc);
12661ac4b82bSMike Smith 
12671ac4b82bSMike Smith     /* is there another message to obtain? */
12681ac4b82bSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent)
12691ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
12701ac4b82bSMike Smith }
12711ac4b82bSMike Smith 
12721ac4b82bSMike Smith /********************************************************************************
12731ac4b82bSMike Smith  * Handle the completion of a rebuild operation.
12741ac4b82bSMike Smith  */
12751ac4b82bSMike Smith static void
12761ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
12771ac4b82bSMike Smith {
12781ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
12791ac4b82bSMike Smith     struct mlx_rebuild_stat	*mr = (struct mlx_rebuild_stat *)mc->mc_private;
12801ac4b82bSMike Smith 
12811ac4b82bSMike Smith     switch(mc->mc_status) {
12821ac4b82bSMike Smith     case 0:				/* all OK, rebuild still running */
12831ac4b82bSMike Smith 	sc->mlx_rebuildstat = mr->rb_remaining;
12841ac4b82bSMike Smith 	break;
12851ac4b82bSMike Smith 
12861ac4b82bSMike Smith     case 0x0105:			/* rebuild/check finished */
12871ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {
12881ac4b82bSMike Smith 	    device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild completed\n");
12891ac4b82bSMike Smith 	    sc->mlx_rebuild = -1;
12901ac4b82bSMike Smith 	} else if (sc->mlx_check >= 0) {
12911ac4b82bSMike Smith 	    device_printf(sc->mlx_sysdrive[sc->mlx_check].ms_disk, "consistency check completed\n");
12921ac4b82bSMike Smith 	    sc->mlx_check = -1;
12931ac4b82bSMike Smith 	} else {
12941ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");
12951ac4b82bSMike Smith 	}
12961ac4b82bSMike Smith 	break;
12971ac4b82bSMike Smith     }
12981ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12991ac4b82bSMike Smith     mlx_releasecmd(mc);
13001ac4b82bSMike Smith }
13011ac4b82bSMike Smith 
13021ac4b82bSMike Smith /********************************************************************************
13031ac4b82bSMike Smith  ********************************************************************************
13041ac4b82bSMike Smith                                                                     Channel Pause
13051ac4b82bSMike Smith  ********************************************************************************
13061ac4b82bSMike Smith  ********************************************************************************/
13071ac4b82bSMike Smith 
13081ac4b82bSMike Smith /********************************************************************************
13091ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
13101ac4b82bSMike Smith  * the pause.
13111ac4b82bSMike Smith  */
13121ac4b82bSMike Smith static void
13131ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
13141ac4b82bSMike Smith {
13151ac4b82bSMike Smith     struct mlx_command	*mc;
13161ac4b82bSMike Smith     int			failsafe, i, command;
13171ac4b82bSMike Smith 
13181ac4b82bSMike Smith     /* What are we doing here? */
13191ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
13201ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
13211ac4b82bSMike Smith 	failsafe = 0;
13221ac4b82bSMike Smith 
13231ac4b82bSMike Smith     } else {
13241ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
13251ac4b82bSMike Smith 
13261ac4b82bSMike Smith 	/*
13271ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
13281ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
13291ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
13301ac4b82bSMike Smith 	 */
13311ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
13321ac4b82bSMike Smith 	if (failsafe > 0xf) {
13331ac4b82bSMike Smith 	    failsafe = 0xf;
13341ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
13351ac4b82bSMike Smith 	}
13361ac4b82bSMike Smith     }
13371ac4b82bSMike Smith 
13381ac4b82bSMike Smith     /* build commands for every channel requested */
13399eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
13401ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
13411ac4b82bSMike Smith 
13421ac4b82bSMike Smith 	    /* get ourselves a command buffer */
13431ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
13441ac4b82bSMike Smith 		goto fail;
13451ac4b82bSMike Smith 	    /* get a command slot */
13461ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
13471ac4b82bSMike Smith 	    if (mlx_getslot(mc))
13481ac4b82bSMike Smith 		goto fail;
13491ac4b82bSMike Smith 
13501ac4b82bSMike Smith 	    /* build the command */
13511ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
13521ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
13531ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
13541ac4b82bSMike Smith 	    if (mlx_start(mc))
13551ac4b82bSMike Smith 		goto fail;
13561ac4b82bSMike Smith 	    /* command submitted OK */
13571ac4b82bSMike Smith 	    return;
13581ac4b82bSMike Smith 
13591ac4b82bSMike Smith 	fail:
13601ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
13611ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
13621ac4b82bSMike Smith 	    if (mc != NULL)
13631ac4b82bSMike Smith 		mlx_releasecmd(mc);
13641ac4b82bSMike Smith 	}
13651ac4b82bSMike Smith     }
13661ac4b82bSMike Smith }
13671ac4b82bSMike Smith 
13681ac4b82bSMike Smith static void
13691ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
13701ac4b82bSMike Smith {
13711ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
13721ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
13731ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
13741ac4b82bSMike Smith 
13751ac4b82bSMike Smith     if (mc->mc_status != 0) {
13761ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
13771ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
13781ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
13791ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
138072c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
13811ac4b82bSMike Smith     } else {
13821ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
13831ac4b82bSMike Smith     }
13841ac4b82bSMike Smith     mlx_releasecmd(mc);
13851ac4b82bSMike Smith }
13861ac4b82bSMike Smith 
13871ac4b82bSMike Smith /********************************************************************************
13881ac4b82bSMike Smith  ********************************************************************************
13891ac4b82bSMike Smith                                                                Command Submission
13901ac4b82bSMike Smith  ********************************************************************************
13911ac4b82bSMike Smith  ********************************************************************************/
13921ac4b82bSMike Smith 
13931ac4b82bSMike Smith /********************************************************************************
13941ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
13951ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
13961ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
13971ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
13981ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
13991ac4b82bSMike Smith  */
14001ac4b82bSMike Smith static void *
14011ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
14021ac4b82bSMike Smith {
14031ac4b82bSMike Smith     struct mlx_command	*mc;
14041ac4b82bSMike Smith     void		*result;
14051ac4b82bSMike Smith     int			error;
14061ac4b82bSMike Smith 
1407da8bb3a3SMike Smith     debug_called(1);
14081ac4b82bSMike Smith 
14091ac4b82bSMike Smith     /* get ourselves a command buffer */
14101ac4b82bSMike Smith     error = 1;
14111ac4b82bSMike Smith     result = NULL;
14121ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14131ac4b82bSMike Smith 	goto out;
14141ac4b82bSMike Smith     /* allocate the response structure */
14151ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
14161ac4b82bSMike Smith 	goto out;
14171ac4b82bSMike Smith     /* get a command slot */
14181ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
14191ac4b82bSMike Smith     if (mlx_getslot(mc))
14201ac4b82bSMike Smith 	goto out;
14211ac4b82bSMike Smith 
14221ac4b82bSMike Smith     /* map the command so the controller can see it */
14231ac4b82bSMike Smith     mc->mc_data = result;
14241ac4b82bSMike Smith     mc->mc_length = bufsize;
14251ac4b82bSMike Smith     mlx_mapcmd(mc);
14261ac4b82bSMike Smith 
14271ac4b82bSMike Smith     /* build an enquiry command */
14281ac4b82bSMike Smith     mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
14291ac4b82bSMike Smith 
14301ac4b82bSMike Smith     /* do we want a completion callback? */
14311ac4b82bSMike Smith     if (complete != NULL) {
14321ac4b82bSMike Smith 	mc->mc_complete = complete;
14331ac4b82bSMike Smith 	mc->mc_private = mc;
14341ac4b82bSMike Smith 	if ((error = mlx_start(mc)) != 0)
14351ac4b82bSMike Smith 	    goto out;
14361ac4b82bSMike Smith     } else {
14371ac4b82bSMike Smith 	/* run the command in either polled or wait mode */
14381ac4b82bSMike Smith 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
14391ac4b82bSMike Smith 	    goto out;
14401ac4b82bSMike Smith 
14411ac4b82bSMike Smith 	/* command completed OK? */
14421ac4b82bSMike Smith 	if (mc->mc_status != 0) {
14431ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc));
14441ac4b82bSMike Smith 	    goto out;
14451ac4b82bSMike Smith 	}
14461ac4b82bSMike Smith     }
14471ac4b82bSMike Smith     error = 0;			/* success */
14481ac4b82bSMike Smith  out:
14491ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
14501ac4b82bSMike Smith     if ((complete == NULL) && (mc != NULL))
14511ac4b82bSMike Smith 	mlx_releasecmd(mc);
145233c8cb18SMike Smith     /* we got an error, and we allocated a result */
14531ac4b82bSMike Smith     if ((error != 0) && (result != NULL)) {
14541ac4b82bSMike Smith 	free(result, M_DEVBUF);
14551ac4b82bSMike Smith 	result = NULL;
14561ac4b82bSMike Smith     }
14571ac4b82bSMike Smith     return(result);
14581ac4b82bSMike Smith }
14591ac4b82bSMike Smith 
14601ac4b82bSMike Smith 
14611ac4b82bSMike Smith /********************************************************************************
14621ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
14631ac4b82bSMike Smith  *
14641ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
14651ac4b82bSMike Smith  * the flush operation completes or fails.
14661ac4b82bSMike Smith  */
14671ac4b82bSMike Smith static int
14681ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
14691ac4b82bSMike Smith {
14701ac4b82bSMike Smith     struct mlx_command	*mc;
14711ac4b82bSMike Smith     int			error;
14721ac4b82bSMike Smith 
1473da8bb3a3SMike Smith     debug_called(1);
14741ac4b82bSMike Smith 
14751ac4b82bSMike Smith     /* get ourselves a command buffer */
14761ac4b82bSMike Smith     error = 1;
14771ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14781ac4b82bSMike Smith 	goto out;
14791ac4b82bSMike Smith     /* get a command slot */
14801ac4b82bSMike Smith     if (mlx_getslot(mc))
14811ac4b82bSMike Smith 	goto out;
14821ac4b82bSMike Smith 
14831ac4b82bSMike Smith     /* build a flush command */
14841ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
14851ac4b82bSMike Smith 
14865792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
14875792b7feSMike Smith     if (mlx_poll_command(mc))
14881ac4b82bSMike Smith 	goto out;
14891ac4b82bSMike Smith 
14901ac4b82bSMike Smith     /* command completed OK? */
14911ac4b82bSMike Smith     if (mc->mc_status != 0) {
14921ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
14931ac4b82bSMike Smith 	goto out;
14941ac4b82bSMike Smith     }
14951ac4b82bSMike Smith 
14961ac4b82bSMike Smith     error = 0;			/* success */
14971ac4b82bSMike Smith  out:
14981ac4b82bSMike Smith     if (mc != NULL)
14991ac4b82bSMike Smith 	mlx_releasecmd(mc);
15001ac4b82bSMike Smith     return(error);
15011ac4b82bSMike Smith }
15021ac4b82bSMike Smith 
15031ac4b82bSMike Smith /********************************************************************************
15041ac4b82bSMike Smith  * Start a background rebuild on the nominated controller/channel/target.
15051ac4b82bSMike Smith  *
15061ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
15071ac4b82bSMike Smith  * operation has started or been refused.
15081ac4b82bSMike Smith  */
15091ac4b82bSMike Smith static int
15101ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
15111ac4b82bSMike Smith {
15121ac4b82bSMike Smith     struct mlx_command	*mc;
15131ac4b82bSMike Smith     int			error;
15141ac4b82bSMike Smith 
1515da8bb3a3SMike Smith     debug_called(1);
15161ac4b82bSMike Smith 
15171ac4b82bSMike Smith     /* get ourselves a command buffer */
15181ac4b82bSMike Smith     error = 0x10000;
15191ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
15201ac4b82bSMike Smith 	goto out;
15211ac4b82bSMike Smith     /* get a command slot */
15221ac4b82bSMike Smith     if (mlx_getslot(mc))
15231ac4b82bSMike Smith 	goto out;
15241ac4b82bSMike Smith 
15251ac4b82bSMike Smith     /* build a rebuild command */
15261ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
15271ac4b82bSMike Smith 
15281ac4b82bSMike Smith     /* run the command in either polled or wait mode */
15291ac4b82bSMike Smith     if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
15301ac4b82bSMike Smith 	goto out;
15311ac4b82bSMike Smith 
15321ac4b82bSMike Smith     /* command completed OK? */
15331ac4b82bSMike Smith     if (mc->mc_status != 0) {
15341ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
15351ac4b82bSMike Smith     } else {
15361ac4b82bSMike Smith 	device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild started");
15371ac4b82bSMike Smith     }
15381ac4b82bSMike Smith     error = mc->mc_status;
15391ac4b82bSMike Smith 
15401ac4b82bSMike Smith  out:
15411ac4b82bSMike Smith     if (mc != NULL)
15421ac4b82bSMike Smith 	mlx_releasecmd(mc);
15431ac4b82bSMike Smith     return(error);
15441ac4b82bSMike Smith }
15451ac4b82bSMike Smith 
15461ac4b82bSMike Smith /********************************************************************************
15471ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
15481ac4b82bSMike Smith  *
15491ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
15501ac4b82bSMike Smith  */
15511ac4b82bSMike Smith static int
15521ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
15531ac4b82bSMike Smith {
15541ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
15551ac4b82bSMike Smith     int			error, count;
15561ac4b82bSMike Smith 
1557da8bb3a3SMike Smith     debug_called(1);
15581ac4b82bSMike Smith 
15591ac4b82bSMike Smith     mc->mc_complete = NULL;
15601ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
15611ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
15621ac4b82bSMike Smith 	return(error);
15631ac4b82bSMike Smith 
15641ac4b82bSMike Smith     count = 0;
15651ac4b82bSMike Smith     /* XXX better timeout? */
15661ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
15671ac4b82bSMike Smith 	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
15681ac4b82bSMike Smith     }
15691ac4b82bSMike Smith 
15701ac4b82bSMike Smith     if (mc->mc_status != 0) {
1571da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
15721ac4b82bSMike Smith 	return(EIO);
15731ac4b82bSMike Smith     }
15741ac4b82bSMike Smith     return(0);
15751ac4b82bSMike Smith }
15761ac4b82bSMike Smith 
15771ac4b82bSMike Smith 
15781ac4b82bSMike Smith /********************************************************************************
15791ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
15801ac4b82bSMike Smith  *
1581da8bb3a3SMike Smith  * Should only be used when interrupts can't be relied upon. Returns 0 on
15821ac4b82bSMike Smith  * success, nonzero on error.
15831ac4b82bSMike Smith  * Successfully completed commands are dequeued.
15841ac4b82bSMike Smith  */
15851ac4b82bSMike Smith static int
15861ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
15871ac4b82bSMike Smith {
15881ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
15891ac4b82bSMike Smith     int			error, count, s;
15901ac4b82bSMike Smith 
1591da8bb3a3SMike Smith     debug_called(1);
15921ac4b82bSMike Smith 
15931ac4b82bSMike Smith     mc->mc_complete = NULL;
15941ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
15951ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
15961ac4b82bSMike Smith 	return(error);
15971ac4b82bSMike Smith 
15981ac4b82bSMike Smith     count = 0;
15991ac4b82bSMike Smith     do {
16001ac4b82bSMike Smith 	/* poll for completion */
16011ac4b82bSMike Smith 	mlx_done(mc->mc_sc);
1602da8bb3a3SMike Smith 
1603da8bb3a3SMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
16041ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
16051ac4b82bSMike Smith 	s = splbio();
16064b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
16071ac4b82bSMike Smith 	splx(s);
16081ac4b82bSMike Smith 	return(0);
16091ac4b82bSMike Smith     }
16101ac4b82bSMike Smith     device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
16111ac4b82bSMike Smith     return(EIO);
16121ac4b82bSMike Smith }
16131ac4b82bSMike Smith 
16141ac4b82bSMike Smith /********************************************************************************
16151ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
16161ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
16171ac4b82bSMike Smith  *
16181ac4b82bSMike Smith  * Must be called at splbio or in an equivalent fashion that prevents
1619da8bb3a3SMike Smith  * reentry or activity on the bufq.
16201ac4b82bSMike Smith  */
16211ac4b82bSMike Smith static void
16221ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
16231ac4b82bSMike Smith {
16241ac4b82bSMike Smith     struct mlx_command	*mc;
16251ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
16261ac4b82bSMike Smith     struct buf		*bp;
16271ac4b82bSMike Smith     int			blkcount;
16281ac4b82bSMike Smith     int			driveno;
16291ac4b82bSMike Smith     int			cmd;
16304b006d7bSMike Smith     int			s;
16311ac4b82bSMike Smith 
16325792b7feSMike Smith     /* avoid reentrancy */
16335792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_STARTING))
16345792b7feSMike Smith 	return;
16355792b7feSMike Smith 
16361ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
16374b006d7bSMike Smith     s = splbio();
16381ac4b82bSMike Smith     for (;;) {
16391ac4b82bSMike Smith 
16401ac4b82bSMike Smith 	/* see if there's work to be done */
16411ac4b82bSMike Smith 	if ((bp = bufq_first(&sc->mlx_bufq)) == NULL)
16421ac4b82bSMike Smith 	    break;
16431ac4b82bSMike Smith 	/* get a command */
16441ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
16451ac4b82bSMike Smith 	    break;
16461ac4b82bSMike Smith 	/* get a slot for the command */
16471ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
16481ac4b82bSMike Smith 	    mlx_releasecmd(mc);
16491ac4b82bSMike Smith 	    break;
16501ac4b82bSMike Smith 	}
16511ac4b82bSMike Smith 	/* get the buf containing our work */
16521ac4b82bSMike Smith 	bufq_remove(&sc->mlx_bufq, bp);
16531ac4b82bSMike Smith 	sc->mlx_waitbufs--;
16544b006d7bSMike Smith 	splx(s);
16551ac4b82bSMike Smith 
16561ac4b82bSMike Smith 	/* connect the buf to the command */
16571ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
16581ac4b82bSMike Smith 	mc->mc_private = bp;
16591ac4b82bSMike Smith 	mc->mc_data = bp->b_data;
16601ac4b82bSMike Smith 	mc->mc_length = bp->b_bcount;
16611ac4b82bSMike Smith 	if (bp->b_flags & B_READ) {
16621ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAIN;
1663da8bb3a3SMike Smith 	    cmd = MLX_CMD_READSG;
16641ac4b82bSMike Smith 	} else {
16651ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAOUT;
1666da8bb3a3SMike Smith 	    cmd = MLX_CMD_WRITESG;
16671ac4b82bSMike Smith 	}
16681ac4b82bSMike Smith 
16691ac4b82bSMike Smith 	/* map the command so the controller can work with it */
16701ac4b82bSMike Smith 	mlx_mapcmd(mc);
16711ac4b82bSMike Smith 
16721ac4b82bSMike Smith 	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
1673f6b84b08SMike Smith 	mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1;
16745792b7feSMike Smith 	driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
167597adfbafSMike Smith 	blkcount = (bp->b_bcount + MLX_BLKSIZE - 1) / MLX_BLKSIZE;
16761ac4b82bSMike Smith 
1677cd4ace0cSMike Smith 	if ((bp->b_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
16781ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n",
1679cd4ace0cSMike Smith 			  bp->b_pblkno, blkcount, sc->mlx_sysdrive[driveno].ms_size);
16801ac4b82bSMike Smith 
16811ac4b82bSMike Smith 	/*
16821ac4b82bSMike Smith 	 * Build the I/O command.  Note that the SG list type bits are set to zero,
16831ac4b82bSMike Smith 	 * denoting the format of SG list that we are using.
16841ac4b82bSMike Smith 	 */
1685da8bb3a3SMike Smith 	if (sc->mlx_iftype == MLX_IFTYPE_2) {
1686da8bb3a3SMike Smith 	    mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD,
1687da8bb3a3SMike Smith 			   blkcount & 0xff, 				/* xfer length low byte */
1688da8bb3a3SMike Smith 			   bp->b_pblkno,				/* physical block number */
1689da8bb3a3SMike Smith 			   driveno,					/* target drive number */
1690da8bb3a3SMike Smith 			   mc->mc_sgphys,				/* location of SG list */
1691da8bb3a3SMike Smith 			   mc->mc_nsgent & 0x3f);			/* size of SG list (top 3 bits clear) */
1692da8bb3a3SMike Smith 	} else {
16931ac4b82bSMike Smith 	    mlx_make_type5(mc, cmd,
16941ac4b82bSMike Smith 			   blkcount & 0xff, 				/* xfer length low byte */
1695f01f2af6SMike Smith 			   (driveno << 3) | ((blkcount >> 8) & 0x07),	/* target and length high 3 bits */
1696cd4ace0cSMike Smith 			   bp->b_pblkno,				/* physical block number */
16971ac4b82bSMike Smith 			   mc->mc_sgphys,				/* location of SG list */
1698da8bb3a3SMike Smith 			   mc->mc_nsgent & 0x3f);			/* size of SG list (top 3 bits clear) */
1699da8bb3a3SMike Smith 	}
17001ac4b82bSMike Smith 
17011ac4b82bSMike Smith 	/* try to give command to controller */
17021ac4b82bSMike Smith 	if (mlx_start(mc) != 0) {
17031ac4b82bSMike Smith 	    /* fail the command */
17041ac4b82bSMike Smith 	    mc->mc_status = MLX_STATUS_WEDGED;
17051ac4b82bSMike Smith 	    mlx_completeio(mc);
17061ac4b82bSMike Smith 	}
17074b006d7bSMike Smith 	s = splbio();
17081ac4b82bSMike Smith     }
17094b006d7bSMike Smith     splx(s);
17105792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_STARTING);
17111ac4b82bSMike Smith }
17121ac4b82bSMike Smith 
17131ac4b82bSMike Smith /********************************************************************************
17141ac4b82bSMike Smith  * Handle completion of an I/O command.
17151ac4b82bSMike Smith  */
17161ac4b82bSMike Smith static void
17171ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
17181ac4b82bSMike Smith {
17191ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17201ac4b82bSMike Smith     struct buf		*bp = (struct buf *)mc->mc_private;
1721f6b84b08SMike Smith     struct mlxd_softc	*mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1;
17221ac4b82bSMike Smith 
17231ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
17241ac4b82bSMike Smith 	bp->b_error = EIO;
17251ac4b82bSMike Smith 	bp->b_flags |= B_ERROR;
17261ac4b82bSMike Smith 
17271ac4b82bSMike Smith 	switch(mc->mc_status) {
17281ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
17291ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1730f6b84b08SMike Smith 	    /* should signal this with a return code */
17311ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
17321ac4b82bSMike Smith 	    break;
17331ac4b82bSMike Smith 
17341ac4b82bSMike Smith 	default:				/* other I/O error */
17351ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
17361ac4b82bSMike Smith #if 0
1737cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
1738cd4ace0cSMike Smith 			  bp->b_bcount, bp->b_bcount / MLX_BLKSIZE, bp->b_pblkno);
17391ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
17401ac4b82bSMike Smith #endif
17411ac4b82bSMike Smith 	    break;
17421ac4b82bSMike Smith 	}
17431ac4b82bSMike Smith     }
17441ac4b82bSMike Smith     mlx_releasecmd(mc);
17451ac4b82bSMike Smith     mlxd_intr(bp);
17461ac4b82bSMike Smith }
17471ac4b82bSMike Smith 
17481ac4b82bSMike Smith /********************************************************************************
17491ac4b82bSMike Smith  * Take a command from user-space and try to run it.
1750da8bb3a3SMike Smith  *
1751da8bb3a3SMike Smith  * XXX Note that this can't perform very much in the way of error checking, and
1752da8bb3a3SMike Smith  *     as such, applications _must_ be considered trustworthy.
1753da8bb3a3SMike Smith  * XXX Commands using S/G for data are not supported.
17541ac4b82bSMike Smith  */
17551ac4b82bSMike Smith static int
17561ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
17571ac4b82bSMike Smith {
17581ac4b82bSMike Smith     struct mlx_command	*mc;
1759da8bb3a3SMike Smith     struct mlx_dcdb	*dcdb;
17601ac4b82bSMike Smith     void		*kbuf;
17611ac4b82bSMike Smith     int			error;
17621ac4b82bSMike Smith 
1763da8bb3a3SMike Smith     debug_called(0);
1764da8bb3a3SMike Smith 
17651ac4b82bSMike Smith     kbuf = NULL;
17661ac4b82bSMike Smith     mc = NULL;
1767da8bb3a3SMike Smith     dcdb = NULL;
17681ac4b82bSMike Smith     error = ENOMEM;
1769da8bb3a3SMike Smith 
1770da8bb3a3SMike Smith     /* get ourselves a command and copy in from user space */
17711ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
17721ac4b82bSMike Smith 	goto out;
17731ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
1774da8bb3a3SMike Smith     debug(0, "got command buffer");
1775da8bb3a3SMike Smith 
1776da8bb3a3SMike Smith     /* if we need a buffer for data transfer, allocate one and copy in its initial contents */
1777da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1778da8bb3a3SMike Smith 	if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
1779da8bb3a3SMike Smith 	    (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))
17801ac4b82bSMike Smith 	    goto out;
1781da8bb3a3SMike Smith 	debug(0, "got kernel buffer");
1782da8bb3a3SMike Smith     }
17831ac4b82bSMike Smith 
17841ac4b82bSMike Smith     /* get a command slot */
17851ac4b82bSMike Smith     if (mlx_getslot(mc))
17861ac4b82bSMike Smith 	goto out;
1787da8bb3a3SMike Smith     debug(0, "got a slot");
17881ac4b82bSMike Smith 
17891ac4b82bSMike Smith     /* map the command so the controller can see it */
17901ac4b82bSMike Smith     mc->mc_data = kbuf;
17911ac4b82bSMike Smith     mc->mc_length = mu->mu_datasize;
17921ac4b82bSMike Smith     mlx_mapcmd(mc);
1793da8bb3a3SMike Smith     debug(0, "mapped");
17941ac4b82bSMike Smith 
1795da8bb3a3SMike Smith     /*
1796da8bb3a3SMike Smith      * If this is a passthrough SCSI command, the DCDB is packed at the
1797da8bb3a3SMike Smith      * beginning of the data area.  Fix up the DCDB to point to the correct physical
1798da8bb3a3SMike Smith      * address and override any bufptr supplied by the caller since we know
1799da8bb3a3SMike Smith      * what it's meant to be.
1800da8bb3a3SMike Smith      */
1801da8bb3a3SMike Smith     if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
1802da8bb3a3SMike Smith 	dcdb = (struct mlx_dcdb *)kbuf;
1803da8bb3a3SMike Smith 	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
1804da8bb3a3SMike Smith 	mu->mu_bufptr = 8;
18051ac4b82bSMike Smith     }
18061ac4b82bSMike Smith 
1807da8bb3a3SMike Smith     /*
1808da8bb3a3SMike Smith      * If there's a data buffer, fix up the command's buffer pointer.
1809da8bb3a3SMike Smith      */
1810da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1811da8bb3a3SMike Smith 
1812da8bb3a3SMike Smith 	/* range check the pointer to physical buffer address */
1813da8bb3a3SMike Smith 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - sizeof(u_int32_t)))) {
1814da8bb3a3SMike Smith 	    error = EINVAL;
1815da8bb3a3SMike Smith 	    goto out;
1816da8bb3a3SMike Smith 	}
1817da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
1818da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
1819da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
1820da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
1821da8bb3a3SMike Smith     }
1822da8bb3a3SMike Smith     debug(0, "command fixup");
1823da8bb3a3SMike Smith 
18241ac4b82bSMike Smith     /* submit the command and wait */
18251ac4b82bSMike Smith     if ((error = mlx_wait_command(mc)) != 0)
18261ac4b82bSMike Smith 	goto out;
18271ac4b82bSMike Smith 
18281ac4b82bSMike Smith     /* copy out status and data */
18291ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
18301ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
18311ac4b82bSMike Smith 	goto out;
18321ac4b82bSMike Smith     error = 0;
18331ac4b82bSMike Smith 
18341ac4b82bSMike Smith  out:
18351ac4b82bSMike Smith     mlx_releasecmd(mc);
18361ac4b82bSMike Smith     if (kbuf != NULL)
18371ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
18381ac4b82bSMike Smith     return(error);
18391ac4b82bSMike Smith }
18401ac4b82bSMike Smith 
18411ac4b82bSMike Smith /********************************************************************************
18421ac4b82bSMike Smith  ********************************************************************************
18431ac4b82bSMike Smith                                                         Command I/O to Controller
18441ac4b82bSMike Smith  ********************************************************************************
18451ac4b82bSMike Smith  ********************************************************************************/
18461ac4b82bSMike Smith 
18471ac4b82bSMike Smith /********************************************************************************
18481ac4b82bSMike Smith  * Find a free command slot for (mc).
18491ac4b82bSMike Smith  *
18501ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
18511ac4b82bSMike Smith  * 4 slots free for priority commands.
18521ac4b82bSMike Smith  */
18531ac4b82bSMike Smith static int
18541ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
18551ac4b82bSMike Smith {
18561ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18579eee27f1SMike Smith     int			s, slot;
18581ac4b82bSMike Smith 
1859da8bb3a3SMike Smith     debug_called(1);
18601ac4b82bSMike Smith 
18611ac4b82bSMike Smith     /* enforce slot-usage limit */
18629eee27f1SMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ?
18639eee27f1SMike Smith 			     sc->mlx_maxiop : sc->mlx_maxiop - 4))
18641ac4b82bSMike Smith 	return(EBUSY);
18651ac4b82bSMike Smith 
18661ac4b82bSMike Smith     /*
18671ac4b82bSMike Smith      * Allocate an outstanding command slot
18681ac4b82bSMike Smith      *
18691ac4b82bSMike Smith      * XXX linear search is slow
18701ac4b82bSMike Smith      */
18711ac4b82bSMike Smith     s = splbio();
18721ac4b82bSMike Smith     for (slot = 0; slot < sc->mlx_maxiop; slot++) {
1873da8bb3a3SMike Smith 	debug(2, "try slot %d", slot);
18741ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
18751ac4b82bSMike Smith 	    break;
18761ac4b82bSMike Smith     }
18771ac4b82bSMike Smith     if (slot < sc->mlx_maxiop) {
18781ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
18791ac4b82bSMike Smith 	sc->mlx_busycmds++;
18801ac4b82bSMike Smith     }
18811ac4b82bSMike Smith     splx(s);
18821ac4b82bSMike Smith 
18831ac4b82bSMike Smith     /* out of slots? */
18841ac4b82bSMike Smith     if (slot >= sc->mlx_maxiop)
18851ac4b82bSMike Smith 	return(EBUSY);
18861ac4b82bSMike Smith 
1887da8bb3a3SMike Smith     debug(2, "got slot %d", slot);
18881ac4b82bSMike Smith     mc->mc_slot = slot;
18891ac4b82bSMike Smith     return(0);
18901ac4b82bSMike Smith }
18911ac4b82bSMike Smith 
18921ac4b82bSMike Smith /********************************************************************************
18931ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
18941ac4b82bSMike Smith  */
18951ac4b82bSMike Smith static void
18961ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
18971ac4b82bSMike Smith {
18981ac4b82bSMike Smith     struct mlx_command	*mc = (struct mlx_command *)arg;
18991ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
19001ac4b82bSMike Smith     struct mlx_sgentry	*sg;
19011ac4b82bSMike Smith     int			i;
19021ac4b82bSMike Smith 
1903da8bb3a3SMike Smith     debug_called(1);
19041ac4b82bSMike Smith 
19051ac4b82bSMike Smith     /* get base address of s/g table */
1906da8bb3a3SMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * sc->mlx_sg_nseg);
19071ac4b82bSMike Smith 
19081ac4b82bSMike Smith     /* save s/g table information in command */
19091ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
1910da8bb3a3SMike Smith     mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * sc->mlx_sg_nseg * sizeof(struct mlx_sgentry));
19111ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
19121ac4b82bSMike Smith 
19131ac4b82bSMike Smith     /* populate s/g table */
19141ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
19151ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
19161ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
19171ac4b82bSMike Smith     }
19181ac4b82bSMike Smith }
19191ac4b82bSMike Smith 
19201ac4b82bSMike Smith static void
19211ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc)
19221ac4b82bSMike Smith {
19231ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
19241ac4b82bSMike Smith 
1925da8bb3a3SMike Smith     debug_called(1);
19261ac4b82bSMike Smith 
19271ac4b82bSMike Smith     /* if the command involves data at all */
19281ac4b82bSMike Smith     if (mc->mc_data != NULL) {
19291ac4b82bSMike Smith 
19301ac4b82bSMike Smith 	/* map the data buffer into bus space and build the s/g list */
19311ac4b82bSMike Smith 	bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length,
19321ac4b82bSMike Smith 			mlx_setup_dmamap, mc, 0);
19331ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
19341ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD);
19351ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
19361ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE);
19371ac4b82bSMike Smith     }
19381ac4b82bSMike Smith }
19391ac4b82bSMike Smith 
19401ac4b82bSMike Smith static void
19411ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
19421ac4b82bSMike Smith {
19431ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
19441ac4b82bSMike Smith 
1945da8bb3a3SMike Smith     debug_called(1);
19461ac4b82bSMike Smith 
19471ac4b82bSMike Smith     /* if the command involved data at all */
19481ac4b82bSMike Smith     if (mc->mc_data != NULL) {
19491ac4b82bSMike Smith 
19501ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
19511ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
19521ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
19531ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
19541ac4b82bSMike Smith 
19551ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
19561ac4b82bSMike Smith     }
19571ac4b82bSMike Smith }
19581ac4b82bSMike Smith 
19591ac4b82bSMike Smith /********************************************************************************
19605792b7feSMike Smith  * Try to deliver (mc) to the controller.
19611ac4b82bSMike Smith  *
19621ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
19631ac4b82bSMike Smith  */
19641ac4b82bSMike Smith static int
19651ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
19661ac4b82bSMike Smith {
19671ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
19685792b7feSMike Smith     int			i, s, done;
19691ac4b82bSMike Smith 
1970da8bb3a3SMike Smith     debug_called(1);
19711ac4b82bSMike Smith 
19721ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
19731ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
19741ac4b82bSMike Smith 
19754b006d7bSMike Smith     /* mark the command as currently being processed */
19761ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
19771ac4b82bSMike Smith 
19785792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
19795792b7feSMike Smith     mc->mc_timeout = time_second + 60;
19801ac4b82bSMike Smith 
19811ac4b82bSMike Smith     /* spin waiting for the mailbox */
19821ac4b82bSMike Smith     for (i = 100000, done = 0; (i > 0) && !done; i--) {
19831ac4b82bSMike Smith 	s = splbio();
19844b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
19854b006d7bSMike Smith 	    done = 1;
19864b006d7bSMike Smith 	    /* move command to work queue */
19874b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
19884b006d7bSMike Smith 	}
19895792b7feSMike Smith 	splx(s);	/* drop spl to allow completion interrupts */
19901ac4b82bSMike Smith     }
19911ac4b82bSMike Smith 
19921ac4b82bSMike Smith     /* command is enqueued */
19931ac4b82bSMike Smith     if (done)
19941ac4b82bSMike Smith 	return(0);
19951ac4b82bSMike Smith 
19961ac4b82bSMike Smith     /*
19971ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
19981ac4b82bSMike Smith      * that the command was given and return it with a bad status.
19991ac4b82bSMike Smith      */
20001ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
20011ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
20021ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
20035792b7feSMike Smith     mlx_complete(sc);
20041ac4b82bSMike Smith     return(EIO);
20051ac4b82bSMike Smith }
20061ac4b82bSMike Smith 
20071ac4b82bSMike Smith /********************************************************************************
20085792b7feSMike Smith  * Poll the controller (sc) for completed commands.
20095792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
20105792b7feSMike Smith  * new commands may be posted.
20111ac4b82bSMike Smith  *
20125792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
20131ac4b82bSMike Smith  */
20141ac4b82bSMike Smith static int
20151ac4b82bSMike Smith mlx_done(struct mlx_softc *sc)
20161ac4b82bSMike Smith {
20171ac4b82bSMike Smith     struct mlx_command	*mc;
20185792b7feSMike Smith     int			s, result;
20191ac4b82bSMike Smith     u_int8_t		slot;
20201ac4b82bSMike Smith     u_int16_t		status;
20211ac4b82bSMike Smith 
2022da8bb3a3SMike Smith     debug_called(2);
20231ac4b82bSMike Smith 
20245792b7feSMike Smith     result = 0;
20251ac4b82bSMike Smith 
20265792b7feSMike Smith     /* loop collecting completed commands */
20274b006d7bSMike Smith     s = splbio();
20285792b7feSMike Smith     for (;;) {
20295792b7feSMike Smith 	/* poll for a completed command's identifier and status */
20301ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
20315792b7feSMike Smith 	    result = 1;
20321ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
20331ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
20341ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
20351ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
20361ac4b82bSMike Smith 
20371ac4b82bSMike Smith 		    /* free slot for reuse */
20381ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
20391ac4b82bSMike Smith 		    sc->mlx_busycmds--;
20401ac4b82bSMike Smith 		} else {
20411ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
20421ac4b82bSMike Smith 		}
20431ac4b82bSMike Smith 	    } else {
20441ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
20451ac4b82bSMike Smith 	    }
20465792b7feSMike Smith 	} else {
20475792b7feSMike Smith 	    break;
20481ac4b82bSMike Smith 	}
20495792b7feSMike Smith     }
20501ac4b82bSMike Smith 
20515792b7feSMike Smith     /* if we've completed any commands, try posting some more */
20525792b7feSMike Smith     if (result)
20535792b7feSMike Smith 	mlx_startio(sc);
20545792b7feSMike Smith 
20555792b7feSMike Smith     /* handle completion and timeouts */
20565792b7feSMike Smith     mlx_complete(sc);
20575792b7feSMike Smith 
20585792b7feSMike Smith     return(result);
20591ac4b82bSMike Smith }
20601ac4b82bSMike Smith 
20611ac4b82bSMike Smith /********************************************************************************
20625792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
20631ac4b82bSMike Smith  */
20641ac4b82bSMike Smith static void
20651ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
20661ac4b82bSMike Smith {
20671ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
20681ac4b82bSMike Smith     int			s, count;
20691ac4b82bSMike Smith 
2070da8bb3a3SMike Smith     debug_called(2);
20711ac4b82bSMike Smith 
20725792b7feSMike Smith     /* avoid reentrancy  XXX might want to signal and request a restart */
20735792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
20745792b7feSMike Smith 	return;
20755792b7feSMike Smith 
20761ac4b82bSMike Smith     s = splbio();
20771ac4b82bSMike Smith     count = 0;
20781ac4b82bSMike Smith 
20795792b7feSMike Smith     /* scan the list of busy/done commands */
20804b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
20811ac4b82bSMike Smith     while (mc != NULL) {
20821ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
20831ac4b82bSMike Smith 
20845792b7feSMike Smith 	/* Command has been completed in some fashion */
20854b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
20864b006d7bSMike Smith 
20875792b7feSMike Smith 	    /* unmap the command's data buffer */
20885792b7feSMike Smith 	    mlx_unmapcmd(mc);
20891ac4b82bSMike Smith 	    /*
20901ac4b82bSMike Smith 	     * Does the command have a completion handler?
20911ac4b82bSMike Smith 	     */
20921ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
20931ac4b82bSMike Smith 		/* remove from list and give to handler */
20944b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
20951ac4b82bSMike Smith 		mc->mc_complete(mc);
20961ac4b82bSMike Smith 
20971ac4b82bSMike Smith 		/*
20981ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
20991ac4b82bSMike Smith 		 */
21001ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
21011ac4b82bSMike Smith 
21021ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
21034b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
21041ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
21051ac4b82bSMike Smith 
21061ac4b82bSMike Smith 		/*
21071ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
21081ac4b82bSMike Smith 		 */
21091ac4b82bSMike Smith 	    } else {
21101ac4b82bSMike Smith 	    }
21114b006d7bSMike Smith 	}
21121ac4b82bSMike Smith 	mc = nc;
21131ac4b82bSMike Smith     }
21141ac4b82bSMike Smith     splx(s);
21151ac4b82bSMike Smith 
21165792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_COMPLETING);
21171ac4b82bSMike Smith }
21181ac4b82bSMike Smith 
21191ac4b82bSMike Smith /********************************************************************************
21201ac4b82bSMike Smith  ********************************************************************************
21211ac4b82bSMike Smith                                                         Command Buffer Management
21221ac4b82bSMike Smith  ********************************************************************************
21231ac4b82bSMike Smith  ********************************************************************************/
21241ac4b82bSMike Smith 
21251ac4b82bSMike Smith /********************************************************************************
21261ac4b82bSMike Smith  * Get a new command buffer.
21271ac4b82bSMike Smith  *
21281ac4b82bSMike Smith  * This may return NULL in low-memory cases.
21291ac4b82bSMike Smith  *
21301ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
21311ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
21321ac4b82bSMike Smith  *
21331ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
21341ac4b82bSMike Smith  *
21351ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
21361ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
21371ac4b82bSMike Smith  *     using a buffer.
21381ac4b82bSMike Smith  */
21391ac4b82bSMike Smith static struct mlx_command *
21401ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
21411ac4b82bSMike Smith {
21421ac4b82bSMike Smith     struct mlx_command	*mc;
21431ac4b82bSMike Smith     int			error;
21441ac4b82bSMike Smith     int			s;
21451ac4b82bSMike Smith 
2146da8bb3a3SMike Smith     debug_called(1);
21471ac4b82bSMike Smith 
21481ac4b82bSMike Smith     s = splbio();
21491ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
21501ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
21511ac4b82bSMike Smith     splx(s);
21521ac4b82bSMike Smith 
21531ac4b82bSMike Smith     /* allocate a new command buffer? */
21541ac4b82bSMike Smith     if (mc == NULL) {
21551ac4b82bSMike Smith 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT);
21561ac4b82bSMike Smith 	if (mc != NULL) {
21571ac4b82bSMike Smith 	    bzero(mc, sizeof(*mc));
21581ac4b82bSMike Smith 	    mc->mc_sc = sc;
21591ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
21601ac4b82bSMike Smith 	    if (error) {
21611ac4b82bSMike Smith 		free(mc, M_DEVBUF);
21621ac4b82bSMike Smith 		return(NULL);
21631ac4b82bSMike Smith 	    }
21641ac4b82bSMike Smith 	}
21651ac4b82bSMike Smith     }
21661ac4b82bSMike Smith     return(mc);
21671ac4b82bSMike Smith }
21681ac4b82bSMike Smith 
21691ac4b82bSMike Smith /********************************************************************************
21701ac4b82bSMike Smith  * Release a command buffer for recycling.
21711ac4b82bSMike Smith  *
21721ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
21731ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
21741ac4b82bSMike Smith  */
21751ac4b82bSMike Smith static void
21761ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
21771ac4b82bSMike Smith {
21781ac4b82bSMike Smith     int		s;
21791ac4b82bSMike Smith 
2180da8bb3a3SMike Smith     debug_called(1);
21811ac4b82bSMike Smith 
21821ac4b82bSMike Smith     s = splbio();
21831ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
21841ac4b82bSMike Smith     splx(s);
21851ac4b82bSMike Smith }
21861ac4b82bSMike Smith 
21871ac4b82bSMike Smith /********************************************************************************
21881ac4b82bSMike Smith  * Permanently discard a command buffer.
21891ac4b82bSMike Smith  */
21901ac4b82bSMike Smith static void
21911ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
21921ac4b82bSMike Smith {
21931ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21941ac4b82bSMike Smith 
2195da8bb3a3SMike Smith     debug_called(1);
21961ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
21971ac4b82bSMike Smith     free(mc, M_DEVBUF);
21981ac4b82bSMike Smith }
21991ac4b82bSMike Smith 
22001ac4b82bSMike Smith 
22011ac4b82bSMike Smith /********************************************************************************
22021ac4b82bSMike Smith  ********************************************************************************
22031ac4b82bSMike Smith                                                 Type 3 interface accessor methods
22041ac4b82bSMike Smith  ********************************************************************************
22051ac4b82bSMike Smith  ********************************************************************************/
22061ac4b82bSMike Smith 
22071ac4b82bSMike Smith /********************************************************************************
22081ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
22091ac4b82bSMike Smith  * (the controller is not ready to take a command).
22101ac4b82bSMike Smith  *
22111ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
22121ac4b82bSMike Smith  */
22131ac4b82bSMike Smith static int
22141ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
22151ac4b82bSMike Smith {
22161ac4b82bSMike Smith     int		i;
22171ac4b82bSMike Smith 
2218da8bb3a3SMike Smith     debug_called(2);
22191ac4b82bSMike Smith 
22201ac4b82bSMike Smith     /* ready for our command? */
22211ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
22221ac4b82bSMike Smith 	/* copy mailbox data to window */
22231ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
22241ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
22251ac4b82bSMike Smith 
22261ac4b82bSMike Smith 	/* post command */
2227f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
22281ac4b82bSMike Smith 	return(1);
22291ac4b82bSMike Smith     }
22301ac4b82bSMike Smith     return(0);
22311ac4b82bSMike Smith }
22321ac4b82bSMike Smith 
22331ac4b82bSMike Smith /********************************************************************************
22341ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
22351ac4b82bSMike Smith  * and recover the slot number and status code.
22361ac4b82bSMike Smith  *
22371ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
22381ac4b82bSMike Smith  */
22391ac4b82bSMike Smith static int
22401ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
22411ac4b82bSMike Smith {
22421ac4b82bSMike Smith 
2243da8bb3a3SMike Smith     debug_called(2);
22441ac4b82bSMike Smith 
22451ac4b82bSMike Smith     /* status available? */
22461ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
22471ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
22481ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
22491ac4b82bSMike Smith 
22501ac4b82bSMike Smith 	/* acknowledge completion */
2251f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2252f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
22531ac4b82bSMike Smith 	return(1);
22541ac4b82bSMike Smith     }
22551ac4b82bSMike Smith     return(0);
22561ac4b82bSMike Smith }
22571ac4b82bSMike Smith 
22581ac4b82bSMike Smith /********************************************************************************
22591ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
22601ac4b82bSMike Smith  *
22611ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
22621ac4b82bSMike Smith  */
22631ac4b82bSMike Smith static void
22641ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
22651ac4b82bSMike Smith {
2266da8bb3a3SMike Smith     debug_called(1);
22671ac4b82bSMike Smith 
22681ac4b82bSMike Smith     switch(action) {
22691ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
22701ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
22711ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
22721ac4b82bSMike Smith 	break;
22731ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
22741ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
22751ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
22761ac4b82bSMike Smith 	break;
22771ac4b82bSMike Smith     }
22781ac4b82bSMike Smith }
22791ac4b82bSMike Smith 
2280da8bb3a3SMike Smith /********************************************************************************
2281da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2282da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2283da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2284da8bb3a3SMike Smith  */
2285da8bb3a3SMike Smith static int
2286da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2287da8bb3a3SMike Smith {
2288da8bb3a3SMike Smith     u_int8_t	fwerror;
2289da8bb3a3SMike Smith     static int	initted = 0;
2290da8bb3a3SMike Smith 
2291da8bb3a3SMike Smith     debug_called(2);
2292da8bb3a3SMike Smith 
2293da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2294da8bb3a3SMike Smith     if (!initted) {
2295da8bb3a3SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
2296da8bb3a3SMike Smith 	DELAY(1000);
2297da8bb3a3SMike Smith 	initted = 1;
2298da8bb3a3SMike Smith     }
2299da8bb3a3SMike Smith 
2300da8bb3a3SMike Smith     /* init in progress? */
2301da8bb3a3SMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
2302da8bb3a3SMike Smith 	return(0);
2303da8bb3a3SMike Smith 
2304da8bb3a3SMike Smith     /* test error value */
2305da8bb3a3SMike Smith     fwerror = MLX_V3_GET_FWERROR(sc);
2306da8bb3a3SMike Smith     if (!(fwerror & MLX_V3_FWERROR_PEND))
2307da8bb3a3SMike Smith 	return(1);
2308da8bb3a3SMike Smith 
2309da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2310da8bb3a3SMike Smith     *error = fwerror & ~MLX_V3_FWERROR_PEND;
2311da8bb3a3SMike Smith     *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
2312da8bb3a3SMike Smith     *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
2313da8bb3a3SMike Smith 
2314da8bb3a3SMike Smith     /* acknowledge */
2315da8bb3a3SMike Smith     MLX_V3_PUT_FWERROR(sc, 0);
2316da8bb3a3SMike Smith 
2317da8bb3a3SMike Smith     return(2);
2318da8bb3a3SMike Smith }
23191ac4b82bSMike Smith 
23201ac4b82bSMike Smith /********************************************************************************
23211ac4b82bSMike Smith  ********************************************************************************
2322f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2323f6b84b08SMike Smith  ********************************************************************************
2324f6b84b08SMike Smith  ********************************************************************************/
2325f6b84b08SMike Smith 
2326f6b84b08SMike Smith /********************************************************************************
2327f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2328f6b84b08SMike Smith  * (the controller is not ready to take a command).
2329f6b84b08SMike Smith  *
2330f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2331f6b84b08SMike Smith  */
2332f6b84b08SMike Smith static int
2333f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2334f6b84b08SMike Smith {
2335f6b84b08SMike Smith     int		i;
2336f6b84b08SMike Smith 
2337da8bb3a3SMike Smith     debug_called(2);
2338f6b84b08SMike Smith 
2339f6b84b08SMike Smith     /* ready for our command? */
2340f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2341f6b84b08SMike Smith 	/* copy mailbox data to window */
2342f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2343f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2344f6b84b08SMike Smith 
2345da8bb3a3SMike Smith 	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
2346da8bb3a3SMike Smith 	bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
2347da8bb3a3SMike Smith 			  BUS_SPACE_BARRIER_WRITE);
2348da8bb3a3SMike Smith 
2349f6b84b08SMike Smith 	/* post command */
2350f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2351f6b84b08SMike Smith 	return(1);
2352f6b84b08SMike Smith     }
2353f6b84b08SMike Smith     return(0);
2354f6b84b08SMike Smith }
2355f6b84b08SMike Smith 
2356f6b84b08SMike Smith /********************************************************************************
2357f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2358f6b84b08SMike Smith  * and recover the slot number and status code.
2359f6b84b08SMike Smith  *
2360f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2361f6b84b08SMike Smith  */
2362f6b84b08SMike Smith static int
2363f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2364f6b84b08SMike Smith {
2365f6b84b08SMike Smith 
2366da8bb3a3SMike Smith     debug_called(2);
2367f6b84b08SMike Smith 
2368f6b84b08SMike Smith     /* status available? */
2369f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2370f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2371f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2372f6b84b08SMike Smith 
2373f6b84b08SMike Smith 	/* acknowledge completion */
2374f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2375f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2376f6b84b08SMike Smith 	return(1);
2377f6b84b08SMike Smith     }
2378f6b84b08SMike Smith     return(0);
2379f6b84b08SMike Smith }
2380f6b84b08SMike Smith 
2381f6b84b08SMike Smith /********************************************************************************
2382f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2383f6b84b08SMike Smith  *
2384f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2385f6b84b08SMike Smith  */
2386f6b84b08SMike Smith static void
2387f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2388f6b84b08SMike Smith {
2389da8bb3a3SMike Smith     debug_called(1);
2390f6b84b08SMike Smith 
2391f6b84b08SMike Smith     switch(action) {
2392f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2393f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2394f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2395f6b84b08SMike Smith 	break;
2396f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2397f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2398f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2399f6b84b08SMike Smith 	break;
2400f6b84b08SMike Smith     }
2401f6b84b08SMike Smith }
2402f6b84b08SMike Smith 
2403da8bb3a3SMike Smith /********************************************************************************
2404da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2405da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2406da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2407da8bb3a3SMike Smith  */
2408da8bb3a3SMike Smith static int
2409da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2410da8bb3a3SMike Smith {
2411da8bb3a3SMike Smith     u_int8_t	fwerror;
2412da8bb3a3SMike Smith     static int	initted = 0;
2413da8bb3a3SMike Smith 
2414da8bb3a3SMike Smith     debug_called(2);
2415da8bb3a3SMike Smith 
2416da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2417da8bb3a3SMike Smith     if (!initted) {
2418da8bb3a3SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2419da8bb3a3SMike Smith 	DELAY(1000);
2420da8bb3a3SMike Smith 	initted = 1;
2421da8bb3a3SMike Smith     }
2422da8bb3a3SMike Smith 
2423da8bb3a3SMike Smith     /* init in progress? */
2424da8bb3a3SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
2425da8bb3a3SMike Smith 	return(0);
2426da8bb3a3SMike Smith 
2427da8bb3a3SMike Smith     /* test error value */
2428da8bb3a3SMike Smith     fwerror = MLX_V4_GET_FWERROR(sc);
2429da8bb3a3SMike Smith     if (!(fwerror & MLX_V4_FWERROR_PEND))
2430da8bb3a3SMike Smith 	return(1);
2431da8bb3a3SMike Smith 
2432da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2433da8bb3a3SMike Smith     *error = fwerror & ~MLX_V4_FWERROR_PEND;
2434da8bb3a3SMike Smith     *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
2435da8bb3a3SMike Smith     *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
2436da8bb3a3SMike Smith 
2437da8bb3a3SMike Smith     /* acknowledge */
2438da8bb3a3SMike Smith     MLX_V4_PUT_FWERROR(sc, 0);
2439da8bb3a3SMike Smith 
2440da8bb3a3SMike Smith     return(2);
2441da8bb3a3SMike Smith }
2442f6b84b08SMike Smith 
2443f6b84b08SMike Smith /********************************************************************************
2444f6b84b08SMike Smith  ********************************************************************************
24455792b7feSMike Smith                                                 Type 5 interface accessor methods
24465792b7feSMike Smith  ********************************************************************************
24475792b7feSMike Smith  ********************************************************************************/
24485792b7feSMike Smith 
24495792b7feSMike Smith /********************************************************************************
24505792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
24515792b7feSMike Smith  * (the controller is not ready to take a command).
24525792b7feSMike Smith  *
24535792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
24545792b7feSMike Smith  */
24555792b7feSMike Smith static int
24565792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
24575792b7feSMike Smith {
24585792b7feSMike Smith     int		i;
24595792b7feSMike Smith 
2460da8bb3a3SMike Smith     debug_called(2);
24615792b7feSMike Smith 
24625792b7feSMike Smith     /* ready for our command? */
24635792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
24645792b7feSMike Smith 	/* copy mailbox data to window */
24655792b7feSMike Smith 	for (i = 0; i < 13; i++)
24665792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
24675792b7feSMike Smith 
24685792b7feSMike Smith 	/* post command */
24695792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
24705792b7feSMike Smith 	return(1);
24715792b7feSMike Smith     }
24725792b7feSMike Smith     return(0);
24735792b7feSMike Smith }
24745792b7feSMike Smith 
24755792b7feSMike Smith /********************************************************************************
24765792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
24775792b7feSMike Smith  * and recover the slot number and status code.
24785792b7feSMike Smith  *
24795792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
24805792b7feSMike Smith  */
24815792b7feSMike Smith static int
24825792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
24835792b7feSMike Smith {
24845792b7feSMike Smith 
2485da8bb3a3SMike Smith     debug_called(2);
24865792b7feSMike Smith 
24875792b7feSMike Smith     /* status available? */
24885792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
24895792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
24905792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
24915792b7feSMike Smith 
24925792b7feSMike Smith 	/* acknowledge completion */
24935792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
24945792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
24955792b7feSMike Smith 	return(1);
24965792b7feSMike Smith     }
24975792b7feSMike Smith     return(0);
24985792b7feSMike Smith }
24995792b7feSMike Smith 
25005792b7feSMike Smith /********************************************************************************
25015792b7feSMike Smith  * Enable/disable interrupts as requested.
25025792b7feSMike Smith  *
25035792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
25045792b7feSMike Smith  */
25055792b7feSMike Smith static void
25065792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
25075792b7feSMike Smith {
2508da8bb3a3SMike Smith     debug_called(1);
25095792b7feSMike Smith 
25105792b7feSMike Smith     switch(action) {
25115792b7feSMike Smith     case MLX_INTACTION_DISABLE:
2512da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
25135792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
25145792b7feSMike Smith 	break;
25155792b7feSMike Smith     case MLX_INTACTION_ENABLE:
2516da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
25175792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
25185792b7feSMike Smith 	break;
25195792b7feSMike Smith     }
25205792b7feSMike Smith }
25215792b7feSMike Smith 
2522da8bb3a3SMike Smith /********************************************************************************
2523da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2524da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2525da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2526da8bb3a3SMike Smith  */
2527da8bb3a3SMike Smith static int
2528da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2529da8bb3a3SMike Smith {
2530da8bb3a3SMike Smith     u_int8_t	fwerror;
2531da8bb3a3SMike Smith     static int	initted = 0;
2532da8bb3a3SMike Smith 
2533da8bb3a3SMike Smith     debug_called(2);
2534da8bb3a3SMike Smith 
2535da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2536da8bb3a3SMike Smith     if (!initted) {
2537da8bb3a3SMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
2538da8bb3a3SMike Smith 	DELAY(1000);
2539da8bb3a3SMike Smith 	initted = 1;
2540da8bb3a3SMike Smith     }
2541da8bb3a3SMike Smith 
2542da8bb3a3SMike Smith     /* init in progress? */
2543da8bb3a3SMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
2544da8bb3a3SMike Smith 	return(0);
2545da8bb3a3SMike Smith 
2546da8bb3a3SMike Smith     /* test for error value */
2547da8bb3a3SMike Smith     fwerror = MLX_V5_GET_FWERROR(sc);
2548da8bb3a3SMike Smith     if (!(fwerror & MLX_V5_FWERROR_PEND))
2549da8bb3a3SMike Smith 	return(1);
2550da8bb3a3SMike Smith 
2551da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2552da8bb3a3SMike Smith     *error = fwerror & ~MLX_V5_FWERROR_PEND;
2553da8bb3a3SMike Smith     *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
2554da8bb3a3SMike Smith     *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
2555da8bb3a3SMike Smith 
2556da8bb3a3SMike Smith     /* acknowledge */
2557da8bb3a3SMike Smith     MLX_V5_PUT_FWERROR(sc, 0xff);
2558da8bb3a3SMike Smith 
2559da8bb3a3SMike Smith     return(2);
2560da8bb3a3SMike Smith }
25615792b7feSMike Smith 
25625792b7feSMike Smith /********************************************************************************
25635792b7feSMike Smith  ********************************************************************************
25641ac4b82bSMike Smith                                                                         Debugging
25651ac4b82bSMike Smith  ********************************************************************************
25661ac4b82bSMike Smith  ********************************************************************************/
25671ac4b82bSMike Smith 
25681ac4b82bSMike Smith /********************************************************************************
25691ac4b82bSMike Smith  * Return a status message describing (mc)
25701ac4b82bSMike Smith  */
25711ac4b82bSMike Smith static char *mlx_status_messages[] = {
25721ac4b82bSMike Smith     "normal completion",			/* 00 */
25731ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
25741ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
25751ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
25761ac4b82bSMike Smith     "bad data encountered",			/* 04 */
25771ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
25781ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
25791ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
25801ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
25811ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
25821ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
25831ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
25841ac4b82bSMike Smith     "channel is busy",				/* 12 */
25851ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
2586da8bb3a3SMike Smith     "rebuild successfully terminated",		/* 14 */
2587da8bb3a3SMike Smith     "unsupported command",			/* 15 */
2588da8bb3a3SMike Smith     "check condition received",			/* 16 */
2589da8bb3a3SMike Smith     "device is busy",				/* 17 */
2590da8bb3a3SMike Smith     "selection or command timeout",		/* 18 */
2591da8bb3a3SMike Smith     "command terminated abnormally",		/* 19 */
2592da8bb3a3SMike Smith     ""
25931ac4b82bSMike Smith };
25941ac4b82bSMike Smith 
25951ac4b82bSMike Smith static struct
25961ac4b82bSMike Smith {
25971ac4b82bSMike Smith     int		command;
25981ac4b82bSMike Smith     u_int16_t	status;
25991ac4b82bSMike Smith     int		msg;
26001ac4b82bSMike Smith } mlx_messages[] = {
2601da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0001,	 1},
2602da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0002,	 1},
2603da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0105,	 3},
2604da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x010c,	 4},
2605da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0001,	 1},
2606da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0002,	 1},
2607da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0105,	 3},
2608da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0001,	 1},
2609da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0002,	 1},
2610da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0105,	 3},
2611da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
2612da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
2613da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
26141ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
26151ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
26161ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
26171ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
26181ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
2619da8bb3a3SMike Smith     {MLX_CMD_REBUILDASYNC,	0x0107, 14},
26201ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
26211ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
26221ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
26231ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
26241ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
26251ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
26261ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
2627da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0002, 16},
2628da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0008, 17},
2629da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000e, 18},
2630da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000f, 19},
2631da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0105,  8},
2632da8bb3a3SMike Smith 
2633da8bb3a3SMike Smith     {0,				0x0104, 14},
26341ac4b82bSMike Smith     {-1, 0, 0}
26351ac4b82bSMike Smith };
26361ac4b82bSMike Smith 
26371ac4b82bSMike Smith static char *
26381ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
26391ac4b82bSMike Smith {
26401ac4b82bSMike Smith     static char	unkmsg[80];
26411ac4b82bSMike Smith     int		i;
26421ac4b82bSMike Smith 
26431ac4b82bSMike Smith     /* look up message in table */
26441ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
2645da8bb3a3SMike Smith 	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
2646466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
26471ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
26481ac4b82bSMike Smith 
26491ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
26501ac4b82bSMike Smith     return(unkmsg);
26511ac4b82bSMike Smith }
26521ac4b82bSMike Smith 
26531ac4b82bSMike Smith /*******************************************************************************
2654da8bb3a3SMike Smith  * Print a string describing the controller (sc)
26551ac4b82bSMike Smith  */
26565792b7feSMike Smith static struct
26575792b7feSMike Smith {
26585792b7feSMike Smith     int		hwid;
26595792b7feSMike Smith     char	*name;
26605792b7feSMike Smith } mlx_controller_names[] = {
26615792b7feSMike Smith     {0x01,	"960P/PD"},
26625792b7feSMike Smith     {0x02,	"960PL"},
26635792b7feSMike Smith     {0x10,	"960PG"},
26645792b7feSMike Smith     {0x11,	"960PJ"},
26659eee27f1SMike Smith     {0x12,	"960PR"},
26669eee27f1SMike Smith     {0x13,	"960PT"},
26679eee27f1SMike Smith     {0x14,	"960PTL0"},
26689eee27f1SMike Smith     {0x15,	"960PRL"},
26699eee27f1SMike Smith     {0x16,	"960PTL1"},
26709eee27f1SMike Smith     {0x20,	"1164PVX"},
26715792b7feSMike Smith     {-1, NULL}
26725792b7feSMike Smith };
26735792b7feSMike Smith 
26749eee27f1SMike Smith static void
26759eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
26761ac4b82bSMike Smith {
26771ac4b82bSMike Smith     static char		buf[80];
26785792b7feSMike Smith     char		*model;
26799eee27f1SMike Smith     int			i;
26801ac4b82bSMike Smith 
26815792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
26829eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
26835792b7feSMike Smith 	    model = mlx_controller_names[i].name;
26841ac4b82bSMike Smith 	    break;
26851ac4b82bSMike Smith 	}
26865792b7feSMike Smith     }
26875792b7feSMike Smith     if (model == NULL) {
26889eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
26895792b7feSMike Smith 	model = buf;
26905792b7feSMike Smith     }
2691da8bb3a3SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
26929eee27f1SMike Smith 		  model,
26939eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
26949eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
26959eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
26969eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
26979eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2698b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
26999eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
27009eee27f1SMike Smith 
27019eee27f1SMike Smith     if (bootverbose) {
27029eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
27039eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
27049eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
27059eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
27069eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
27079eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
27089eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
27099eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
27109eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
27119eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
27129eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
27139eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
27149eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
27159eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
27169eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
27179eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
27189eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
27199eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
27209eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
27219eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
27229eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
27239eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
27249eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
27259eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
27269eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
27279eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
27289eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
27299eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
27309eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
27319eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
27329eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
27339eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
27349eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
27359eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
27369eee27f1SMike Smith 
27379eee27f1SMike Smith     }
27381ac4b82bSMike Smith }
27391ac4b82bSMike Smith 
2740da8bb3a3SMike Smith /*******************************************************************************
2741da8bb3a3SMike Smith  * Emit a string describing the firmware handshake status code, and return a flag
2742da8bb3a3SMike Smith  * indicating whether the code represents a fatal error.
2743da8bb3a3SMike Smith  *
2744da8bb3a3SMike Smith  * Error code interpretations are from the Linux driver, and don't directly match
2745da8bb3a3SMike Smith  * the messages printed by Mylex's BIOS.  This may change if documentation on the
2746da8bb3a3SMike Smith  * codes is forthcoming.
2747da8bb3a3SMike Smith  */
2748da8bb3a3SMike Smith static int
2749da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
2750da8bb3a3SMike Smith {
2751da8bb3a3SMike Smith     switch(error) {
2752da8bb3a3SMike Smith     case 0x00:
2753da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
2754da8bb3a3SMike Smith 	break;
2755da8bb3a3SMike Smith     case 0x08:
2756da8bb3a3SMike Smith 	/* we could be neater about this and give some indication when we receive more of them */
2757da8bb3a3SMike Smith 	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
2758da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "spinning up drives...\n");
2759da8bb3a3SMike Smith 	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
2760da8bb3a3SMike Smith 	}
2761da8bb3a3SMike Smith 	break;
2762da8bb3a3SMike Smith     case 0x30:
2763da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "configuration checksum error\n");
2764da8bb3a3SMike Smith 	break;
2765da8bb3a3SMike Smith     case 0x60:
2766da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
2767da8bb3a3SMike Smith 	break;
2768da8bb3a3SMike Smith     case 0x70:
2769da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
2770da8bb3a3SMike Smith 	break;
2771da8bb3a3SMike Smith     case 0x90:
2772da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
2773da8bb3a3SMike Smith 	break;
2774da8bb3a3SMike Smith     case 0xa0:
2775da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
2776da8bb3a3SMike Smith 	break;
2777da8bb3a3SMike Smith     case 0xb0:
2778da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
2779da8bb3a3SMike Smith 	break;
2780da8bb3a3SMike Smith     case 0xd0:
2781da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "new controller configuration found\n");
2782da8bb3a3SMike Smith 	break;
2783da8bb3a3SMike Smith     case 0xf0:
2784da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
2785da8bb3a3SMike Smith 	return(1);
2786da8bb3a3SMike Smith     default:
2787da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
2788da8bb3a3SMike Smith 	break;
2789da8bb3a3SMike Smith     }
2790da8bb3a3SMike Smith     return(0);
2791da8bb3a3SMike Smith }
2792da8bb3a3SMike Smith 
27931ac4b82bSMike Smith /********************************************************************************
27941ac4b82bSMike Smith  ********************************************************************************
27951ac4b82bSMike Smith                                                                 Utility Functions
27961ac4b82bSMike Smith  ********************************************************************************
27971ac4b82bSMike Smith  ********************************************************************************/
27981ac4b82bSMike Smith 
27991ac4b82bSMike Smith /********************************************************************************
28001ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
28011ac4b82bSMike Smith  */
28021ac4b82bSMike Smith static struct mlx_sysdrive *
28031ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
28041ac4b82bSMike Smith {
28051ac4b82bSMike Smith     int		i;
28061ac4b82bSMike Smith 
28071ac4b82bSMike Smith     /* search system drives */
28081ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
28091ac4b82bSMike Smith 	/* is this one attached? */
28101ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
28111ac4b82bSMike Smith 	    /* is this the one? */
28121ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
28131ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
28141ac4b82bSMike Smith 	}
28151ac4b82bSMike Smith     }
28161ac4b82bSMike Smith     return(NULL);
28171ac4b82bSMike Smith }
2818