xref: /freebsd/sys/dev/mlx/mlx.c (revision 5d278f5cf339e8f66b1b29244c4321e1daa590e7)
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);
114421f2f7dSMike Smith static int			mlx_check(struct mlx_softc *sc, int drive);
1151ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1161ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1171ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1181ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1191ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1201ac4b82bSMike Smith static int			mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu);
1211ac4b82bSMike Smith 
1221ac4b82bSMike Smith /*
1231ac4b82bSMike Smith  * Command buffer allocation.
1241ac4b82bSMike Smith  */
1251ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1261ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1271ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1281ac4b82bSMike Smith 
1291ac4b82bSMike Smith /*
1301ac4b82bSMike Smith  * Command management.
1311ac4b82bSMike Smith  */
1321ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1331ac4b82bSMike Smith static void			mlx_mapcmd(struct mlx_command *mc);
1341ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1351ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1361ac4b82bSMike Smith static int			mlx_done(struct mlx_softc *sc);
1371ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1381ac4b82bSMike Smith 
1391ac4b82bSMike Smith /*
1401ac4b82bSMike Smith  * Debugging.
1411ac4b82bSMike Smith  */
1421ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1439eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
144da8bb3a3SMike Smith static int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
1451ac4b82bSMike Smith 
1461ac4b82bSMike Smith /*
1471ac4b82bSMike Smith  * Utility functions.
1481ac4b82bSMike Smith  */
1491ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1501ac4b82bSMike Smith 
1511ac4b82bSMike Smith /********************************************************************************
1521ac4b82bSMike Smith  ********************************************************************************
1531ac4b82bSMike Smith                                                                 Public Interfaces
1541ac4b82bSMike Smith  ********************************************************************************
1551ac4b82bSMike Smith  ********************************************************************************/
1561ac4b82bSMike Smith 
1571ac4b82bSMike Smith /********************************************************************************
1581ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1591ac4b82bSMike Smith  *
1601ac4b82bSMike Smith  * Should not be called if the controller is active.
1611ac4b82bSMike Smith  */
1621ac4b82bSMike Smith void
1631ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1641ac4b82bSMike Smith {
1651ac4b82bSMike Smith     struct mlx_command	*mc;
1661ac4b82bSMike Smith 
167da8bb3a3SMike Smith     debug_called(1);
1681ac4b82bSMike Smith 
1691ac4b82bSMike Smith     /* cancel status timeout */
1701ac4b82bSMike Smith     untimeout(mlx_periodic, sc, sc->mlx_timeout);
1711ac4b82bSMike Smith 
1721ac4b82bSMike Smith     /* throw away any command buffers */
1731ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1741ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1751ac4b82bSMike Smith 	mlx_freecmd(mc);
1761ac4b82bSMike Smith     }
1771ac4b82bSMike Smith 
1781ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1791ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1801ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1811ac4b82bSMike Smith 
1821ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
1831ac4b82bSMike Smith     if (sc->mlx_sgtable)
1841ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1851ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1861ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1871ac4b82bSMike Smith 
1881ac4b82bSMike Smith     /* disconnect the interrupt handler */
1891ac4b82bSMike Smith     if (sc->mlx_intr)
1901ac4b82bSMike Smith 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1911ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
1921ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
1931ac4b82bSMike Smith 
1941ac4b82bSMike Smith     /* destroy the parent DMA tag */
1951ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
1961ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
1971ac4b82bSMike Smith 
1981ac4b82bSMike Smith     /* release the register window mapping */
1991ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
2001ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY,
2011ac4b82bSMike Smith 			     (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem);
2029eee27f1SMike Smith 
2039eee27f1SMike Smith     /* free controller enquiry data */
2049eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
2059eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
206da8bb3a3SMike Smith 
207da8bb3a3SMike Smith     /* destroy control device */
208da8bb3a3SMike Smith     if (sc->mlx_dev_t != (dev_t)NULL)
209da8bb3a3SMike Smith 	destroy_dev(sc->mlx_dev_t);
2101ac4b82bSMike Smith }
2111ac4b82bSMike Smith 
2121ac4b82bSMike Smith /********************************************************************************
2131ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2141ac4b82bSMike Smith  */
2151ac4b82bSMike Smith static void
2161ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2171ac4b82bSMike Smith {
2181ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2191ac4b82bSMike Smith 
220da8bb3a3SMike Smith     debug_called(1);
2211ac4b82bSMike Smith 
2221ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2231ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2241ac4b82bSMike Smith }
2251ac4b82bSMike Smith 
2261ac4b82bSMike Smith static int
2271ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2281ac4b82bSMike Smith {
2291ac4b82bSMike Smith     size_t	segsize;
2301ac4b82bSMike Smith     int		error;
2311ac4b82bSMike Smith 
232da8bb3a3SMike Smith     debug_called(1);
2331ac4b82bSMike Smith 
2341ac4b82bSMike Smith     /* destroy any existing mappings */
2351ac4b82bSMike Smith     if (sc->mlx_sgtable)
2361ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2371ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2381ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2391ac4b82bSMike Smith 
2401ac4b82bSMike Smith     /*
2411ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
2421ac4b82bSMike Smith      * the s/g lists we will need.
2431ac4b82bSMike Smith      */
244da8bb3a3SMike Smith     segsize = sizeof(struct mlx_sgentry) * sc->mlx_sg_nseg * sc->mlx_maxiop;
2451ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2461ac4b82bSMike Smith 			       1, 0, 			/* alignment, boundary */
2471ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2481ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2491ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2501ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2511ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2521ac4b82bSMike Smith 			       0,			/* flags */
2531ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2541ac4b82bSMike Smith     if (error != 0) {
2551ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2561ac4b82bSMike Smith 	return(ENOMEM);
2571ac4b82bSMike Smith     }
2581ac4b82bSMike Smith 
2591ac4b82bSMike Smith     /*
2601ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2611ac4b82bSMike Smith      * controller-visible space.
2621ac4b82bSMike Smith      *
2631ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
2641ac4b82bSMike Smith      * contiguous slab.  We may need to switch to a more complex arrangement where
2651ac4b82bSMike Smith      * we allocate in smaller chunks and keep a lookup table from slot to bus address.
2661ac4b82bSMike Smith      */
2671ac4b82bSMike Smith     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2681ac4b82bSMike Smith     if (error) {
2691ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2701ac4b82bSMike Smith 	return(ENOMEM);
2711ac4b82bSMike Smith     }
2721ac4b82bSMike Smith     bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0);
2731ac4b82bSMike Smith     return(0);
2741ac4b82bSMike Smith }
2751ac4b82bSMike Smith 
2761ac4b82bSMike Smith /********************************************************************************
2771ac4b82bSMike Smith  * Initialise the controller and softc
2781ac4b82bSMike Smith  */
2791ac4b82bSMike Smith int
2801ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
2811ac4b82bSMike Smith {
282da8bb3a3SMike Smith     struct mlx_enquiry_old	*meo;
283da8bb3a3SMike Smith     int				rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
2841ac4b82bSMike Smith 
285da8bb3a3SMike Smith     debug_called(1);
2861ac4b82bSMike Smith 
2871ac4b82bSMike Smith     /*
2881ac4b82bSMike Smith      * Initialise per-controller queues.
2891ac4b82bSMike Smith      */
2904b006d7bSMike Smith     TAILQ_INIT(&sc->mlx_work);
2911ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
2928177437dSPoul-Henning Kamp     bioq_init(&sc->mlx_bioq);
2931ac4b82bSMike Smith 
2941ac4b82bSMike Smith     /*
2951ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
2961ac4b82bSMike Smith      */
2971ac4b82bSMike Smith     switch(sc->mlx_iftype) {
298da8bb3a3SMike Smith     case MLX_IFTYPE_2:
2991ac4b82bSMike Smith     case MLX_IFTYPE_3:
3001ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
3011ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
3021ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
303da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v3_fw_handshake;
304da8bb3a3SMike Smith 	sc->mlx_sg_nseg		= MLX_NSEG_OLD;
3051ac4b82bSMike Smith 	break;
306f6b84b08SMike Smith     case MLX_IFTYPE_4:
307f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
308f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
309f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
310da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
311da8bb3a3SMike Smith 	sc->mlx_sg_nseg		= MLX_NSEG_NEW;
312f6b84b08SMike Smith 	break;
3135792b7feSMike Smith     case MLX_IFTYPE_5:
3145792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3155792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3165792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
317da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
318da8bb3a3SMike Smith 	sc->mlx_sg_nseg		= MLX_NSEG_NEW;
3195792b7feSMike Smith 	break;
3201ac4b82bSMike Smith     default:
3215d278f5cSMike Smith 	mlx_free(sc);
3221ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3231ac4b82bSMike Smith     }
3241ac4b82bSMike Smith 
3251ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3261ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3271ac4b82bSMike Smith 
3281ac4b82bSMike Smith     /*
329da8bb3a3SMike Smith      * Wait for the controller to come ready, handshake with the firmware if required.
330da8bb3a3SMike Smith      * This is typically only necessary on platforms where the controller BIOS does not
331da8bb3a3SMike Smith      * run.
332da8bb3a3SMike Smith      */
333da8bb3a3SMike Smith     hsmsg = 0;
334da8bb3a3SMike Smith     DELAY(1000);
335da8bb3a3SMike Smith     while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) {
336da8bb3a3SMike Smith 	/* report first time around... */
337da8bb3a3SMike Smith 	if (hsmsg == 0) {
338da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
339da8bb3a3SMike Smith 	    hsmsg = 1;
340da8bb3a3SMike Smith 	}
341da8bb3a3SMike Smith 	/* did we get a real message? */
342da8bb3a3SMike Smith 	if (hscode == 2) {
343da8bb3a3SMike Smith 	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
344da8bb3a3SMike Smith 	    /* fatal initialisation error? */
345da8bb3a3SMike Smith 	    if (hscode != 0) {
346da8bb3a3SMike Smith 		mlx_free(sc);
347da8bb3a3SMike Smith 		return(ENXIO);
348da8bb3a3SMike Smith 	    }
349da8bb3a3SMike Smith 	}
350da8bb3a3SMike Smith     }
351da8bb3a3SMike Smith     if (hsmsg == 1)
352da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "initialisation complete.\n");
353da8bb3a3SMike Smith 
354da8bb3a3SMike Smith     /*
3551ac4b82bSMike Smith      * Allocate and connect our interrupt.
3561ac4b82bSMike Smith      */
3571ac4b82bSMike Smith     rid = 0;
3581ac4b82bSMike Smith     sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
3591ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
360421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't allocate interrupt\n");
3611ac4b82bSMike Smith 	mlx_free(sc);
3621ac4b82bSMike Smith 	return(ENXIO);
3631ac4b82bSMike Smith     }
3641ac4b82bSMike Smith     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO,  mlx_intr, sc, &sc->mlx_intr);
3651ac4b82bSMike Smith     if (error) {
366421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't set up interrupt\n");
3671ac4b82bSMike Smith 	mlx_free(sc);
3681ac4b82bSMike Smith 	return(ENXIO);
3691ac4b82bSMike Smith     }
3701ac4b82bSMike Smith 
3711ac4b82bSMike Smith     /*
3721ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3731ac4b82bSMike Smith      */
3741ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 		/* parent */
3751ac4b82bSMike Smith 			       1, 0, 				/* alignment, boundary */
3761ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,		/* lowaddr */
3771ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 		/* highaddr */
3781ac4b82bSMike Smith 			       NULL, NULL, 			/* filter, filterarg */
379da8bb3a3SMike Smith 			       MAXBSIZE, sc->mlx_sg_nseg,	/* maxsize, nsegments */
3801ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,		/* maxsegsize */
3811ac4b82bSMike Smith 			       0,				/* flags */
3821ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
3831ac4b82bSMike Smith     if (error != 0) {
3841ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
3855d278f5cSMike Smith 	mlx_free(sc);
3861ac4b82bSMike Smith 	return(ENOMEM);
3871ac4b82bSMike Smith     }
3881ac4b82bSMike Smith 
3891ac4b82bSMike Smith     /*
3901ac4b82bSMike Smith      * Create an initial set of s/g mappings.
3911ac4b82bSMike Smith      */
3929eee27f1SMike Smith     sc->mlx_maxiop = 8;
3931ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3941ac4b82bSMike Smith     if (error != 0) {
395421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
3965d278f5cSMike Smith 	mlx_free(sc);
3971ac4b82bSMike Smith 	return(error);
3981ac4b82bSMike Smith     }
3991ac4b82bSMike Smith 
4001ac4b82bSMike Smith     /* send an ENQUIRY2 to the controller */
4019eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
4021ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
4035d278f5cSMike Smith 	mlx_free(sc);
4041ac4b82bSMike Smith 	return(ENXIO);
4051ac4b82bSMike Smith     }
4061ac4b82bSMike Smith 
4079eee27f1SMike Smith     /*
4089eee27f1SMike Smith      * We don't (yet) know where the event log is up to.
4099eee27f1SMike Smith      */
410421f2f7dSMike Smith     sc->mlx_currevent = -1;
4111ac4b82bSMike Smith 
4121ac4b82bSMike Smith     /*
4131ac4b82bSMike Smith      * Do quirk/feature related things.
4141ac4b82bSMike Smith      */
4159eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
4161ac4b82bSMike Smith     switch(sc->mlx_iftype) {
417da8bb3a3SMike Smith     case MLX_IFTYPE_2:
418da8bb3a3SMike Smith 	/* These controllers don't report the firmware version in the ENQUIRY2 response */
419da8bb3a3SMike Smith 	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
420da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
4215d278f5cSMike Smith 	    mlx_free(sc);
422da8bb3a3SMike Smith 	    return(ENXIO);
423da8bb3a3SMike Smith 	}
424da8bb3a3SMike Smith 	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
425da8bb3a3SMike Smith 	free(meo, M_DEVBUF);
426da8bb3a3SMike Smith 
427da8bb3a3SMike Smith 	/* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */
428da8bb3a3SMike Smith 	if (meo->me_fwminor < 42) {
429da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
430da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
431da8bb3a3SMike Smith 	}
432da8bb3a3SMike Smith 	break;
4331ac4b82bSMike Smith     case MLX_IFTYPE_3:
434f6b84b08SMike Smith 	/* XXX certify 3.52? */
4359eee27f1SMike Smith 	if (fwminor < 51) {
4364b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4374b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
4381ac4b82bSMike Smith 	}
4391ac4b82bSMike Smith 	break;
440f6b84b08SMike Smith     case MLX_IFTYPE_4:
441f6b84b08SMike Smith 	/* XXX certify firmware versions? */
4429eee27f1SMike Smith 	if (fwminor < 6) {
4434b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4444b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
445f6b84b08SMike Smith 	}
446f6b84b08SMike Smith 	break;
4475792b7feSMike Smith     case MLX_IFTYPE_5:
4489eee27f1SMike Smith 	if (fwminor < 7) {
4495792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4505792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
4515792b7feSMike Smith 	}
4525792b7feSMike Smith 	break;
4531ac4b82bSMike Smith     default:
4545d278f5cSMike Smith 	mlx_free(sc);
4551ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4561ac4b82bSMike Smith     }
4571ac4b82bSMike Smith 
4581ac4b82bSMike Smith     /*
4591ac4b82bSMike Smith      * Create the final set of s/g mappings now that we know how many commands
4601ac4b82bSMike Smith      * the controller actually supports.
4611ac4b82bSMike Smith      */
4629eee27f1SMike Smith     sc->mlx_maxiop = sc->mlx_enq2->me_max_commands;
4631ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4641ac4b82bSMike Smith     if (error != 0) {
465421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't make permanent s/g list mapping\n");
4665d278f5cSMike Smith 	mlx_free(sc);
4671ac4b82bSMike Smith 	return(error);
4681ac4b82bSMike Smith     }
4691ac4b82bSMike Smith 
4701ac4b82bSMike Smith     /*
471421f2f7dSMike Smith      * No user-requested background operation is in progress.
4721ac4b82bSMike Smith      */
473421f2f7dSMike Smith     sc->mlx_background = 0;
474421f2f7dSMike Smith     sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
4751ac4b82bSMike Smith 
4761ac4b82bSMike Smith     /*
477da8bb3a3SMike Smith      * Create the control device.
4781ac4b82bSMike Smith      */
479da8bb3a3SMike Smith     sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR,
480da8bb3a3SMike Smith 			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
4811ac4b82bSMike Smith 
4821ac4b82bSMike Smith     /*
4831ac4b82bSMike Smith      * Start the timeout routine.
4841ac4b82bSMike Smith      */
4851ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
4861ac4b82bSMike Smith 
487da8bb3a3SMike Smith     /* print a little information about the controller */
488da8bb3a3SMike Smith     mlx_describe_controller(sc);
489da8bb3a3SMike Smith 
4901ac4b82bSMike Smith     return(0);
4911ac4b82bSMike Smith }
4921ac4b82bSMike Smith 
4931ac4b82bSMike Smith /********************************************************************************
4941ac4b82bSMike Smith  * Locate disk resources and attach children to them.
4951ac4b82bSMike Smith  */
4961ac4b82bSMike Smith void
4971ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
4981ac4b82bSMike Smith {
4991ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
5001ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
5011ac4b82bSMike Smith     int				i, error;
5021ac4b82bSMike Smith 
503da8bb3a3SMike Smith     debug_called(1);
5041ac4b82bSMike Smith 
5051ac4b82bSMike Smith     /*
5061ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
5071ac4b82bSMike Smith      * don't currently have them.
5081ac4b82bSMike Smith      */
5091ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
5101ac4b82bSMike Smith     if (mes == NULL) {
5119eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
5121ac4b82bSMike Smith 	return;
5131ac4b82bSMike Smith     }
5141ac4b82bSMike Smith 
5151ac4b82bSMike Smith     /* iterate over drives returned */
5161ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
5171ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
5181ac4b82bSMike Smith 	 i++, dr++) {
5191ac4b82bSMike Smith 	/* are we already attached to this drive? */
5201ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
5211ac4b82bSMike Smith 	    /* pick up drive information */
5221ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
523f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
5241ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
5251ac4b82bSMike Smith 
5261ac4b82bSMike Smith 	    /* generate geometry information */
5271ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
5281ac4b82bSMike Smith 		dr->ms_heads = 128;
5291ac4b82bSMike Smith 		dr->ms_sectors = 32;
5301ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
5311ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
5321ac4b82bSMike Smith 		dr->ms_heads = 255;
5331ac4b82bSMike Smith 		dr->ms_sectors = 63;
5341ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
5351ac4b82bSMike Smith 	    }
536fe0d4089SMatthew N. Dodd 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
5371ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
5381ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
539fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
5401ac4b82bSMike Smith 	}
5411ac4b82bSMike Smith     }
5421ac4b82bSMike Smith     free(mes, M_DEVBUF);
5431ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
5441ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
5451ac4b82bSMike Smith 
5461ac4b82bSMike Smith     /* mark controller back up */
5471ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5481ac4b82bSMike Smith 
5491ac4b82bSMike Smith     /* enable interrupts */
5501ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
5511ac4b82bSMike Smith }
5521ac4b82bSMike Smith 
5531ac4b82bSMike Smith /********************************************************************************
5541ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
5551ac4b82bSMike Smith  */
5561ac4b82bSMike Smith int
5571ac4b82bSMike Smith mlx_detach(device_t dev)
5581ac4b82bSMike Smith {
5591ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5605792b7feSMike Smith     struct mlxd_softc	*mlxd;
5615792b7feSMike Smith     int			i, s, error;
5621ac4b82bSMike Smith 
563da8bb3a3SMike Smith     debug_called(1);
5641ac4b82bSMike Smith 
5655792b7feSMike Smith     error = EBUSY;
5665792b7feSMike Smith     s = splbio();
5671ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
5685792b7feSMike Smith 	goto out;
5691ac4b82bSMike Smith 
5705792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5715792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5725792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
5735792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
5745792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
5755792b7feSMike Smith 		goto out;
5765792b7feSMike Smith 	    }
5775792b7feSMike Smith 	}
5785792b7feSMike Smith     }
5791ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
5805792b7feSMike Smith 	goto out;
5811ac4b82bSMike Smith 
5821ac4b82bSMike Smith     mlx_free(sc);
5831ac4b82bSMike Smith 
5845792b7feSMike Smith     error = 0;
5855792b7feSMike Smith  out:
5865792b7feSMike Smith     splx(s);
5875792b7feSMike Smith     return(error);
5881ac4b82bSMike Smith }
5891ac4b82bSMike Smith 
5901ac4b82bSMike Smith /********************************************************************************
5911ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
5921ac4b82bSMike Smith  *
5931ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
5941ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
5951ac4b82bSMike Smith  * resume normal operation.)
5961ac4b82bSMike Smith  *
5978177437dSPoul-Henning Kamp  * Note that we can assume that the bioq on the controller is empty, as we won't
5981ac4b82bSMike Smith  * allow shutdown if any device is open.
5991ac4b82bSMike Smith  */
6001ac4b82bSMike Smith int
6011ac4b82bSMike Smith mlx_shutdown(device_t dev)
6021ac4b82bSMike Smith {
6031ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6041ac4b82bSMike Smith     int			i, s, error;
6051ac4b82bSMike Smith 
606da8bb3a3SMike Smith     debug_called(1);
6071ac4b82bSMike Smith 
6081ac4b82bSMike Smith     s = splbio();
6091ac4b82bSMike Smith     error = 0;
6101ac4b82bSMike Smith 
6111ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
6125792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6131ac4b82bSMike Smith 
6141ac4b82bSMike Smith     /* flush controller */
6151ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6161ac4b82bSMike Smith     if (mlx_flush(sc)) {
6171ac4b82bSMike Smith 	printf("failed\n");
6181ac4b82bSMike Smith     } else {
6191ac4b82bSMike Smith 	printf("done\n");
6201ac4b82bSMike Smith     }
6211ac4b82bSMike Smith 
6221ac4b82bSMike Smith     /* delete all our child devices */
6231ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6241ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6251ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
6261ac4b82bSMike Smith 		goto out;
6271ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
6281ac4b82bSMike Smith 	}
6291ac4b82bSMike Smith     }
6301ac4b82bSMike Smith 
6311ac4b82bSMike Smith  out:
6321ac4b82bSMike Smith     splx(s);
6331ac4b82bSMike Smith     return(error);
6341ac4b82bSMike Smith }
6351ac4b82bSMike Smith 
6361ac4b82bSMike Smith /********************************************************************************
6371ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
6381ac4b82bSMike Smith  */
6391ac4b82bSMike Smith int
6401ac4b82bSMike Smith mlx_suspend(device_t dev)
6411ac4b82bSMike Smith {
6421ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6431ac4b82bSMike Smith     int			s;
6441ac4b82bSMike Smith 
645da8bb3a3SMike Smith     debug_called(1);
6461ac4b82bSMike Smith 
6471ac4b82bSMike Smith     s = splbio();
6481ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
6491ac4b82bSMike Smith 
6501ac4b82bSMike Smith     /* flush controller */
6511ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6521ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6531ac4b82bSMike Smith 
6541ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6551ac4b82bSMike Smith     splx(s);
6561ac4b82bSMike Smith 
6571ac4b82bSMike Smith     return(0);
6581ac4b82bSMike Smith }
6591ac4b82bSMike Smith 
6601ac4b82bSMike Smith /********************************************************************************
6611ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
6621ac4b82bSMike Smith  */
6631ac4b82bSMike Smith int
6641ac4b82bSMike Smith mlx_resume(device_t dev)
6651ac4b82bSMike Smith {
6661ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6671ac4b82bSMike Smith 
668da8bb3a3SMike Smith     debug_called(1);
6691ac4b82bSMike Smith 
6701ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
6711ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
6721ac4b82bSMike Smith 
6731ac4b82bSMike Smith     return(0);
6741ac4b82bSMike Smith }
6751ac4b82bSMike Smith 
6761ac4b82bSMike Smith /*******************************************************************************
6771ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
6781ac4b82bSMike Smith  * status.
6791ac4b82bSMike Smith  */
6801ac4b82bSMike Smith void
6811ac4b82bSMike Smith mlx_intr(void *arg)
6821ac4b82bSMike Smith {
6831ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
6841ac4b82bSMike Smith 
685da8bb3a3SMike Smith     debug_called(1);
6861ac4b82bSMike Smith 
6875792b7feSMike Smith     /* collect finished commands, queue anything waiting */
6885792b7feSMike Smith     mlx_done(sc);
6891ac4b82bSMike Smith };
6901ac4b82bSMike Smith 
6911ac4b82bSMike Smith /*******************************************************************************
6921ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
6931ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
6941ac4b82bSMike Smith  */
6951ac4b82bSMike Smith int
6968177437dSPoul-Henning Kamp mlx_submit_buf(struct mlx_softc *sc, struct bio *bp)
6971ac4b82bSMike Smith {
6984b006d7bSMike Smith     int		s;
6994b006d7bSMike Smith 
700da8bb3a3SMike Smith     debug_called(1);
7011ac4b82bSMike Smith 
7024b006d7bSMike Smith     s = splbio();
7038177437dSPoul-Henning Kamp     bioq_insert_tail(&sc->mlx_bioq, bp);
7041ac4b82bSMike Smith     sc->mlx_waitbufs++;
7054b006d7bSMike Smith     splx(s);
7061ac4b82bSMike Smith     mlx_startio(sc);
7071ac4b82bSMike Smith     return(0);
7081ac4b82bSMike Smith }
7091ac4b82bSMike Smith 
7101ac4b82bSMike Smith /********************************************************************************
7111ac4b82bSMike Smith  * Accept an open operation on the control device.
7121ac4b82bSMike Smith  */
7131ac4b82bSMike Smith int
7141ac4b82bSMike Smith mlx_open(dev_t dev, int flags, int fmt, struct proc *p)
7151ac4b82bSMike Smith {
7161ac4b82bSMike Smith     int			unit = minor(dev);
7171ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7181ac4b82bSMike Smith 
7191ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
7201ac4b82bSMike Smith     return(0);
7211ac4b82bSMike Smith }
7221ac4b82bSMike Smith 
7231ac4b82bSMike Smith /********************************************************************************
7241ac4b82bSMike Smith  * Accept the last close on the control device.
7251ac4b82bSMike Smith  */
7261ac4b82bSMike Smith int
7271ac4b82bSMike Smith mlx_close(dev_t dev, int flags, int fmt, struct proc *p)
7281ac4b82bSMike Smith {
7291ac4b82bSMike Smith     int			unit = minor(dev);
7301ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7311ac4b82bSMike Smith 
7321ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
7331ac4b82bSMike Smith     return (0);
7341ac4b82bSMike Smith }
7351ac4b82bSMike Smith 
7361ac4b82bSMike Smith /********************************************************************************
7371ac4b82bSMike Smith  * Handle controller-specific control operations.
7381ac4b82bSMike Smith  */
7391ac4b82bSMike Smith int
7401ac4b82bSMike Smith mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
7411ac4b82bSMike Smith {
7421ac4b82bSMike Smith     int				unit = minor(dev);
7431ac4b82bSMike Smith     struct mlx_softc		*sc = devclass_get_softc(mlx_devclass, unit);
744421f2f7dSMike Smith     struct mlx_rebuild_request	*rb = (struct mlx_rebuild_request *)addr;
745421f2f7dSMike Smith     struct mlx_rebuild_status	*rs = (struct mlx_rebuild_status *)addr;
7461ac4b82bSMike Smith     int				*arg = (int *)addr;
7471ac4b82bSMike Smith     struct mlx_pause		*mp;
7481ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
7491ac4b82bSMike Smith     struct mlxd_softc		*mlxd;
7501ac4b82bSMike Smith     int				i, error;
7511ac4b82bSMike Smith 
7521ac4b82bSMike Smith     switch(cmd) {
7531ac4b82bSMike Smith 	/*
7541ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
7551ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
7561ac4b82bSMike Smith 	 * a valid unit on this controller.
7571ac4b82bSMike Smith 	 */
7581ac4b82bSMike Smith     case MLX_NEXT_CHILD:
7591ac4b82bSMike Smith 	/* search system drives */
7601ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
7611ac4b82bSMike Smith 	    /* is this one attached? */
7621ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
7631ac4b82bSMike Smith 		/* looking for the next one we come across? */
7641ac4b82bSMike Smith 		if (*arg == -1) {
765421f2f7dSMike Smith 		    *arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk);
7661ac4b82bSMike Smith 		    return(0);
7671ac4b82bSMike Smith 		}
7681ac4b82bSMike Smith 		/* we want the one after this one */
7691ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
7701ac4b82bSMike Smith 		    *arg = -1;
7711ac4b82bSMike Smith 	    }
7721ac4b82bSMike Smith 	}
7731ac4b82bSMike Smith 	return(ENOENT);
7741ac4b82bSMike Smith 
7751ac4b82bSMike Smith 	/*
7761ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
7771ac4b82bSMike Smith 	 */
7781ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
7791ac4b82bSMike Smith 	mlx_startup(sc);
7801ac4b82bSMike Smith 	return(0);
7811ac4b82bSMike Smith 
7821ac4b82bSMike Smith 	/*
7831ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
7841ac4b82bSMike Smith 	 * away.
7851ac4b82bSMike Smith 	 */
7861ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
7871ac4b82bSMike Smith 
7881ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
7891ac4b82bSMike Smith 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
7901ac4b82bSMike Smith 	    return(ENOENT);
7911ac4b82bSMike Smith 
7921ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
7931ac4b82bSMike Smith 	error = 0;
7941ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
7951ac4b82bSMike Smith 	    error = EBUSY;
7961ac4b82bSMike Smith 	    goto detach_out;
7971ac4b82bSMike Smith 	}
7981ac4b82bSMike Smith 
7991ac4b82bSMike Smith 	/* flush controller */
8001ac4b82bSMike Smith 	if (mlx_flush(sc)) {
8011ac4b82bSMike Smith 	    error = EBUSY;
8021ac4b82bSMike Smith 	    goto detach_out;
8031ac4b82bSMike Smith 	}
8041ac4b82bSMike Smith 
8051ac4b82bSMike Smith 	/* nuke drive */
8061ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
8071ac4b82bSMike Smith 	    goto detach_out;
8081ac4b82bSMike Smith 	dr->ms_disk = 0;
8091ac4b82bSMike Smith 
8101ac4b82bSMike Smith     detach_out:
8111ac4b82bSMike Smith 	if (error) {
8121ac4b82bSMike Smith 	    printf("failed\n");
8131ac4b82bSMike Smith 	} else {
8141ac4b82bSMike Smith 	    printf("done\n");
8151ac4b82bSMike Smith 	}
8161ac4b82bSMike Smith 	return(error);
8171ac4b82bSMike Smith 
8181ac4b82bSMike Smith 	/*
8191ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
8201ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
8211ac4b82bSMike Smith 	 *
8221ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
8231ac4b82bSMike Smith 	 * to do this right.
8241ac4b82bSMike Smith 	 */
8251ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
8261ac4b82bSMike Smith 	/* Does this command work on this firmware? */
8271ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
8281ac4b82bSMike Smith 	    return(EOPNOTSUPP);
8291ac4b82bSMike Smith 
8301ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
8311ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
8321ac4b82bSMike Smith 	    /* cancel a pending pause operation */
8331ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
8341ac4b82bSMike Smith 	} else {
8351ac4b82bSMike Smith 	    /* fix for legal channels */
8369eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
8371ac4b82bSMike Smith 	    /* check time values */
8381ac4b82bSMike Smith 	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
8391ac4b82bSMike Smith 		return(EINVAL);
8401ac4b82bSMike Smith 	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
8411ac4b82bSMike Smith 		return(EINVAL);
8421ac4b82bSMike Smith 
8431ac4b82bSMike Smith 	    /* check for a pause currently running */
8441ac4b82bSMike Smith 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
8451ac4b82bSMike Smith 		return(EBUSY);
8461ac4b82bSMike Smith 
8471ac4b82bSMike Smith 	    /* looks ok, go with it */
8481ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
8491ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
8501ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
8511ac4b82bSMike Smith 	}
8521ac4b82bSMike Smith 	return(0);
8531ac4b82bSMike Smith 
8541ac4b82bSMike Smith 	/*
8551ac4b82bSMike Smith 	 * Accept a command passthrough-style.
8561ac4b82bSMike Smith 	 */
8571ac4b82bSMike Smith     case MLX_COMMAND:
8581ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
8591ac4b82bSMike Smith 
860421f2f7dSMike Smith 	/*
861421f2f7dSMike Smith 	 * Start a rebuild on a given SCSI disk
862421f2f7dSMike Smith 	 */
863421f2f7dSMike Smith     case MLX_REBUILDASYNC:
864421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
865421f2f7dSMike Smith 	    rb->rr_status = 0x0106;
866421f2f7dSMike Smith 	    return(EBUSY);
867421f2f7dSMike Smith 	}
868421f2f7dSMike Smith 	rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
869421f2f7dSMike Smith 	switch (rb->rr_status) {
870421f2f7dSMike Smith 	case 0:
871421f2f7dSMike Smith 	    error = 0;
872421f2f7dSMike Smith 	    break;
873421f2f7dSMike Smith 	case 0x10000:
874421f2f7dSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
875421f2f7dSMike Smith 	    break;
876421f2f7dSMike Smith 	case 0x0002:
877421f2f7dSMike Smith 	    error = EBUSY;
878421f2f7dSMike Smith 	    break;
879421f2f7dSMike Smith 	case 0x0104:
880421f2f7dSMike Smith 	    error = EIO;
881421f2f7dSMike Smith 	    break;
882421f2f7dSMike Smith 	case 0x0105:
883421f2f7dSMike Smith 	    error = ERANGE;
884421f2f7dSMike Smith 	    break;
885421f2f7dSMike Smith 	case 0x0106:
886421f2f7dSMike Smith 	    error = EBUSY;
887421f2f7dSMike Smith 	    break;
888421f2f7dSMike Smith 	default:
889421f2f7dSMike Smith 	    error = EINVAL;
890421f2f7dSMike Smith 	    break;
891421f2f7dSMike Smith 	}
892421f2f7dSMike Smith 	if (error == 0)
893421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_REBUILD;
894421f2f7dSMike Smith 	return(error);
895421f2f7dSMike Smith 
896421f2f7dSMike Smith 	/*
897421f2f7dSMike Smith 	 * Get the status of the current rebuild or consistency check.
898421f2f7dSMike Smith 	 */
899421f2f7dSMike Smith     case MLX_REBUILDSTAT:
900421f2f7dSMike Smith 	*rs = sc->mlx_rebuildstat;
901421f2f7dSMike Smith 	return(0);
902421f2f7dSMike Smith 
903421f2f7dSMike Smith 	/*
904421f2f7dSMike Smith 	 * Return the per-controller system drive number matching the
905421f2f7dSMike Smith 	 * disk device number in (arg), if it happens to belong to us.
906421f2f7dSMike Smith 	 */
907421f2f7dSMike Smith     case MLX_GET_SYSDRIVE:
908421f2f7dSMike Smith 	error = ENOENT;
909421f2f7dSMike Smith 	mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg);
910421f2f7dSMike Smith 	if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
911421f2f7dSMike Smith 	    (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
912421f2f7dSMike Smith 	    error = 0;
913421f2f7dSMike Smith 	    *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
914421f2f7dSMike Smith 	}
915421f2f7dSMike Smith 	return(error);
916421f2f7dSMike Smith 
9171ac4b82bSMike Smith     default:
9181ac4b82bSMike Smith 	return(ENOTTY);
9191ac4b82bSMike Smith     }
9201ac4b82bSMike Smith }
9211ac4b82bSMike Smith 
9221ac4b82bSMike Smith /********************************************************************************
9231ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
9241ac4b82bSMike Smith  */
9251ac4b82bSMike Smith int
9261ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
9271ac4b82bSMike Smith 		caddr_t addr, int32_t flag, struct proc *p)
9281ac4b82bSMike Smith {
9291ac4b82bSMike Smith     int				*arg = (int *)addr;
930421f2f7dSMike Smith     int				error, result;
9311ac4b82bSMike Smith 
9321ac4b82bSMike Smith     switch(cmd) {
9331ac4b82bSMike Smith 	/*
9341ac4b82bSMike Smith 	 * Return the current status of this drive.
9351ac4b82bSMike Smith 	 */
9361ac4b82bSMike Smith     case MLXD_STATUS:
9371ac4b82bSMike Smith 	*arg = drive->ms_state;
9381ac4b82bSMike Smith 	return(0);
9391ac4b82bSMike Smith 
9401ac4b82bSMike Smith 	/*
941421f2f7dSMike Smith 	 * Start a background consistency check on this drive.
9421ac4b82bSMike Smith 	 */
943421f2f7dSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
944421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
945421f2f7dSMike Smith 	    *arg = 0x0106;
9461ac4b82bSMike Smith 	    return(EBUSY);
947421f2f7dSMike Smith 	}
948421f2f7dSMike Smith 	result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
949421f2f7dSMike Smith 	switch (result) {
9501ac4b82bSMike Smith 	case 0:
9511ac4b82bSMike Smith 	    error = 0;
9521ac4b82bSMike Smith 	    break;
9531ac4b82bSMike Smith 	case 0x10000:
9541ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
9551ac4b82bSMike Smith 	    break;
9561ac4b82bSMike Smith 	case 0x0002:
9571ac4b82bSMike Smith 	    error = EIO;
9581ac4b82bSMike Smith 	    break;
9591ac4b82bSMike Smith 	case 0x0105:
9601ac4b82bSMike Smith 	    error = ERANGE;
9611ac4b82bSMike Smith 	    break;
962421f2f7dSMike Smith 	case 0x0106:
963421f2f7dSMike Smith 	    error = EBUSY;
964421f2f7dSMike Smith 	    break;
9651ac4b82bSMike Smith 	default:
9661ac4b82bSMike Smith 	    error = EINVAL;
9671ac4b82bSMike Smith 	    break;
9681ac4b82bSMike Smith 	}
969421f2f7dSMike Smith 	if (error == 0)
970421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_CHECK;
971421f2f7dSMike Smith 	*arg = result;
9721ac4b82bSMike Smith 	return(error);
9731ac4b82bSMike Smith 
9741ac4b82bSMike Smith     }
9751ac4b82bSMike Smith     return(ENOIOCTL);
9761ac4b82bSMike Smith }
9771ac4b82bSMike Smith 
9781ac4b82bSMike Smith 
9791ac4b82bSMike Smith /********************************************************************************
9801ac4b82bSMike Smith  ********************************************************************************
9811ac4b82bSMike Smith                                                                 Status Monitoring
9821ac4b82bSMike Smith  ********************************************************************************
9831ac4b82bSMike Smith  ********************************************************************************/
9841ac4b82bSMike Smith 
9851ac4b82bSMike Smith /********************************************************************************
9861ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
9871ac4b82bSMike Smith  */
9881ac4b82bSMike Smith static void
9891ac4b82bSMike Smith mlx_periodic(void *data)
9901ac4b82bSMike Smith {
9911ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
9921ac4b82bSMike Smith 
993da8bb3a3SMike Smith     debug_called(1);
9941ac4b82bSMike Smith 
9951ac4b82bSMike Smith     /*
9961ac4b82bSMike Smith      * Run a bus pause?
9971ac4b82bSMike Smith      */
9981ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
9991ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
10001ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
10011ac4b82bSMike Smith 
10021ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
10031ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
10041ac4b82bSMike Smith 	sysbeep(500, hz);
10051ac4b82bSMike Smith 
10061ac4b82bSMike Smith 	/*
10071ac4b82bSMike Smith 	 * Bus pause still running?
10081ac4b82bSMike Smith 	 */
10091ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
10101ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
10111ac4b82bSMike Smith 
10121ac4b82bSMike Smith 	/* time to stop bus pause? */
10131ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
10141ac4b82bSMike Smith 	    mlx_pause_action(sc);
10151ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
10161ac4b82bSMike Smith 	    sysbeep(500, hz);
10171ac4b82bSMike Smith 	} else {
10181ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
10191ac4b82bSMike Smith 	}
10201ac4b82bSMike Smith 
10211ac4b82bSMike Smith 	/*
10221ac4b82bSMike Smith 	 * Run normal periodic activities?
10231ac4b82bSMike Smith 	 */
10245792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
10251ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
10261ac4b82bSMike Smith 
10271ac4b82bSMike Smith 	/*
10281ac4b82bSMike Smith 	 * Check controller status.
10295792b7feSMike Smith 	 *
10305792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
10311ac4b82bSMike Smith 	 */
1032da8bb3a3SMike Smith 	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
1033da8bb3a3SMike Smith 		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
10341ac4b82bSMike Smith 
10351ac4b82bSMike Smith 	/*
10361ac4b82bSMike Smith 	 * Check system drive status.
10371ac4b82bSMike Smith 	 *
10381ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
10391ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
10401ac4b82bSMike Smith 	 *     online.
10411ac4b82bSMike Smith 	 */
10421ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
10431ac4b82bSMike Smith 			mlx_periodic_enquiry);
10441ac4b82bSMike Smith 
10451ac4b82bSMike Smith     }
10461ac4b82bSMike Smith 
1047421f2f7dSMike Smith     /* get drive rebuild/check status */
1048421f2f7dSMike Smith     /* XXX should check sc->mlx_background if this is only valid while in progress */
1049421f2f7dSMike Smith     mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
1050421f2f7dSMike Smith 
10515792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
10525792b7feSMike Smith     mlx_done(sc);
10531ac4b82bSMike Smith 
10541ac4b82bSMike Smith     /* reschedule another poll next second or so */
10551ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
10561ac4b82bSMike Smith }
10571ac4b82bSMike Smith 
10581ac4b82bSMike Smith /********************************************************************************
10591ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
10601ac4b82bSMike Smith  */
10611ac4b82bSMike Smith static void
10621ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
10631ac4b82bSMike Smith {
10641ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
10651ac4b82bSMike Smith 
1066da8bb3a3SMike Smith     debug_called(1);
10671ac4b82bSMike Smith 
10681ac4b82bSMike Smith     /* Command completed OK? */
10691ac4b82bSMike Smith     if (mc->mc_status != 0) {
1070da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
10711ac4b82bSMike Smith 	goto out;
10721ac4b82bSMike Smith     }
10731ac4b82bSMike Smith 
10741ac4b82bSMike Smith     /* respond to command */
10751ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
10761ac4b82bSMike Smith 	/*
1077da8bb3a3SMike Smith 	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
1078da8bb3a3SMike Smith 	 * pointer yet.
1079da8bb3a3SMike Smith 	 */
1080da8bb3a3SMike Smith     case MLX_CMD_ENQUIRY_OLD:
1081da8bb3a3SMike Smith     {
1082da8bb3a3SMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
1083da8bb3a3SMike Smith 	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
1084da8bb3a3SMike Smith 	int				i;
1085da8bb3a3SMike Smith 
1086da8bb3a3SMike Smith 	/* convert data in-place to new format */
1087da8bb3a3SMike Smith 	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
1088da8bb3a3SMike Smith 	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1089da8bb3a3SMike Smith 	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1090da8bb3a3SMike Smith 	}
1091da8bb3a3SMike Smith 	me->me_misc_flags        = 0;
1092da8bb3a3SMike Smith 	me->me_rebuild_count     = meo->me_rebuild_count;
1093da8bb3a3SMike Smith 	me->me_dead_count        = meo->me_dead_count;
1094da8bb3a3SMike Smith 	me->me_critical_sd_count = meo->me_critical_sd_count;
1095da8bb3a3SMike Smith 	me->me_event_log_seq_num = 0;
1096da8bb3a3SMike Smith 	me->me_offline_sd_count  = meo->me_offline_sd_count;
1097da8bb3a3SMike Smith 	me->me_max_commands      = meo->me_max_commands;
1098da8bb3a3SMike Smith 	me->me_rebuild_flag      = meo->me_rebuild_flag;
1099da8bb3a3SMike Smith 	me->me_fwmajor           = meo->me_fwmajor;
1100da8bb3a3SMike Smith 	me->me_fwminor           = meo->me_fwminor;
1101da8bb3a3SMike Smith 	me->me_status_flags      = meo->me_status_flags;
1102da8bb3a3SMike Smith 	me->me_flash_age         = meo->me_flash_age;
1103da8bb3a3SMike Smith 	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
1104da8bb3a3SMike Smith 	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
1105da8bb3a3SMike Smith 		me->me_drvsize[i] = 0;		/* drive beyond supported range */
1106da8bb3a3SMike Smith 	    } else {
1107da8bb3a3SMike Smith 		me->me_drvsize[i] = meo->me_drvsize[i];
1108da8bb3a3SMike Smith 	    }
1109da8bb3a3SMike Smith 	}
1110da8bb3a3SMike Smith 	me->me_num_sys_drvs = meo->me_num_sys_drvs;
1111da8bb3a3SMike Smith     }
1112da8bb3a3SMike Smith     /* FALLTHROUGH */
1113da8bb3a3SMike Smith 
1114da8bb3a3SMike Smith 	/*
11151ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
11161ac4b82bSMike Smith 	 * checking the event log.
11171ac4b82bSMike Smith 	 */
11181ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
11191ac4b82bSMike Smith     {
11201ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
11211ac4b82bSMike Smith 
1122421f2f7dSMike Smith 	if (sc->mlx_currevent == -1) {
11239eee27f1SMike Smith 	    /* initialise our view of the event log */
11249eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
11255d278f5cSMike Smith 	} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
11261ac4b82bSMike Smith 	    /* record where current events are up to */
11271ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
1128da8bb3a3SMike Smith 	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
11291ac4b82bSMike Smith 
11305d278f5cSMike Smith 	    /* mark the event log as busy */
11315d278f5cSMike Smith 	    atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
11325d278f5cSMike Smith 
11339eee27f1SMike Smith 	    /* drain new eventlog entries */
11341ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
11351ac4b82bSMike Smith 	}
11361ac4b82bSMike Smith 	break;
11371ac4b82bSMike Smith     }
11381ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
11391ac4b82bSMike Smith     {
11401ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
11411ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
11421ac4b82bSMike Smith 	int				i;
11431ac4b82bSMike Smith 
11441ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
11451ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
11461ac4b82bSMike Smith 	     i++) {
11471ac4b82bSMike Smith 
11481ac4b82bSMike Smith 	    /* has state been changed by controller? */
11491ac4b82bSMike Smith 	    if (dr->ms_state != mes[i].sd_state) {
11501ac4b82bSMike Smith 		switch(mes[i].sd_state) {
11511ac4b82bSMike Smith 		case MLX_SYSD_OFFLINE:
11521ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive offline\n");
11531ac4b82bSMike Smith 		    break;
11541ac4b82bSMike Smith 		case MLX_SYSD_ONLINE:
11551ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive online\n");
11561ac4b82bSMike Smith 		    break;
11571ac4b82bSMike Smith 		case MLX_SYSD_CRITICAL:
11581ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive critical\n");
11591ac4b82bSMike Smith 		    break;
11601ac4b82bSMike Smith 		}
11611ac4b82bSMike Smith 		/* save new state */
11621ac4b82bSMike Smith 		dr->ms_state = mes[i].sd_state;
11631ac4b82bSMike Smith 	    }
11641ac4b82bSMike Smith 	}
11651ac4b82bSMike Smith 	break;
11661ac4b82bSMike Smith     }
11671ac4b82bSMike Smith     default:
11685792b7feSMike Smith 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __FUNCTION__, mc->mc_mailbox[0]);
11691ac4b82bSMike Smith 	break;
11701ac4b82bSMike Smith     }
11711ac4b82bSMike Smith 
11721ac4b82bSMike Smith  out:
11731ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
11741ac4b82bSMike Smith     mlx_releasecmd(mc);
11751ac4b82bSMike Smith }
11761ac4b82bSMike Smith 
11771ac4b82bSMike Smith /********************************************************************************
11781ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
11791ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
11801ac4b82bSMike Smith  */
11811ac4b82bSMike Smith static void
11821ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
11831ac4b82bSMike Smith {
11841ac4b82bSMike Smith     struct mlx_command	*mc;
11851ac4b82bSMike Smith     void		*result = NULL;
11861ac4b82bSMike Smith     int			error;
11871ac4b82bSMike Smith 
1188da8bb3a3SMike Smith     debug_called(1);
11891ac4b82bSMike Smith 
11901ac4b82bSMike Smith     /* get ourselves a command buffer */
11911ac4b82bSMike Smith     error = 1;
11921ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
11931ac4b82bSMike Smith 	goto out;
11941ac4b82bSMike Smith     /* allocate the response structure */
119533c8cb18SMike Smith     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL)
11961ac4b82bSMike Smith 	goto out;
11971ac4b82bSMike Smith     /* get a command slot */
11981ac4b82bSMike Smith     if (mlx_getslot(mc))
11991ac4b82bSMike Smith 	goto out;
12001ac4b82bSMike Smith 
12011ac4b82bSMike Smith     /* map the command so the controller can see it */
12021ac4b82bSMike Smith     mc->mc_data = result;
120333c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
12041ac4b82bSMike Smith     mlx_mapcmd(mc);
12051ac4b82bSMike Smith 
12061ac4b82bSMike Smith     /* build the command to get one entry */
12071ac4b82bSMike Smith     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
12081ac4b82bSMike Smith     mc->mc_complete = mlx_periodic_eventlog_respond;
12091ac4b82bSMike Smith     mc->mc_private = mc;
12101ac4b82bSMike Smith 
12111ac4b82bSMike Smith     /* start the command */
12121ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
12131ac4b82bSMike Smith 	goto out;
12141ac4b82bSMike Smith 
12151ac4b82bSMike Smith     error = 0;			/* success */
12161ac4b82bSMike Smith  out:
121733c8cb18SMike Smith     if (error != 0) {
12181ac4b82bSMike Smith 	if (mc != NULL)
12191ac4b82bSMike Smith 	    mlx_releasecmd(mc);
122033c8cb18SMike Smith 	if (result != NULL)
12211ac4b82bSMike Smith 	    free(result, M_DEVBUF);
12221ac4b82bSMike Smith     }
122333c8cb18SMike Smith }
12241ac4b82bSMike Smith 
12251ac4b82bSMike Smith /********************************************************************************
12261ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
12271ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
12281ac4b82bSMike Smith  */
12291ac4b82bSMike Smith static char *mlx_sense_messages[] = {
12301ac4b82bSMike Smith     "because write recovery failed",
12311ac4b82bSMike Smith     "because of SCSI bus reset failure",
12321ac4b82bSMike Smith     "because of double check condition",
12331ac4b82bSMike Smith     "because it was removed",
12341ac4b82bSMike Smith     "because of gross error on SCSI chip",
12351ac4b82bSMike Smith     "because of bad tag returned from drive",
12361ac4b82bSMike Smith     "because of timeout on SCSI command",
12371ac4b82bSMike Smith     "because of reset SCSI command issued from system",
12381ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
12391ac4b82bSMike Smith     "because of 'kill drive' command from system",
12401ac4b82bSMike Smith     "because of selection timeout",
12411ac4b82bSMike Smith     "due to SCSI phase sequence error",
12421ac4b82bSMike Smith     "due to unknown status"
12431ac4b82bSMike Smith };
12441ac4b82bSMike Smith 
12451ac4b82bSMike Smith static void
12461ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
12471ac4b82bSMike Smith {
12481ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
12491ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
12501ac4b82bSMike Smith     char			*reason;
12511ac4b82bSMike Smith 
1252da8bb3a3SMike Smith     debug_called(1);
12531ac4b82bSMike Smith 
12545792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
12551ac4b82bSMike Smith     if (mc->mc_status == 0) {
12561ac4b82bSMike Smith 
12571ac4b82bSMike Smith 	/* handle event log message */
12581ac4b82bSMike Smith 	switch(el->el_type) {
12591ac4b82bSMike Smith 	    /*
12601ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
12611ac4b82bSMike Smith 	     * The tests here are probably incomplete.
12621ac4b82bSMike Smith 	     */
12631ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
12641ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
12651ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
12661ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
12671ac4b82bSMike Smith 		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
12681ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
12691ac4b82bSMike Smith 		} else {
12701ac4b82bSMike Smith 		    reason = "for unknown reason";
12711ac4b82bSMike Smith 		}
12721ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
12731ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
12741ac4b82bSMike Smith 	    }
12751ac4b82bSMike Smith 	    /* SCSI drive was reset? */
12761ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
12771ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
12781ac4b82bSMike Smith 			      el->el_channel, el->el_target);
12791ac4b82bSMike Smith 	    }
12801ac4b82bSMike Smith 	    /* SCSI drive error? */
12811ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
12821ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
12831ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
12841ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
12851ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
12861ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
12871ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
12881ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
12891ac4b82bSMike Smith 	    }
12901ac4b82bSMike Smith 	    break;
12911ac4b82bSMike Smith 
12921ac4b82bSMike Smith 	default:
12931ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
12941ac4b82bSMike Smith 	    break;
12951ac4b82bSMike Smith 	}
12961ac4b82bSMike Smith     } else {
12971ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
12985d278f5cSMike Smith 	/* give up on all the outstanding messages, as we may have come unsynched */
12995d278f5cSMike Smith 	sc->mlx_lastevent = sc->mlx_currevent;
13001ac4b82bSMike Smith     }
13011ac4b82bSMike Smith 
13021ac4b82bSMike Smith     /* dispose of command and data */
13031ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
13041ac4b82bSMike Smith     mlx_releasecmd(mc);
13051ac4b82bSMike Smith 
13061ac4b82bSMike Smith     /* is there another message to obtain? */
13075d278f5cSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent) {
13081ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
13095d278f5cSMike Smith     } else {
13105d278f5cSMike Smith 	/* clear log-busy status */
13115d278f5cSMike Smith 	atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
13125d278f5cSMike Smith     }
13131ac4b82bSMike Smith }
13141ac4b82bSMike Smith 
13151ac4b82bSMike Smith /********************************************************************************
1316421f2f7dSMike Smith  * Handle check/rebuild operations in progress.
13171ac4b82bSMike Smith  */
13181ac4b82bSMike Smith static void
13191ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
13201ac4b82bSMike Smith {
13211ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
1322421f2f7dSMike Smith     struct mlx_rebuild_status	*mr = (struct mlx_rebuild_status *)mc->mc_data;
13231ac4b82bSMike Smith 
13241ac4b82bSMike Smith     switch(mc->mc_status) {
1325421f2f7dSMike Smith     case 0:				/* operation running, update stats */
1326421f2f7dSMike Smith 	sc->mlx_rebuildstat = *mr;
1327421f2f7dSMike Smith 
1328421f2f7dSMike Smith 	/* spontaneous rebuild/check? */
1329421f2f7dSMike Smith 	if (sc->mlx_background == 0) {
1330421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
1331421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
1332421f2f7dSMike Smith 	}
13331ac4b82bSMike Smith 	break;
13341ac4b82bSMike Smith 
1335421f2f7dSMike Smith     case 0x0105:			/* nothing running, finalise stats and report */
1336421f2f7dSMike Smith 	switch(sc->mlx_background) {
1337421f2f7dSMike Smith 	case MLX_BACKGROUND_CHECK:
1338421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");	/* XXX print drive? */
1339421f2f7dSMike Smith 	    break;
1340421f2f7dSMike Smith 	case MLX_BACKGROUND_REBUILD:
1341421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "drive rebuild completed\n");	/* XXX print channel/target? */
1342421f2f7dSMike Smith 	    break;
1343421f2f7dSMike Smith 	case MLX_BACKGROUND_SPONTANEOUS:
1344421f2f7dSMike Smith 	default:
1345421f2f7dSMike Smith 	    /* if we have previously been non-idle, report the transition */
1346421f2f7dSMike Smith 	    if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
1347421f2f7dSMike Smith 		device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
13481ac4b82bSMike Smith 	    }
1349421f2f7dSMike Smith 	}
1350421f2f7dSMike Smith 	sc->mlx_background = 0;
1351421f2f7dSMike Smith 	sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
13521ac4b82bSMike Smith 	break;
13531ac4b82bSMike Smith     }
13541ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
13551ac4b82bSMike Smith     mlx_releasecmd(mc);
13561ac4b82bSMike Smith }
13571ac4b82bSMike Smith 
13581ac4b82bSMike Smith /********************************************************************************
13591ac4b82bSMike Smith  ********************************************************************************
13601ac4b82bSMike Smith                                                                     Channel Pause
13611ac4b82bSMike Smith  ********************************************************************************
13621ac4b82bSMike Smith  ********************************************************************************/
13631ac4b82bSMike Smith 
13641ac4b82bSMike Smith /********************************************************************************
13651ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
13661ac4b82bSMike Smith  * the pause.
13671ac4b82bSMike Smith  */
13681ac4b82bSMike Smith static void
13691ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
13701ac4b82bSMike Smith {
13711ac4b82bSMike Smith     struct mlx_command	*mc;
13721ac4b82bSMike Smith     int			failsafe, i, command;
13731ac4b82bSMike Smith 
13741ac4b82bSMike Smith     /* What are we doing here? */
13751ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
13761ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
13771ac4b82bSMike Smith 	failsafe = 0;
13781ac4b82bSMike Smith 
13791ac4b82bSMike Smith     } else {
13801ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
13811ac4b82bSMike Smith 
13821ac4b82bSMike Smith 	/*
13831ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
13841ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
13851ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
13861ac4b82bSMike Smith 	 */
13871ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
13881ac4b82bSMike Smith 	if (failsafe > 0xf) {
13891ac4b82bSMike Smith 	    failsafe = 0xf;
13901ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
13911ac4b82bSMike Smith 	}
13921ac4b82bSMike Smith     }
13931ac4b82bSMike Smith 
13941ac4b82bSMike Smith     /* build commands for every channel requested */
13959eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
13961ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
13971ac4b82bSMike Smith 
13981ac4b82bSMike Smith 	    /* get ourselves a command buffer */
13991ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
14001ac4b82bSMike Smith 		goto fail;
14011ac4b82bSMike Smith 	    /* get a command slot */
14021ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
14031ac4b82bSMike Smith 	    if (mlx_getslot(mc))
14041ac4b82bSMike Smith 		goto fail;
14051ac4b82bSMike Smith 
14061ac4b82bSMike Smith 	    /* build the command */
14071ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
14081ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
14091ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
14101ac4b82bSMike Smith 	    if (mlx_start(mc))
14111ac4b82bSMike Smith 		goto fail;
14121ac4b82bSMike Smith 	    /* command submitted OK */
14131ac4b82bSMike Smith 	    return;
14141ac4b82bSMike Smith 
14151ac4b82bSMike Smith 	fail:
14161ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
14171ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
14181ac4b82bSMike Smith 	    if (mc != NULL)
14191ac4b82bSMike Smith 		mlx_releasecmd(mc);
14201ac4b82bSMike Smith 	}
14211ac4b82bSMike Smith     }
14221ac4b82bSMike Smith }
14231ac4b82bSMike Smith 
14241ac4b82bSMike Smith static void
14251ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
14261ac4b82bSMike Smith {
14271ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
14281ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
14291ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
14301ac4b82bSMike Smith 
14311ac4b82bSMike Smith     if (mc->mc_status != 0) {
14321ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
14331ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
14341ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
14351ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
143672c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
14371ac4b82bSMike Smith     } else {
14381ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
14391ac4b82bSMike Smith     }
14401ac4b82bSMike Smith     mlx_releasecmd(mc);
14411ac4b82bSMike Smith }
14421ac4b82bSMike Smith 
14431ac4b82bSMike Smith /********************************************************************************
14441ac4b82bSMike Smith  ********************************************************************************
14451ac4b82bSMike Smith                                                                Command Submission
14461ac4b82bSMike Smith  ********************************************************************************
14471ac4b82bSMike Smith  ********************************************************************************/
14481ac4b82bSMike Smith 
14491ac4b82bSMike Smith /********************************************************************************
14501ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
14511ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
14521ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
14531ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
14541ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
14551ac4b82bSMike Smith  */
14561ac4b82bSMike Smith static void *
14571ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
14581ac4b82bSMike Smith {
14591ac4b82bSMike Smith     struct mlx_command	*mc;
14601ac4b82bSMike Smith     void		*result;
14611ac4b82bSMike Smith     int			error;
14621ac4b82bSMike Smith 
1463da8bb3a3SMike Smith     debug_called(1);
14641ac4b82bSMike Smith 
14651ac4b82bSMike Smith     /* get ourselves a command buffer */
14661ac4b82bSMike Smith     error = 1;
14671ac4b82bSMike Smith     result = NULL;
14681ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14691ac4b82bSMike Smith 	goto out;
14701ac4b82bSMike Smith     /* allocate the response structure */
14711ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
14721ac4b82bSMike Smith 	goto out;
14731ac4b82bSMike Smith     /* get a command slot */
14741ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
14751ac4b82bSMike Smith     if (mlx_getslot(mc))
14761ac4b82bSMike Smith 	goto out;
14771ac4b82bSMike Smith 
14781ac4b82bSMike Smith     /* map the command so the controller can see it */
14791ac4b82bSMike Smith     mc->mc_data = result;
14801ac4b82bSMike Smith     mc->mc_length = bufsize;
14811ac4b82bSMike Smith     mlx_mapcmd(mc);
14821ac4b82bSMike Smith 
14831ac4b82bSMike Smith     /* build an enquiry command */
14841ac4b82bSMike Smith     mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
14851ac4b82bSMike Smith 
14861ac4b82bSMike Smith     /* do we want a completion callback? */
14871ac4b82bSMike Smith     if (complete != NULL) {
14881ac4b82bSMike Smith 	mc->mc_complete = complete;
14891ac4b82bSMike Smith 	mc->mc_private = mc;
14901ac4b82bSMike Smith 	if ((error = mlx_start(mc)) != 0)
14911ac4b82bSMike Smith 	    goto out;
14921ac4b82bSMike Smith     } else {
14931ac4b82bSMike Smith 	/* run the command in either polled or wait mode */
14941ac4b82bSMike Smith 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
14951ac4b82bSMike Smith 	    goto out;
14961ac4b82bSMike Smith 
14971ac4b82bSMike Smith 	/* command completed OK? */
14981ac4b82bSMike Smith 	if (mc->mc_status != 0) {
14991ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc));
15001ac4b82bSMike Smith 	    goto out;
15011ac4b82bSMike Smith 	}
15021ac4b82bSMike Smith     }
15031ac4b82bSMike Smith     error = 0;			/* success */
15041ac4b82bSMike Smith  out:
15051ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
15061ac4b82bSMike Smith     if ((complete == NULL) && (mc != NULL))
15071ac4b82bSMike Smith 	mlx_releasecmd(mc);
150833c8cb18SMike Smith     /* we got an error, and we allocated a result */
15091ac4b82bSMike Smith     if ((error != 0) && (result != NULL)) {
15101ac4b82bSMike Smith 	free(result, M_DEVBUF);
15111ac4b82bSMike Smith 	result = NULL;
15121ac4b82bSMike Smith     }
15131ac4b82bSMike Smith     return(result);
15141ac4b82bSMike Smith }
15151ac4b82bSMike Smith 
15161ac4b82bSMike Smith 
15171ac4b82bSMike Smith /********************************************************************************
15181ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
15191ac4b82bSMike Smith  *
15201ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
15211ac4b82bSMike Smith  * the flush operation completes or fails.
15221ac4b82bSMike Smith  */
15231ac4b82bSMike Smith static int
15241ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
15251ac4b82bSMike Smith {
15261ac4b82bSMike Smith     struct mlx_command	*mc;
15271ac4b82bSMike Smith     int			error;
15281ac4b82bSMike Smith 
1529da8bb3a3SMike Smith     debug_called(1);
15301ac4b82bSMike Smith 
15311ac4b82bSMike Smith     /* get ourselves a command buffer */
15321ac4b82bSMike Smith     error = 1;
15331ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
15341ac4b82bSMike Smith 	goto out;
15351ac4b82bSMike Smith     /* get a command slot */
15361ac4b82bSMike Smith     if (mlx_getslot(mc))
15371ac4b82bSMike Smith 	goto out;
15381ac4b82bSMike Smith 
15391ac4b82bSMike Smith     /* build a flush command */
15401ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
15411ac4b82bSMike Smith 
15425792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
15435792b7feSMike Smith     if (mlx_poll_command(mc))
15441ac4b82bSMike Smith 	goto out;
15451ac4b82bSMike Smith 
15461ac4b82bSMike Smith     /* command completed OK? */
15471ac4b82bSMike Smith     if (mc->mc_status != 0) {
15481ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
15491ac4b82bSMike Smith 	goto out;
15501ac4b82bSMike Smith     }
15511ac4b82bSMike Smith 
15521ac4b82bSMike Smith     error = 0;			/* success */
15531ac4b82bSMike Smith  out:
15541ac4b82bSMike Smith     if (mc != NULL)
15551ac4b82bSMike Smith 	mlx_releasecmd(mc);
15561ac4b82bSMike Smith     return(error);
15571ac4b82bSMike Smith }
15581ac4b82bSMike Smith 
15591ac4b82bSMike Smith /********************************************************************************
1560421f2f7dSMike Smith  * Start a background consistency check on (drive).
1561421f2f7dSMike Smith  *
1562421f2f7dSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
1563421f2f7dSMike Smith  * operation has started or been refused.
1564421f2f7dSMike Smith  */
1565421f2f7dSMike Smith static int
1566421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive)
1567421f2f7dSMike Smith {
1568421f2f7dSMike Smith     struct mlx_command	*mc;
1569421f2f7dSMike Smith     int			error;
1570421f2f7dSMike Smith 
1571421f2f7dSMike Smith     debug_called(1);
1572421f2f7dSMike Smith 
1573421f2f7dSMike Smith     /* get ourselves a command buffer */
1574421f2f7dSMike Smith     error = 0x10000;
1575421f2f7dSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
1576421f2f7dSMike Smith 	goto out;
1577421f2f7dSMike Smith     /* get a command slot */
1578421f2f7dSMike Smith     if (mlx_getslot(mc))
1579421f2f7dSMike Smith 	goto out;
1580421f2f7dSMike Smith 
1581421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
1582421f2f7dSMike Smith     mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
1583421f2f7dSMike Smith 
1584421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1585421f2f7dSMike Smith     if (mlx_wait_command(mc))
1586421f2f7dSMike Smith 	goto out;
1587421f2f7dSMike Smith 
1588421f2f7dSMike Smith     /* command completed OK? */
1589421f2f7dSMike Smith     if (mc->mc_status != 0) {
1590421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
1591421f2f7dSMike Smith     } else {
1592421f2f7dSMike Smith 	device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
1593421f2f7dSMike Smith     }
1594421f2f7dSMike Smith     error = mc->mc_status;
1595421f2f7dSMike Smith 
1596421f2f7dSMike Smith  out:
1597421f2f7dSMike Smith     if (mc != NULL)
1598421f2f7dSMike Smith 	mlx_releasecmd(mc);
1599421f2f7dSMike Smith     return(error);
1600421f2f7dSMike Smith }
1601421f2f7dSMike Smith 
1602421f2f7dSMike Smith /********************************************************************************
1603421f2f7dSMike Smith  * Start a background rebuild of the physical drive at (channel),(target).
16041ac4b82bSMike Smith  *
16051ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
16061ac4b82bSMike Smith  * operation has started or been refused.
16071ac4b82bSMike Smith  */
16081ac4b82bSMike Smith static int
16091ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
16101ac4b82bSMike Smith {
16111ac4b82bSMike Smith     struct mlx_command	*mc;
16121ac4b82bSMike Smith     int			error;
16131ac4b82bSMike Smith 
1614da8bb3a3SMike Smith     debug_called(1);
16151ac4b82bSMike Smith 
16161ac4b82bSMike Smith     /* get ourselves a command buffer */
16171ac4b82bSMike Smith     error = 0x10000;
16181ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16191ac4b82bSMike Smith 	goto out;
16201ac4b82bSMike Smith     /* get a command slot */
16211ac4b82bSMike Smith     if (mlx_getslot(mc))
16221ac4b82bSMike Smith 	goto out;
16231ac4b82bSMike Smith 
1624421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
16251ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
16261ac4b82bSMike Smith 
1627421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1628421f2f7dSMike Smith     if (mlx_wait_command(mc))
16291ac4b82bSMike Smith 	goto out;
16301ac4b82bSMike Smith 
16311ac4b82bSMike Smith     /* command completed OK? */
16321ac4b82bSMike Smith     if (mc->mc_status != 0) {
16331ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
16341ac4b82bSMike Smith     } else {
1635421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
16361ac4b82bSMike Smith     }
16371ac4b82bSMike Smith     error = mc->mc_status;
16381ac4b82bSMike Smith 
16391ac4b82bSMike Smith  out:
16401ac4b82bSMike Smith     if (mc != NULL)
16411ac4b82bSMike Smith 	mlx_releasecmd(mc);
16421ac4b82bSMike Smith     return(error);
16431ac4b82bSMike Smith }
16441ac4b82bSMike Smith 
16451ac4b82bSMike Smith /********************************************************************************
16461ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
16471ac4b82bSMike Smith  *
16481ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
16491ac4b82bSMike Smith  */
16501ac4b82bSMike Smith static int
16511ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
16521ac4b82bSMike Smith {
16531ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16541ac4b82bSMike Smith     int			error, count;
16551ac4b82bSMike Smith 
1656da8bb3a3SMike Smith     debug_called(1);
16571ac4b82bSMike Smith 
16581ac4b82bSMike Smith     mc->mc_complete = NULL;
16591ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
16601ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
16611ac4b82bSMike Smith 	return(error);
16621ac4b82bSMike Smith 
16631ac4b82bSMike Smith     count = 0;
16641ac4b82bSMike Smith     /* XXX better timeout? */
16651ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
16661ac4b82bSMike Smith 	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
16671ac4b82bSMike Smith     }
16681ac4b82bSMike Smith 
16691ac4b82bSMike Smith     if (mc->mc_status != 0) {
1670da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
16711ac4b82bSMike Smith 	return(EIO);
16721ac4b82bSMike Smith     }
16731ac4b82bSMike Smith     return(0);
16741ac4b82bSMike Smith }
16751ac4b82bSMike Smith 
16761ac4b82bSMike Smith 
16771ac4b82bSMike Smith /********************************************************************************
16781ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
16791ac4b82bSMike Smith  *
1680da8bb3a3SMike Smith  * Should only be used when interrupts can't be relied upon. Returns 0 on
16811ac4b82bSMike Smith  * success, nonzero on error.
16821ac4b82bSMike Smith  * Successfully completed commands are dequeued.
16831ac4b82bSMike Smith  */
16841ac4b82bSMike Smith static int
16851ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
16861ac4b82bSMike Smith {
16871ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16881ac4b82bSMike Smith     int			error, count, s;
16891ac4b82bSMike Smith 
1690da8bb3a3SMike Smith     debug_called(1);
16911ac4b82bSMike Smith 
16921ac4b82bSMike Smith     mc->mc_complete = NULL;
16931ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
16941ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
16951ac4b82bSMike Smith 	return(error);
16961ac4b82bSMike Smith 
16971ac4b82bSMike Smith     count = 0;
16981ac4b82bSMike Smith     do {
16991ac4b82bSMike Smith 	/* poll for completion */
17001ac4b82bSMike Smith 	mlx_done(mc->mc_sc);
1701da8bb3a3SMike Smith 
1702da8bb3a3SMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
17031ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
17041ac4b82bSMike Smith 	s = splbio();
17054b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
17061ac4b82bSMike Smith 	splx(s);
17071ac4b82bSMike Smith 	return(0);
17081ac4b82bSMike Smith     }
1709421f2f7dSMike Smith     device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
17101ac4b82bSMike Smith     return(EIO);
17111ac4b82bSMike Smith }
17121ac4b82bSMike Smith 
17131ac4b82bSMike Smith /********************************************************************************
17141ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
17151ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
17161ac4b82bSMike Smith  *
17171ac4b82bSMike Smith  * Must be called at splbio or in an equivalent fashion that prevents
17188177437dSPoul-Henning Kamp  * reentry or activity on the bioq.
17191ac4b82bSMike Smith  */
17201ac4b82bSMike Smith static void
17211ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
17221ac4b82bSMike Smith {
17231ac4b82bSMike Smith     struct mlx_command	*mc;
17241ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
17258177437dSPoul-Henning Kamp     struct bio		*bp;
17261ac4b82bSMike Smith     int			blkcount;
17271ac4b82bSMike Smith     int			driveno;
17281ac4b82bSMike Smith     int			cmd;
17294b006d7bSMike Smith     int			s;
17301ac4b82bSMike Smith 
17315792b7feSMike Smith     /* avoid reentrancy */
17325792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_STARTING))
17335792b7feSMike Smith 	return;
17345792b7feSMike Smith 
17351ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
17364b006d7bSMike Smith     s = splbio();
17371ac4b82bSMike Smith     for (;;) {
17381ac4b82bSMike Smith 
17391ac4b82bSMike Smith 	/* see if there's work to be done */
17408177437dSPoul-Henning Kamp 	if ((bp = bioq_first(&sc->mlx_bioq)) == NULL)
17411ac4b82bSMike Smith 	    break;
17421ac4b82bSMike Smith 	/* get a command */
17431ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
17441ac4b82bSMike Smith 	    break;
17451ac4b82bSMike Smith 	/* get a slot for the command */
17461ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
17471ac4b82bSMike Smith 	    mlx_releasecmd(mc);
17481ac4b82bSMike Smith 	    break;
17491ac4b82bSMike Smith 	}
17501ac4b82bSMike Smith 	/* get the buf containing our work */
17518177437dSPoul-Henning Kamp 	bioq_remove(&sc->mlx_bioq, bp);
17521ac4b82bSMike Smith 	sc->mlx_waitbufs--;
17534b006d7bSMike Smith 	splx(s);
17541ac4b82bSMike Smith 
17551ac4b82bSMike Smith 	/* connect the buf to the command */
17561ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
17571ac4b82bSMike Smith 	mc->mc_private = bp;
17588177437dSPoul-Henning Kamp 	mc->mc_data = bp->bio_data;
17598177437dSPoul-Henning Kamp 	mc->mc_length = bp->bio_bcount;
17608177437dSPoul-Henning Kamp 	if (bp->bio_cmd == BIO_READ) {
17611ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAIN;
1762da8bb3a3SMike Smith 	    cmd = MLX_CMD_READSG;
17631ac4b82bSMike Smith 	} else {
17641ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAOUT;
1765da8bb3a3SMike Smith 	    cmd = MLX_CMD_WRITESG;
17661ac4b82bSMike Smith 	}
17671ac4b82bSMike Smith 
17681ac4b82bSMike Smith 	/* map the command so the controller can work with it */
17691ac4b82bSMike Smith 	mlx_mapcmd(mc);
17701ac4b82bSMike Smith 
17711ac4b82bSMike Smith 	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
17728177437dSPoul-Henning Kamp 	mlxd = (struct mlxd_softc *)bp->bio_dev->si_drv1;
17735792b7feSMike Smith 	driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
17748177437dSPoul-Henning Kamp 	blkcount = (bp->bio_bcount + MLX_BLKSIZE - 1) / MLX_BLKSIZE;
17751ac4b82bSMike Smith 
17768177437dSPoul-Henning Kamp 	if ((bp->bio_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
17771ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n",
17788177437dSPoul-Henning Kamp 			  bp->bio_pblkno, blkcount, sc->mlx_sysdrive[driveno].ms_size);
17791ac4b82bSMike Smith 
17801ac4b82bSMike Smith 	/*
17811ac4b82bSMike Smith 	 * Build the I/O command.  Note that the SG list type bits are set to zero,
17821ac4b82bSMike Smith 	 * denoting the format of SG list that we are using.
17831ac4b82bSMike Smith 	 */
1784da8bb3a3SMike Smith 	if (sc->mlx_iftype == MLX_IFTYPE_2) {
1785da8bb3a3SMike Smith 	    mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD,
1786da8bb3a3SMike Smith 			   blkcount & 0xff, 				/* xfer length low byte */
17878177437dSPoul-Henning Kamp 			   bp->bio_pblkno,				/* physical block number */
1788da8bb3a3SMike Smith 			   driveno,					/* target drive number */
1789da8bb3a3SMike Smith 			   mc->mc_sgphys,				/* location of SG list */
1790da8bb3a3SMike Smith 			   mc->mc_nsgent & 0x3f);			/* size of SG list (top 3 bits clear) */
1791da8bb3a3SMike Smith 	} else {
17921ac4b82bSMike Smith 	    mlx_make_type5(mc, cmd,
17931ac4b82bSMike Smith 			   blkcount & 0xff, 				/* xfer length low byte */
1794f01f2af6SMike Smith 			   (driveno << 3) | ((blkcount >> 8) & 0x07),	/* target and length high 3 bits */
17958177437dSPoul-Henning Kamp 			   bp->bio_pblkno,				/* physical block number */
17961ac4b82bSMike Smith 			   mc->mc_sgphys,				/* location of SG list */
1797da8bb3a3SMike Smith 			   mc->mc_nsgent & 0x3f);			/* size of SG list (top 3 bits clear) */
1798da8bb3a3SMike Smith 	}
17991ac4b82bSMike Smith 
18001ac4b82bSMike Smith 	/* try to give command to controller */
18011ac4b82bSMike Smith 	if (mlx_start(mc) != 0) {
18021ac4b82bSMike Smith 	    /* fail the command */
18031ac4b82bSMike Smith 	    mc->mc_status = MLX_STATUS_WEDGED;
18041ac4b82bSMike Smith 	    mlx_completeio(mc);
18051ac4b82bSMike Smith 	}
18064b006d7bSMike Smith 	s = splbio();
18071ac4b82bSMike Smith     }
18084b006d7bSMike Smith     splx(s);
18095792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_STARTING);
18101ac4b82bSMike Smith }
18111ac4b82bSMike Smith 
18121ac4b82bSMike Smith /********************************************************************************
18131ac4b82bSMike Smith  * Handle completion of an I/O command.
18141ac4b82bSMike Smith  */
18151ac4b82bSMike Smith static void
18161ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
18171ac4b82bSMike Smith {
18181ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18198177437dSPoul-Henning Kamp     struct bio		*bp = (struct bio *)mc->mc_private;
18208177437dSPoul-Henning Kamp     struct mlxd_softc	*mlxd = (struct mlxd_softc *)bp->bio_dev->si_drv1;
18211ac4b82bSMike Smith 
18221ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
18238177437dSPoul-Henning Kamp 	bp->bio_error = EIO;
18248177437dSPoul-Henning Kamp 	bp->bio_flags |= BIO_ERROR;
18251ac4b82bSMike Smith 
18261ac4b82bSMike Smith 	switch(mc->mc_status) {
18271ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
18281ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1829f6b84b08SMike Smith 	    /* should signal this with a return code */
18301ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
18311ac4b82bSMike Smith 	    break;
18321ac4b82bSMike Smith 
18331ac4b82bSMike Smith 	default:				/* other I/O error */
18341ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
18351ac4b82bSMike Smith #if 0
1836cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
18378177437dSPoul-Henning Kamp 			  bp->bio_bcount, bp->bio_bcount / MLX_BLKSIZE, bp->bio_pblkno);
18381ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
18391ac4b82bSMike Smith #endif
18401ac4b82bSMike Smith 	    break;
18411ac4b82bSMike Smith 	}
18421ac4b82bSMike Smith     }
18431ac4b82bSMike Smith     mlx_releasecmd(mc);
18441ac4b82bSMike Smith     mlxd_intr(bp);
18451ac4b82bSMike Smith }
18461ac4b82bSMike Smith 
18471ac4b82bSMike Smith /********************************************************************************
18481ac4b82bSMike Smith  * Take a command from user-space and try to run it.
1849da8bb3a3SMike Smith  *
1850da8bb3a3SMike Smith  * XXX Note that this can't perform very much in the way of error checking, and
1851da8bb3a3SMike Smith  *     as such, applications _must_ be considered trustworthy.
1852da8bb3a3SMike Smith  * XXX Commands using S/G for data are not supported.
18531ac4b82bSMike Smith  */
18541ac4b82bSMike Smith static int
18551ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
18561ac4b82bSMike Smith {
18571ac4b82bSMike Smith     struct mlx_command	*mc;
1858da8bb3a3SMike Smith     struct mlx_dcdb	*dcdb;
18591ac4b82bSMike Smith     void		*kbuf;
18601ac4b82bSMike Smith     int			error;
18611ac4b82bSMike Smith 
1862da8bb3a3SMike Smith     debug_called(0);
1863da8bb3a3SMike Smith 
18641ac4b82bSMike Smith     kbuf = NULL;
18651ac4b82bSMike Smith     mc = NULL;
1866da8bb3a3SMike Smith     dcdb = NULL;
18671ac4b82bSMike Smith     error = ENOMEM;
1868da8bb3a3SMike Smith 
1869da8bb3a3SMike Smith     /* get ourselves a command and copy in from user space */
18701ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
18711ac4b82bSMike Smith 	goto out;
18721ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
1873da8bb3a3SMike Smith     debug(0, "got command buffer");
1874da8bb3a3SMike Smith 
1875da8bb3a3SMike Smith     /* if we need a buffer for data transfer, allocate one and copy in its initial contents */
1876da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1877da8bb3a3SMike Smith 	if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
1878da8bb3a3SMike Smith 	    (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))
18791ac4b82bSMike Smith 	    goto out;
1880da8bb3a3SMike Smith 	debug(0, "got kernel buffer");
1881da8bb3a3SMike Smith     }
18821ac4b82bSMike Smith 
18831ac4b82bSMike Smith     /* get a command slot */
18841ac4b82bSMike Smith     if (mlx_getslot(mc))
18851ac4b82bSMike Smith 	goto out;
1886da8bb3a3SMike Smith     debug(0, "got a slot");
18871ac4b82bSMike Smith 
18881ac4b82bSMike Smith     /* map the command so the controller can see it */
18891ac4b82bSMike Smith     mc->mc_data = kbuf;
18901ac4b82bSMike Smith     mc->mc_length = mu->mu_datasize;
18911ac4b82bSMike Smith     mlx_mapcmd(mc);
1892da8bb3a3SMike Smith     debug(0, "mapped");
18931ac4b82bSMike Smith 
1894da8bb3a3SMike Smith     /*
1895da8bb3a3SMike Smith      * If this is a passthrough SCSI command, the DCDB is packed at the
1896da8bb3a3SMike Smith      * beginning of the data area.  Fix up the DCDB to point to the correct physical
1897da8bb3a3SMike Smith      * address and override any bufptr supplied by the caller since we know
1898da8bb3a3SMike Smith      * what it's meant to be.
1899da8bb3a3SMike Smith      */
1900da8bb3a3SMike Smith     if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
1901da8bb3a3SMike Smith 	dcdb = (struct mlx_dcdb *)kbuf;
1902da8bb3a3SMike Smith 	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
1903da8bb3a3SMike Smith 	mu->mu_bufptr = 8;
19041ac4b82bSMike Smith     }
19051ac4b82bSMike Smith 
1906da8bb3a3SMike Smith     /*
1907da8bb3a3SMike Smith      * If there's a data buffer, fix up the command's buffer pointer.
1908da8bb3a3SMike Smith      */
1909da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1910da8bb3a3SMike Smith 
1911da8bb3a3SMike Smith 	/* range check the pointer to physical buffer address */
1912da8bb3a3SMike Smith 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - sizeof(u_int32_t)))) {
1913da8bb3a3SMike Smith 	    error = EINVAL;
1914da8bb3a3SMike Smith 	    goto out;
1915da8bb3a3SMike Smith 	}
1916da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
1917da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
1918da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
1919da8bb3a3SMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
1920da8bb3a3SMike Smith     }
1921da8bb3a3SMike Smith     debug(0, "command fixup");
1922da8bb3a3SMike Smith 
19231ac4b82bSMike Smith     /* submit the command and wait */
19241ac4b82bSMike Smith     if ((error = mlx_wait_command(mc)) != 0)
19251ac4b82bSMike Smith 	goto out;
19261ac4b82bSMike Smith 
19271ac4b82bSMike Smith     /* copy out status and data */
19281ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
19291ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
19301ac4b82bSMike Smith 	goto out;
19311ac4b82bSMike Smith     error = 0;
19321ac4b82bSMike Smith 
19331ac4b82bSMike Smith  out:
19341ac4b82bSMike Smith     mlx_releasecmd(mc);
19351ac4b82bSMike Smith     if (kbuf != NULL)
19361ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
19371ac4b82bSMike Smith     return(error);
19381ac4b82bSMike Smith }
19391ac4b82bSMike Smith 
19401ac4b82bSMike Smith /********************************************************************************
19411ac4b82bSMike Smith  ********************************************************************************
19421ac4b82bSMike Smith                                                         Command I/O to Controller
19431ac4b82bSMike Smith  ********************************************************************************
19441ac4b82bSMike Smith  ********************************************************************************/
19451ac4b82bSMike Smith 
19461ac4b82bSMike Smith /********************************************************************************
19471ac4b82bSMike Smith  * Find a free command slot for (mc).
19481ac4b82bSMike Smith  *
19491ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
19501ac4b82bSMike Smith  * 4 slots free for priority commands.
19511ac4b82bSMike Smith  */
19521ac4b82bSMike Smith static int
19531ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
19541ac4b82bSMike Smith {
19551ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
19569eee27f1SMike Smith     int			s, slot;
19571ac4b82bSMike Smith 
1958da8bb3a3SMike Smith     debug_called(1);
19591ac4b82bSMike Smith 
19601ac4b82bSMike Smith     /* enforce slot-usage limit */
19619eee27f1SMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ?
19629eee27f1SMike Smith 			     sc->mlx_maxiop : sc->mlx_maxiop - 4))
19631ac4b82bSMike Smith 	return(EBUSY);
19641ac4b82bSMike Smith 
19651ac4b82bSMike Smith     /*
19661ac4b82bSMike Smith      * Allocate an outstanding command slot
19671ac4b82bSMike Smith      *
19681ac4b82bSMike Smith      * XXX linear search is slow
19691ac4b82bSMike Smith      */
19701ac4b82bSMike Smith     s = splbio();
19711ac4b82bSMike Smith     for (slot = 0; slot < sc->mlx_maxiop; slot++) {
1972da8bb3a3SMike Smith 	debug(2, "try slot %d", slot);
19731ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
19741ac4b82bSMike Smith 	    break;
19751ac4b82bSMike Smith     }
19761ac4b82bSMike Smith     if (slot < sc->mlx_maxiop) {
19771ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
19781ac4b82bSMike Smith 	sc->mlx_busycmds++;
19791ac4b82bSMike Smith     }
19801ac4b82bSMike Smith     splx(s);
19811ac4b82bSMike Smith 
19821ac4b82bSMike Smith     /* out of slots? */
19831ac4b82bSMike Smith     if (slot >= sc->mlx_maxiop)
19841ac4b82bSMike Smith 	return(EBUSY);
19851ac4b82bSMike Smith 
1986da8bb3a3SMike Smith     debug(2, "got slot %d", slot);
19871ac4b82bSMike Smith     mc->mc_slot = slot;
19881ac4b82bSMike Smith     return(0);
19891ac4b82bSMike Smith }
19901ac4b82bSMike Smith 
19911ac4b82bSMike Smith /********************************************************************************
19921ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
19931ac4b82bSMike Smith  */
19941ac4b82bSMike Smith static void
19951ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
19961ac4b82bSMike Smith {
19971ac4b82bSMike Smith     struct mlx_command	*mc = (struct mlx_command *)arg;
19981ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
19991ac4b82bSMike Smith     struct mlx_sgentry	*sg;
20001ac4b82bSMike Smith     int			i;
20011ac4b82bSMike Smith 
2002da8bb3a3SMike Smith     debug_called(1);
20031ac4b82bSMike Smith 
20041ac4b82bSMike Smith     /* get base address of s/g table */
2005da8bb3a3SMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * sc->mlx_sg_nseg);
20061ac4b82bSMike Smith 
20071ac4b82bSMike Smith     /* save s/g table information in command */
20081ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
2009da8bb3a3SMike Smith     mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * sc->mlx_sg_nseg * sizeof(struct mlx_sgentry));
20101ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
20111ac4b82bSMike Smith 
20121ac4b82bSMike Smith     /* populate s/g table */
20131ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
20141ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
20151ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
20161ac4b82bSMike Smith     }
20171ac4b82bSMike Smith }
20181ac4b82bSMike Smith 
20191ac4b82bSMike Smith static void
20201ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc)
20211ac4b82bSMike Smith {
20221ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20231ac4b82bSMike Smith 
2024da8bb3a3SMike Smith     debug_called(1);
20251ac4b82bSMike Smith 
20261ac4b82bSMike Smith     /* if the command involves data at all */
20271ac4b82bSMike Smith     if (mc->mc_data != NULL) {
20281ac4b82bSMike Smith 
20291ac4b82bSMike Smith 	/* map the data buffer into bus space and build the s/g list */
20301ac4b82bSMike Smith 	bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length,
20311ac4b82bSMike Smith 			mlx_setup_dmamap, mc, 0);
20321ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
20331ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD);
20341ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
20351ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE);
20361ac4b82bSMike Smith     }
20371ac4b82bSMike Smith }
20381ac4b82bSMike Smith 
20391ac4b82bSMike Smith static void
20401ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
20411ac4b82bSMike Smith {
20421ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20431ac4b82bSMike Smith 
2044da8bb3a3SMike Smith     debug_called(1);
20451ac4b82bSMike Smith 
20461ac4b82bSMike Smith     /* if the command involved data at all */
20471ac4b82bSMike Smith     if (mc->mc_data != NULL) {
20481ac4b82bSMike Smith 
20491ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
20501ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
20511ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
20521ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
20531ac4b82bSMike Smith 
20541ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
20551ac4b82bSMike Smith     }
20561ac4b82bSMike Smith }
20571ac4b82bSMike Smith 
20581ac4b82bSMike Smith /********************************************************************************
20595792b7feSMike Smith  * Try to deliver (mc) to the controller.
20601ac4b82bSMike Smith  *
20611ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
20621ac4b82bSMike Smith  */
20631ac4b82bSMike Smith static int
20641ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
20651ac4b82bSMike Smith {
20661ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20675792b7feSMike Smith     int			i, s, done;
20681ac4b82bSMike Smith 
2069da8bb3a3SMike Smith     debug_called(1);
20701ac4b82bSMike Smith 
20711ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
20721ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
20731ac4b82bSMike Smith 
20744b006d7bSMike Smith     /* mark the command as currently being processed */
20751ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
20761ac4b82bSMike Smith 
20775792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
20785792b7feSMike Smith     mc->mc_timeout = time_second + 60;
20791ac4b82bSMike Smith 
20801ac4b82bSMike Smith     /* spin waiting for the mailbox */
20811ac4b82bSMike Smith     for (i = 100000, done = 0; (i > 0) && !done; i--) {
20821ac4b82bSMike Smith 	s = splbio();
20834b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
20844b006d7bSMike Smith 	    done = 1;
20854b006d7bSMike Smith 	    /* move command to work queue */
20864b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
20874b006d7bSMike Smith 	}
20885792b7feSMike Smith 	splx(s);	/* drop spl to allow completion interrupts */
20891ac4b82bSMike Smith     }
20901ac4b82bSMike Smith 
20911ac4b82bSMike Smith     /* command is enqueued */
20921ac4b82bSMike Smith     if (done)
20931ac4b82bSMike Smith 	return(0);
20941ac4b82bSMike Smith 
20951ac4b82bSMike Smith     /*
20961ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
20971ac4b82bSMike Smith      * that the command was given and return it with a bad status.
20981ac4b82bSMike Smith      */
20991ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
21001ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
21011ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
21025792b7feSMike Smith     mlx_complete(sc);
21031ac4b82bSMike Smith     return(EIO);
21041ac4b82bSMike Smith }
21051ac4b82bSMike Smith 
21061ac4b82bSMike Smith /********************************************************************************
21075792b7feSMike Smith  * Poll the controller (sc) for completed commands.
21085792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
21095792b7feSMike Smith  * new commands may be posted.
21101ac4b82bSMike Smith  *
21115792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
21121ac4b82bSMike Smith  */
21131ac4b82bSMike Smith static int
21141ac4b82bSMike Smith mlx_done(struct mlx_softc *sc)
21151ac4b82bSMike Smith {
21161ac4b82bSMike Smith     struct mlx_command	*mc;
21175792b7feSMike Smith     int			s, result;
21181ac4b82bSMike Smith     u_int8_t		slot;
21191ac4b82bSMike Smith     u_int16_t		status;
21201ac4b82bSMike Smith 
2121da8bb3a3SMike Smith     debug_called(2);
21221ac4b82bSMike Smith 
21235792b7feSMike Smith     result = 0;
21241ac4b82bSMike Smith 
21255792b7feSMike Smith     /* loop collecting completed commands */
21264b006d7bSMike Smith     s = splbio();
21275792b7feSMike Smith     for (;;) {
21285792b7feSMike Smith 	/* poll for a completed command's identifier and status */
21291ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
21305792b7feSMike Smith 	    result = 1;
21311ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
21321ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
21331ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
21341ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
21351ac4b82bSMike Smith 
21361ac4b82bSMike Smith 		    /* free slot for reuse */
21371ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
21381ac4b82bSMike Smith 		    sc->mlx_busycmds--;
21391ac4b82bSMike Smith 		} else {
21401ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
21411ac4b82bSMike Smith 		}
21421ac4b82bSMike Smith 	    } else {
21431ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
21441ac4b82bSMike Smith 	    }
21455792b7feSMike Smith 	} else {
21465792b7feSMike Smith 	    break;
21471ac4b82bSMike Smith 	}
21485792b7feSMike Smith     }
21491ac4b82bSMike Smith 
21505792b7feSMike Smith     /* if we've completed any commands, try posting some more */
21515792b7feSMike Smith     if (result)
21525792b7feSMike Smith 	mlx_startio(sc);
21535792b7feSMike Smith 
21545792b7feSMike Smith     /* handle completion and timeouts */
21555792b7feSMike Smith     mlx_complete(sc);
21565792b7feSMike Smith 
21575792b7feSMike Smith     return(result);
21581ac4b82bSMike Smith }
21591ac4b82bSMike Smith 
21601ac4b82bSMike Smith /********************************************************************************
21615792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
21621ac4b82bSMike Smith  */
21631ac4b82bSMike Smith static void
21641ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
21651ac4b82bSMike Smith {
21661ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
21671ac4b82bSMike Smith     int			s, count;
21681ac4b82bSMike Smith 
2169da8bb3a3SMike Smith     debug_called(2);
21701ac4b82bSMike Smith 
21715792b7feSMike Smith     /* avoid reentrancy  XXX might want to signal and request a restart */
21725792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
21735792b7feSMike Smith 	return;
21745792b7feSMike Smith 
21751ac4b82bSMike Smith     s = splbio();
21761ac4b82bSMike Smith     count = 0;
21771ac4b82bSMike Smith 
21785792b7feSMike Smith     /* scan the list of busy/done commands */
21794b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
21801ac4b82bSMike Smith     while (mc != NULL) {
21811ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
21821ac4b82bSMike Smith 
21835792b7feSMike Smith 	/* Command has been completed in some fashion */
21844b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
21854b006d7bSMike Smith 
21865792b7feSMike Smith 	    /* unmap the command's data buffer */
21875792b7feSMike Smith 	    mlx_unmapcmd(mc);
21881ac4b82bSMike Smith 	    /*
21891ac4b82bSMike Smith 	     * Does the command have a completion handler?
21901ac4b82bSMike Smith 	     */
21911ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
21921ac4b82bSMike Smith 		/* remove from list and give to handler */
21934b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
21941ac4b82bSMike Smith 		mc->mc_complete(mc);
21951ac4b82bSMike Smith 
21961ac4b82bSMike Smith 		/*
21971ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
21981ac4b82bSMike Smith 		 */
21991ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
22001ac4b82bSMike Smith 
22011ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
22024b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
22031ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
22041ac4b82bSMike Smith 
22051ac4b82bSMike Smith 		/*
22061ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
22071ac4b82bSMike Smith 		 */
22081ac4b82bSMike Smith 	    } else {
22091ac4b82bSMike Smith 	    }
22104b006d7bSMike Smith 	}
22111ac4b82bSMike Smith 	mc = nc;
22121ac4b82bSMike Smith     }
22131ac4b82bSMike Smith     splx(s);
22141ac4b82bSMike Smith 
22155792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_COMPLETING);
22161ac4b82bSMike Smith }
22171ac4b82bSMike Smith 
22181ac4b82bSMike Smith /********************************************************************************
22191ac4b82bSMike Smith  ********************************************************************************
22201ac4b82bSMike Smith                                                         Command Buffer Management
22211ac4b82bSMike Smith  ********************************************************************************
22221ac4b82bSMike Smith  ********************************************************************************/
22231ac4b82bSMike Smith 
22241ac4b82bSMike Smith /********************************************************************************
22251ac4b82bSMike Smith  * Get a new command buffer.
22261ac4b82bSMike Smith  *
22271ac4b82bSMike Smith  * This may return NULL in low-memory cases.
22281ac4b82bSMike Smith  *
22291ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
22301ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
22311ac4b82bSMike Smith  *
22321ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
22331ac4b82bSMike Smith  *
22341ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
22351ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
22361ac4b82bSMike Smith  *     using a buffer.
22371ac4b82bSMike Smith  */
22381ac4b82bSMike Smith static struct mlx_command *
22391ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
22401ac4b82bSMike Smith {
22411ac4b82bSMike Smith     struct mlx_command	*mc;
22421ac4b82bSMike Smith     int			error;
22431ac4b82bSMike Smith     int			s;
22441ac4b82bSMike Smith 
2245da8bb3a3SMike Smith     debug_called(1);
22461ac4b82bSMike Smith 
22471ac4b82bSMike Smith     s = splbio();
22481ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
22491ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
22501ac4b82bSMike Smith     splx(s);
22511ac4b82bSMike Smith 
22521ac4b82bSMike Smith     /* allocate a new command buffer? */
22531ac4b82bSMike Smith     if (mc == NULL) {
22541ac4b82bSMike Smith 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT);
22551ac4b82bSMike Smith 	if (mc != NULL) {
22561ac4b82bSMike Smith 	    bzero(mc, sizeof(*mc));
22571ac4b82bSMike Smith 	    mc->mc_sc = sc;
22581ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
22591ac4b82bSMike Smith 	    if (error) {
22601ac4b82bSMike Smith 		free(mc, M_DEVBUF);
22611ac4b82bSMike Smith 		return(NULL);
22621ac4b82bSMike Smith 	    }
22631ac4b82bSMike Smith 	}
22641ac4b82bSMike Smith     }
22651ac4b82bSMike Smith     return(mc);
22661ac4b82bSMike Smith }
22671ac4b82bSMike Smith 
22681ac4b82bSMike Smith /********************************************************************************
22691ac4b82bSMike Smith  * Release a command buffer for recycling.
22701ac4b82bSMike Smith  *
22711ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
22721ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
22731ac4b82bSMike Smith  */
22741ac4b82bSMike Smith static void
22751ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
22761ac4b82bSMike Smith {
22771ac4b82bSMike Smith     int		s;
22781ac4b82bSMike Smith 
2279da8bb3a3SMike Smith     debug_called(1);
22801ac4b82bSMike Smith 
22811ac4b82bSMike Smith     s = splbio();
22821ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
22831ac4b82bSMike Smith     splx(s);
22841ac4b82bSMike Smith }
22851ac4b82bSMike Smith 
22861ac4b82bSMike Smith /********************************************************************************
22871ac4b82bSMike Smith  * Permanently discard a command buffer.
22881ac4b82bSMike Smith  */
22891ac4b82bSMike Smith static void
22901ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
22911ac4b82bSMike Smith {
22921ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
22931ac4b82bSMike Smith 
2294da8bb3a3SMike Smith     debug_called(1);
22951ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
22961ac4b82bSMike Smith     free(mc, M_DEVBUF);
22971ac4b82bSMike Smith }
22981ac4b82bSMike Smith 
22991ac4b82bSMike Smith 
23001ac4b82bSMike Smith /********************************************************************************
23011ac4b82bSMike Smith  ********************************************************************************
23021ac4b82bSMike Smith                                                 Type 3 interface accessor methods
23031ac4b82bSMike Smith  ********************************************************************************
23041ac4b82bSMike Smith  ********************************************************************************/
23051ac4b82bSMike Smith 
23061ac4b82bSMike Smith /********************************************************************************
23071ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
23081ac4b82bSMike Smith  * (the controller is not ready to take a command).
23091ac4b82bSMike Smith  *
23101ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23111ac4b82bSMike Smith  */
23121ac4b82bSMike Smith static int
23131ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
23141ac4b82bSMike Smith {
23151ac4b82bSMike Smith     int		i;
23161ac4b82bSMike Smith 
2317da8bb3a3SMike Smith     debug_called(2);
23181ac4b82bSMike Smith 
23191ac4b82bSMike Smith     /* ready for our command? */
23201ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
23211ac4b82bSMike Smith 	/* copy mailbox data to window */
23221ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
23231ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
23241ac4b82bSMike Smith 
23251ac4b82bSMike Smith 	/* post command */
2326f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
23271ac4b82bSMike Smith 	return(1);
23281ac4b82bSMike Smith     }
23291ac4b82bSMike Smith     return(0);
23301ac4b82bSMike Smith }
23311ac4b82bSMike Smith 
23321ac4b82bSMike Smith /********************************************************************************
23331ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
23341ac4b82bSMike Smith  * and recover the slot number and status code.
23351ac4b82bSMike Smith  *
23361ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23371ac4b82bSMike Smith  */
23381ac4b82bSMike Smith static int
23391ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
23401ac4b82bSMike Smith {
23411ac4b82bSMike Smith 
2342da8bb3a3SMike Smith     debug_called(2);
23431ac4b82bSMike Smith 
23441ac4b82bSMike Smith     /* status available? */
23451ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
23461ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
23471ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
23481ac4b82bSMike Smith 
23491ac4b82bSMike Smith 	/* acknowledge completion */
2350f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2351f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
23521ac4b82bSMike Smith 	return(1);
23531ac4b82bSMike Smith     }
23541ac4b82bSMike Smith     return(0);
23551ac4b82bSMike Smith }
23561ac4b82bSMike Smith 
23571ac4b82bSMike Smith /********************************************************************************
23581ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
23591ac4b82bSMike Smith  *
23601ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23611ac4b82bSMike Smith  */
23621ac4b82bSMike Smith static void
23631ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
23641ac4b82bSMike Smith {
2365da8bb3a3SMike Smith     debug_called(1);
23661ac4b82bSMike Smith 
23671ac4b82bSMike Smith     switch(action) {
23681ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
23691ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
23701ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
23711ac4b82bSMike Smith 	break;
23721ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
23731ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
23741ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
23751ac4b82bSMike Smith 	break;
23761ac4b82bSMike Smith     }
23771ac4b82bSMike Smith }
23781ac4b82bSMike Smith 
2379da8bb3a3SMike Smith /********************************************************************************
2380da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2381da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2382da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2383da8bb3a3SMike Smith  */
2384da8bb3a3SMike Smith static int
2385da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2386da8bb3a3SMike Smith {
2387da8bb3a3SMike Smith     u_int8_t	fwerror;
2388da8bb3a3SMike Smith     static int	initted = 0;
2389da8bb3a3SMike Smith 
2390da8bb3a3SMike Smith     debug_called(2);
2391da8bb3a3SMike Smith 
2392da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2393da8bb3a3SMike Smith     if (!initted) {
2394da8bb3a3SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
2395da8bb3a3SMike Smith 	DELAY(1000);
2396da8bb3a3SMike Smith 	initted = 1;
2397da8bb3a3SMike Smith     }
2398da8bb3a3SMike Smith 
2399da8bb3a3SMike Smith     /* init in progress? */
2400da8bb3a3SMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
2401da8bb3a3SMike Smith 	return(0);
2402da8bb3a3SMike Smith 
2403da8bb3a3SMike Smith     /* test error value */
2404da8bb3a3SMike Smith     fwerror = MLX_V3_GET_FWERROR(sc);
2405da8bb3a3SMike Smith     if (!(fwerror & MLX_V3_FWERROR_PEND))
2406da8bb3a3SMike Smith 	return(1);
2407da8bb3a3SMike Smith 
2408da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2409da8bb3a3SMike Smith     *error = fwerror & ~MLX_V3_FWERROR_PEND;
2410da8bb3a3SMike Smith     *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
2411da8bb3a3SMike Smith     *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
2412da8bb3a3SMike Smith 
2413da8bb3a3SMike Smith     /* acknowledge */
2414da8bb3a3SMike Smith     MLX_V3_PUT_FWERROR(sc, 0);
2415da8bb3a3SMike Smith 
2416da8bb3a3SMike Smith     return(2);
2417da8bb3a3SMike Smith }
24181ac4b82bSMike Smith 
24191ac4b82bSMike Smith /********************************************************************************
24201ac4b82bSMike Smith  ********************************************************************************
2421f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2422f6b84b08SMike Smith  ********************************************************************************
2423f6b84b08SMike Smith  ********************************************************************************/
2424f6b84b08SMike Smith 
2425f6b84b08SMike Smith /********************************************************************************
2426f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2427f6b84b08SMike Smith  * (the controller is not ready to take a command).
2428f6b84b08SMike Smith  *
2429f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2430f6b84b08SMike Smith  */
2431f6b84b08SMike Smith static int
2432f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2433f6b84b08SMike Smith {
2434f6b84b08SMike Smith     int		i;
2435f6b84b08SMike Smith 
2436da8bb3a3SMike Smith     debug_called(2);
2437f6b84b08SMike Smith 
2438f6b84b08SMike Smith     /* ready for our command? */
2439f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2440f6b84b08SMike Smith 	/* copy mailbox data to window */
2441f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2442f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2443f6b84b08SMike Smith 
2444da8bb3a3SMike Smith 	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
2445da8bb3a3SMike Smith 	bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
2446da8bb3a3SMike Smith 			  BUS_SPACE_BARRIER_WRITE);
2447da8bb3a3SMike Smith 
2448f6b84b08SMike Smith 	/* post command */
2449f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2450f6b84b08SMike Smith 	return(1);
2451f6b84b08SMike Smith     }
2452f6b84b08SMike Smith     return(0);
2453f6b84b08SMike Smith }
2454f6b84b08SMike Smith 
2455f6b84b08SMike Smith /********************************************************************************
2456f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2457f6b84b08SMike Smith  * and recover the slot number and status code.
2458f6b84b08SMike Smith  *
2459f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2460f6b84b08SMike Smith  */
2461f6b84b08SMike Smith static int
2462f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2463f6b84b08SMike Smith {
2464f6b84b08SMike Smith 
2465da8bb3a3SMike Smith     debug_called(2);
2466f6b84b08SMike Smith 
2467f6b84b08SMike Smith     /* status available? */
2468f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2469f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2470f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2471f6b84b08SMike Smith 
2472f6b84b08SMike Smith 	/* acknowledge completion */
2473f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2474f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2475f6b84b08SMike Smith 	return(1);
2476f6b84b08SMike Smith     }
2477f6b84b08SMike Smith     return(0);
2478f6b84b08SMike Smith }
2479f6b84b08SMike Smith 
2480f6b84b08SMike Smith /********************************************************************************
2481f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2482f6b84b08SMike Smith  *
2483f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2484f6b84b08SMike Smith  */
2485f6b84b08SMike Smith static void
2486f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2487f6b84b08SMike Smith {
2488da8bb3a3SMike Smith     debug_called(1);
2489f6b84b08SMike Smith 
2490f6b84b08SMike Smith     switch(action) {
2491f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2492f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2493f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2494f6b84b08SMike Smith 	break;
2495f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2496f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2497f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2498f6b84b08SMike Smith 	break;
2499f6b84b08SMike Smith     }
2500f6b84b08SMike Smith }
2501f6b84b08SMike Smith 
2502da8bb3a3SMike Smith /********************************************************************************
2503da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2504da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2505da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2506da8bb3a3SMike Smith  */
2507da8bb3a3SMike Smith static int
2508da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2509da8bb3a3SMike Smith {
2510da8bb3a3SMike Smith     u_int8_t	fwerror;
2511da8bb3a3SMike Smith     static int	initted = 0;
2512da8bb3a3SMike Smith 
2513da8bb3a3SMike Smith     debug_called(2);
2514da8bb3a3SMike Smith 
2515da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2516da8bb3a3SMike Smith     if (!initted) {
2517da8bb3a3SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2518da8bb3a3SMike Smith 	DELAY(1000);
2519da8bb3a3SMike Smith 	initted = 1;
2520da8bb3a3SMike Smith     }
2521da8bb3a3SMike Smith 
2522da8bb3a3SMike Smith     /* init in progress? */
2523da8bb3a3SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
2524da8bb3a3SMike Smith 	return(0);
2525da8bb3a3SMike Smith 
2526da8bb3a3SMike Smith     /* test error value */
2527da8bb3a3SMike Smith     fwerror = MLX_V4_GET_FWERROR(sc);
2528da8bb3a3SMike Smith     if (!(fwerror & MLX_V4_FWERROR_PEND))
2529da8bb3a3SMike Smith 	return(1);
2530da8bb3a3SMike Smith 
2531da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2532da8bb3a3SMike Smith     *error = fwerror & ~MLX_V4_FWERROR_PEND;
2533da8bb3a3SMike Smith     *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
2534da8bb3a3SMike Smith     *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
2535da8bb3a3SMike Smith 
2536da8bb3a3SMike Smith     /* acknowledge */
2537da8bb3a3SMike Smith     MLX_V4_PUT_FWERROR(sc, 0);
2538da8bb3a3SMike Smith 
2539da8bb3a3SMike Smith     return(2);
2540da8bb3a3SMike Smith }
2541f6b84b08SMike Smith 
2542f6b84b08SMike Smith /********************************************************************************
2543f6b84b08SMike Smith  ********************************************************************************
25445792b7feSMike Smith                                                 Type 5 interface accessor methods
25455792b7feSMike Smith  ********************************************************************************
25465792b7feSMike Smith  ********************************************************************************/
25475792b7feSMike Smith 
25485792b7feSMike Smith /********************************************************************************
25495792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
25505792b7feSMike Smith  * (the controller is not ready to take a command).
25515792b7feSMike Smith  *
25525792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
25535792b7feSMike Smith  */
25545792b7feSMike Smith static int
25555792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
25565792b7feSMike Smith {
25575792b7feSMike Smith     int		i;
25585792b7feSMike Smith 
2559da8bb3a3SMike Smith     debug_called(2);
25605792b7feSMike Smith 
25615792b7feSMike Smith     /* ready for our command? */
25625792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
25635792b7feSMike Smith 	/* copy mailbox data to window */
25645792b7feSMike Smith 	for (i = 0; i < 13; i++)
25655792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
25665792b7feSMike Smith 
25675792b7feSMike Smith 	/* post command */
25685792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
25695792b7feSMike Smith 	return(1);
25705792b7feSMike Smith     }
25715792b7feSMike Smith     return(0);
25725792b7feSMike Smith }
25735792b7feSMike Smith 
25745792b7feSMike Smith /********************************************************************************
25755792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
25765792b7feSMike Smith  * and recover the slot number and status code.
25775792b7feSMike Smith  *
25785792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
25795792b7feSMike Smith  */
25805792b7feSMike Smith static int
25815792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
25825792b7feSMike Smith {
25835792b7feSMike Smith 
2584da8bb3a3SMike Smith     debug_called(2);
25855792b7feSMike Smith 
25865792b7feSMike Smith     /* status available? */
25875792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
25885792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
25895792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
25905792b7feSMike Smith 
25915792b7feSMike Smith 	/* acknowledge completion */
25925792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
25935792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
25945792b7feSMike Smith 	return(1);
25955792b7feSMike Smith     }
25965792b7feSMike Smith     return(0);
25975792b7feSMike Smith }
25985792b7feSMike Smith 
25995792b7feSMike Smith /********************************************************************************
26005792b7feSMike Smith  * Enable/disable interrupts as requested.
26015792b7feSMike Smith  *
26025792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
26035792b7feSMike Smith  */
26045792b7feSMike Smith static void
26055792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
26065792b7feSMike Smith {
2607da8bb3a3SMike Smith     debug_called(1);
26085792b7feSMike Smith 
26095792b7feSMike Smith     switch(action) {
26105792b7feSMike Smith     case MLX_INTACTION_DISABLE:
2611da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
26125792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
26135792b7feSMike Smith 	break;
26145792b7feSMike Smith     case MLX_INTACTION_ENABLE:
2615da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
26165792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
26175792b7feSMike Smith 	break;
26185792b7feSMike Smith     }
26195792b7feSMike Smith }
26205792b7feSMike Smith 
2621da8bb3a3SMike Smith /********************************************************************************
2622da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2623da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2624da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2625da8bb3a3SMike Smith  */
2626da8bb3a3SMike Smith static int
2627da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2628da8bb3a3SMike Smith {
2629da8bb3a3SMike Smith     u_int8_t	fwerror;
2630da8bb3a3SMike Smith     static int	initted = 0;
2631da8bb3a3SMike Smith 
2632da8bb3a3SMike Smith     debug_called(2);
2633da8bb3a3SMike Smith 
2634da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2635da8bb3a3SMike Smith     if (!initted) {
2636da8bb3a3SMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
2637da8bb3a3SMike Smith 	DELAY(1000);
2638da8bb3a3SMike Smith 	initted = 1;
2639da8bb3a3SMike Smith     }
2640da8bb3a3SMike Smith 
2641da8bb3a3SMike Smith     /* init in progress? */
2642da8bb3a3SMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
2643da8bb3a3SMike Smith 	return(0);
2644da8bb3a3SMike Smith 
2645da8bb3a3SMike Smith     /* test for error value */
2646da8bb3a3SMike Smith     fwerror = MLX_V5_GET_FWERROR(sc);
2647da8bb3a3SMike Smith     if (!(fwerror & MLX_V5_FWERROR_PEND))
2648da8bb3a3SMike Smith 	return(1);
2649da8bb3a3SMike Smith 
2650da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2651da8bb3a3SMike Smith     *error = fwerror & ~MLX_V5_FWERROR_PEND;
2652da8bb3a3SMike Smith     *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
2653da8bb3a3SMike Smith     *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
2654da8bb3a3SMike Smith 
2655da8bb3a3SMike Smith     /* acknowledge */
2656da8bb3a3SMike Smith     MLX_V5_PUT_FWERROR(sc, 0xff);
2657da8bb3a3SMike Smith 
2658da8bb3a3SMike Smith     return(2);
2659da8bb3a3SMike Smith }
26605792b7feSMike Smith 
26615792b7feSMike Smith /********************************************************************************
26625792b7feSMike Smith  ********************************************************************************
26631ac4b82bSMike Smith                                                                         Debugging
26641ac4b82bSMike Smith  ********************************************************************************
26651ac4b82bSMike Smith  ********************************************************************************/
26661ac4b82bSMike Smith 
26671ac4b82bSMike Smith /********************************************************************************
26681ac4b82bSMike Smith  * Return a status message describing (mc)
26691ac4b82bSMike Smith  */
26701ac4b82bSMike Smith static char *mlx_status_messages[] = {
26711ac4b82bSMike Smith     "normal completion",			/* 00 */
26721ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
26731ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
26741ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
26751ac4b82bSMike Smith     "bad data encountered",			/* 04 */
26761ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
26771ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
26781ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
26791ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
26801ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
26811ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
26821ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
26831ac4b82bSMike Smith     "channel is busy",				/* 12 */
26841ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
2685da8bb3a3SMike Smith     "rebuild successfully terminated",		/* 14 */
2686da8bb3a3SMike Smith     "unsupported command",			/* 15 */
2687da8bb3a3SMike Smith     "check condition received",			/* 16 */
2688da8bb3a3SMike Smith     "device is busy",				/* 17 */
2689da8bb3a3SMike Smith     "selection or command timeout",		/* 18 */
2690da8bb3a3SMike Smith     "command terminated abnormally",		/* 19 */
2691da8bb3a3SMike Smith     ""
26921ac4b82bSMike Smith };
26931ac4b82bSMike Smith 
26941ac4b82bSMike Smith static struct
26951ac4b82bSMike Smith {
26961ac4b82bSMike Smith     int		command;
26971ac4b82bSMike Smith     u_int16_t	status;
26981ac4b82bSMike Smith     int		msg;
26991ac4b82bSMike Smith } mlx_messages[] = {
2700da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0001,	 1},
2701da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0002,	 1},
2702da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0105,	 3},
2703da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x010c,	 4},
2704da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0001,	 1},
2705da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0002,	 1},
2706da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0105,	 3},
2707da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0001,	 1},
2708da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0002,	 1},
2709da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0105,	 3},
2710da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
2711da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
2712da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
27131ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
27141ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
27151ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
27161ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
27171ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
2718da8bb3a3SMike Smith     {MLX_CMD_REBUILDASYNC,	0x0107, 14},
27191ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
27201ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
27211ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
27221ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
27231ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
27241ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
27251ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
2726da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0002, 16},
2727da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0008, 17},
2728da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000e, 18},
2729da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000f, 19},
2730da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0105,  8},
2731da8bb3a3SMike Smith 
2732da8bb3a3SMike Smith     {0,				0x0104, 14},
27331ac4b82bSMike Smith     {-1, 0, 0}
27341ac4b82bSMike Smith };
27351ac4b82bSMike Smith 
27361ac4b82bSMike Smith static char *
27371ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
27381ac4b82bSMike Smith {
27391ac4b82bSMike Smith     static char	unkmsg[80];
27401ac4b82bSMike Smith     int		i;
27411ac4b82bSMike Smith 
27421ac4b82bSMike Smith     /* look up message in table */
27431ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
2744da8bb3a3SMike Smith 	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
2745466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
27461ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
27471ac4b82bSMike Smith 
27481ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
27491ac4b82bSMike Smith     return(unkmsg);
27501ac4b82bSMike Smith }
27511ac4b82bSMike Smith 
27521ac4b82bSMike Smith /*******************************************************************************
2753da8bb3a3SMike Smith  * Print a string describing the controller (sc)
27541ac4b82bSMike Smith  */
27555792b7feSMike Smith static struct
27565792b7feSMike Smith {
27575792b7feSMike Smith     int		hwid;
27585792b7feSMike Smith     char	*name;
27595792b7feSMike Smith } mlx_controller_names[] = {
27605792b7feSMike Smith     {0x01,	"960P/PD"},
27615792b7feSMike Smith     {0x02,	"960PL"},
27625792b7feSMike Smith     {0x10,	"960PG"},
27635792b7feSMike Smith     {0x11,	"960PJ"},
27649eee27f1SMike Smith     {0x12,	"960PR"},
27659eee27f1SMike Smith     {0x13,	"960PT"},
27669eee27f1SMike Smith     {0x14,	"960PTL0"},
27679eee27f1SMike Smith     {0x15,	"960PRL"},
27689eee27f1SMike Smith     {0x16,	"960PTL1"},
27699eee27f1SMike Smith     {0x20,	"1164PVX"},
27705792b7feSMike Smith     {-1, NULL}
27715792b7feSMike Smith };
27725792b7feSMike Smith 
27739eee27f1SMike Smith static void
27749eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
27751ac4b82bSMike Smith {
27761ac4b82bSMike Smith     static char		buf[80];
27775792b7feSMike Smith     char		*model;
27789eee27f1SMike Smith     int			i;
27791ac4b82bSMike Smith 
27805792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
27819eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
27825792b7feSMike Smith 	    model = mlx_controller_names[i].name;
27831ac4b82bSMike Smith 	    break;
27841ac4b82bSMike Smith 	}
27855792b7feSMike Smith     }
27865792b7feSMike Smith     if (model == NULL) {
27879eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
27885792b7feSMike Smith 	model = buf;
27895792b7feSMike Smith     }
2790da8bb3a3SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
27919eee27f1SMike Smith 		  model,
27929eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
27939eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
27949eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
27959eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
27969eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2797b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
27989eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
27999eee27f1SMike Smith 
28009eee27f1SMike Smith     if (bootverbose) {
28019eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
28029eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
28039eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
28049eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
28059eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
28069eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
28079eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
28089eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
28099eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
28109eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
28119eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
28129eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
28139eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
28149eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
28159eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
28169eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
28179eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
28189eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
28199eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
28209eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
28219eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
28229eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
28239eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
28249eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
28259eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
28269eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
28279eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
28289eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
28299eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
28309eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
28319eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
28329eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
28339eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
28349eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
28359eee27f1SMike Smith 
28369eee27f1SMike Smith     }
28371ac4b82bSMike Smith }
28381ac4b82bSMike Smith 
2839da8bb3a3SMike Smith /*******************************************************************************
2840da8bb3a3SMike Smith  * Emit a string describing the firmware handshake status code, and return a flag
2841da8bb3a3SMike Smith  * indicating whether the code represents a fatal error.
2842da8bb3a3SMike Smith  *
2843da8bb3a3SMike Smith  * Error code interpretations are from the Linux driver, and don't directly match
2844da8bb3a3SMike Smith  * the messages printed by Mylex's BIOS.  This may change if documentation on the
2845da8bb3a3SMike Smith  * codes is forthcoming.
2846da8bb3a3SMike Smith  */
2847da8bb3a3SMike Smith static int
2848da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
2849da8bb3a3SMike Smith {
2850da8bb3a3SMike Smith     switch(error) {
2851da8bb3a3SMike Smith     case 0x00:
2852da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
2853da8bb3a3SMike Smith 	break;
2854da8bb3a3SMike Smith     case 0x08:
2855da8bb3a3SMike Smith 	/* we could be neater about this and give some indication when we receive more of them */
2856da8bb3a3SMike Smith 	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
2857da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "spinning up drives...\n");
2858da8bb3a3SMike Smith 	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
2859da8bb3a3SMike Smith 	}
2860da8bb3a3SMike Smith 	break;
2861da8bb3a3SMike Smith     case 0x30:
2862da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "configuration checksum error\n");
2863da8bb3a3SMike Smith 	break;
2864da8bb3a3SMike Smith     case 0x60:
2865da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
2866da8bb3a3SMike Smith 	break;
2867da8bb3a3SMike Smith     case 0x70:
2868da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
2869da8bb3a3SMike Smith 	break;
2870da8bb3a3SMike Smith     case 0x90:
2871da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
2872da8bb3a3SMike Smith 	break;
2873da8bb3a3SMike Smith     case 0xa0:
2874da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
2875da8bb3a3SMike Smith 	break;
2876da8bb3a3SMike Smith     case 0xb0:
2877da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
2878da8bb3a3SMike Smith 	break;
2879da8bb3a3SMike Smith     case 0xd0:
2880da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "new controller configuration found\n");
2881da8bb3a3SMike Smith 	break;
2882da8bb3a3SMike Smith     case 0xf0:
2883da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
2884da8bb3a3SMike Smith 	return(1);
2885da8bb3a3SMike Smith     default:
2886da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
2887da8bb3a3SMike Smith 	break;
2888da8bb3a3SMike Smith     }
2889da8bb3a3SMike Smith     return(0);
2890da8bb3a3SMike Smith }
2891da8bb3a3SMike Smith 
28921ac4b82bSMike Smith /********************************************************************************
28931ac4b82bSMike Smith  ********************************************************************************
28941ac4b82bSMike Smith                                                                 Utility Functions
28951ac4b82bSMike Smith  ********************************************************************************
28961ac4b82bSMike Smith  ********************************************************************************/
28971ac4b82bSMike Smith 
28981ac4b82bSMike Smith /********************************************************************************
28991ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
29001ac4b82bSMike Smith  */
29011ac4b82bSMike Smith static struct mlx_sysdrive *
29021ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
29031ac4b82bSMike Smith {
29041ac4b82bSMike Smith     int		i;
29051ac4b82bSMike Smith 
29061ac4b82bSMike Smith     /* search system drives */
29071ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
29081ac4b82bSMike Smith 	/* is this one attached? */
29091ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
29101ac4b82bSMike Smith 	    /* is this the one? */
29111ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
29121ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
29131ac4b82bSMike Smith 	}
29141ac4b82bSMike Smith     }
29151ac4b82bSMike Smith     return(NULL);
29161ac4b82bSMike Smith }
2917