xref: /freebsd/sys/dev/mlx/mlx.c (revision b9256fe34ed4f5394392fb4cce456051bd5ef2f6)
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>
431ac4b82bSMike Smith 
441ac4b82bSMike Smith #include <machine/resource.h>
451ac4b82bSMike Smith #include <machine/bus.h>
461ac4b82bSMike Smith #include <machine/clock.h>
471ac4b82bSMike Smith #include <sys/rman.h>
481ac4b82bSMike Smith 
491ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
501ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
511ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
521ac4b82bSMike Smith 
531ac4b82bSMike Smith #if 0
541ac4b82bSMike Smith #define debug(fmt, args...)	printf("%s: " fmt "\n", __FUNCTION__ , ##args)
551ac4b82bSMike Smith #else
561ac4b82bSMike Smith #define debug(fmt, args...)
571ac4b82bSMike Smith #endif
581ac4b82bSMike Smith 
591ac4b82bSMike Smith #define MLX_CDEV_MAJOR	130
601ac4b82bSMike Smith 
611ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
621ac4b82bSMike Smith 		/* open */	mlx_open,
631ac4b82bSMike Smith 		/* close */	mlx_close,
641ac4b82bSMike Smith 		/* read */	noread,
651ac4b82bSMike Smith 		/* write */	nowrite,
661ac4b82bSMike Smith 		/* ioctl */	mlx_ioctl,
671ac4b82bSMike Smith 		/* poll */	nopoll,
681ac4b82bSMike Smith 		/* mmap */	nommap,
691ac4b82bSMike Smith 		/* strategy */	nostrategy,
701ac4b82bSMike Smith 		/* name */ 	"mlx",
711ac4b82bSMike Smith 		/* maj */	MLX_CDEV_MAJOR,
721ac4b82bSMike Smith 		/* dump */	nodump,
731ac4b82bSMike Smith 		/* psize */ 	nopsize,
741ac4b82bSMike Smith 		/* flags */	0,
75f6b84b08SMike Smith 		/* bmaj */	-1
761ac4b82bSMike Smith };
771ac4b82bSMike Smith 
781ac4b82bSMike Smith static int	cdev_registered = 0;
791ac4b82bSMike Smith devclass_t	mlx_devclass;
801ac4b82bSMike Smith 
811ac4b82bSMike Smith /*
821ac4b82bSMike Smith  * Per-interface accessor methods
831ac4b82bSMike Smith  */
841ac4b82bSMike Smith static int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
851ac4b82bSMike Smith static int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
861ac4b82bSMike Smith static void			mlx_v3_intaction(struct mlx_softc *sc, int action);
871ac4b82bSMike Smith 
88f6b84b08SMike Smith static int			mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
89f6b84b08SMike Smith static int			mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
90f6b84b08SMike Smith static void			mlx_v4_intaction(struct mlx_softc *sc, int action);
91f6b84b08SMike Smith 
925792b7feSMike Smith static int			mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
935792b7feSMike Smith static int			mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
945792b7feSMike Smith static void			mlx_v5_intaction(struct mlx_softc *sc, int action);
955792b7feSMike Smith 
961ac4b82bSMike Smith /*
971ac4b82bSMike Smith  * Status monitoring
981ac4b82bSMike Smith  */
991ac4b82bSMike Smith static void			mlx_periodic(void *data);
1001ac4b82bSMike Smith static void			mlx_periodic_enquiry(struct mlx_command *mc);
1011ac4b82bSMike Smith static void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
1021ac4b82bSMike Smith static void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
1031ac4b82bSMike Smith static void			mlx_periodic_rebuild(struct mlx_command *mc);
1041ac4b82bSMike Smith 
1051ac4b82bSMike Smith /*
1061ac4b82bSMike Smith  * Channel Pause
1071ac4b82bSMike Smith  */
1081ac4b82bSMike Smith static void			mlx_pause_action(struct mlx_softc *sc);
1091ac4b82bSMike Smith static void			mlx_pause_done(struct mlx_command *mc);
1101ac4b82bSMike Smith 
1111ac4b82bSMike Smith /*
1121ac4b82bSMike Smith  * Command submission.
1131ac4b82bSMike Smith  */
1141ac4b82bSMike Smith static void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
1151ac4b82bSMike Smith 					     void (*complete)(struct mlx_command *mc));
1161ac4b82bSMike Smith static int			mlx_flush(struct mlx_softc *sc);
1171ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1181ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1191ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1201ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1211ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1221ac4b82bSMike Smith static int			mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu);
1231ac4b82bSMike Smith 
1241ac4b82bSMike Smith /*
1251ac4b82bSMike Smith  * Command buffer allocation.
1261ac4b82bSMike Smith  */
1271ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1281ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1291ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1301ac4b82bSMike Smith 
1311ac4b82bSMike Smith /*
1321ac4b82bSMike Smith  * Command management.
1331ac4b82bSMike Smith  */
1341ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1351ac4b82bSMike Smith static void			mlx_mapcmd(struct mlx_command *mc);
1361ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1371ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1381ac4b82bSMike Smith static int			mlx_done(struct mlx_softc *sc);
1391ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1401ac4b82bSMike Smith 
1411ac4b82bSMike Smith /*
1421ac4b82bSMike Smith  * Debugging.
1431ac4b82bSMike Smith  */
1441ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1459eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
1461ac4b82bSMike Smith 
1471ac4b82bSMike Smith 
1481ac4b82bSMike Smith /*
1491ac4b82bSMike Smith  * Utility functions.
1501ac4b82bSMike Smith  */
1511ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1521ac4b82bSMike Smith 
1531ac4b82bSMike Smith /********************************************************************************
1541ac4b82bSMike Smith  ********************************************************************************
1551ac4b82bSMike Smith                                                                 Public Interfaces
1561ac4b82bSMike Smith  ********************************************************************************
1571ac4b82bSMike Smith  ********************************************************************************/
1581ac4b82bSMike Smith 
1591ac4b82bSMike Smith /********************************************************************************
1601ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1611ac4b82bSMike Smith  *
1621ac4b82bSMike Smith  * Should not be called if the controller is active.
1631ac4b82bSMike Smith  */
1641ac4b82bSMike Smith void
1651ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1661ac4b82bSMike Smith {
1671ac4b82bSMike Smith     struct mlx_command	*mc;
1681ac4b82bSMike Smith 
1691ac4b82bSMike Smith     debug("called");
1701ac4b82bSMike Smith 
1711ac4b82bSMike Smith     /* cancel status timeout */
1721ac4b82bSMike Smith     untimeout(mlx_periodic, sc, sc->mlx_timeout);
1731ac4b82bSMike Smith 
1741ac4b82bSMike Smith     /* throw away any command buffers */
1751ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1761ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1771ac4b82bSMike Smith 	mlx_freecmd(mc);
1781ac4b82bSMike Smith     }
1791ac4b82bSMike Smith 
1801ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1811ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1821ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1831ac4b82bSMike Smith 
1841ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
1851ac4b82bSMike Smith     if (sc->mlx_sgtable)
1861ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1871ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1881ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1891ac4b82bSMike Smith 
1901ac4b82bSMike Smith     /* disconnect the interrupt handler */
1911ac4b82bSMike Smith     if (sc->mlx_intr)
1921ac4b82bSMike Smith 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1931ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
1941ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
1951ac4b82bSMike Smith 
1961ac4b82bSMike Smith     /* destroy the parent DMA tag */
1971ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
1981ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
1991ac4b82bSMike Smith 
2001ac4b82bSMike Smith     /* release the register window mapping */
2011ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
2021ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY,
2031ac4b82bSMike Smith 			     (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem);
2049eee27f1SMike Smith 
2059eee27f1SMike Smith     /* free controller enquiry data */
2069eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
2079eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
2081ac4b82bSMike Smith }
2091ac4b82bSMike Smith 
2101ac4b82bSMike Smith /********************************************************************************
2111ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2121ac4b82bSMike Smith  */
2131ac4b82bSMike Smith static void
2141ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2151ac4b82bSMike Smith {
2161ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2171ac4b82bSMike Smith 
2181ac4b82bSMike Smith     debug("called");
2191ac4b82bSMike Smith 
2201ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2211ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2221ac4b82bSMike Smith }
2231ac4b82bSMike Smith 
2241ac4b82bSMike Smith static int
2251ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2261ac4b82bSMike Smith {
2271ac4b82bSMike Smith     size_t	segsize;
2281ac4b82bSMike Smith     int		error;
2291ac4b82bSMike Smith 
2301ac4b82bSMike Smith     debug("called");
2311ac4b82bSMike Smith 
2321ac4b82bSMike Smith     /* destroy any existing mappings */
2331ac4b82bSMike Smith     if (sc->mlx_sgtable)
2341ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2351ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2361ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2371ac4b82bSMike Smith 
2381ac4b82bSMike Smith     /*
2391ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
2401ac4b82bSMike Smith      * the s/g lists we will need.
2411ac4b82bSMike Smith      */
2421ac4b82bSMike Smith     segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * sc->mlx_maxiop;
2431ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2441ac4b82bSMike Smith 			       1, 0, 			/* alignment, boundary */
2451ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2461ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2471ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2481ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2491ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2501ac4b82bSMike Smith 			       0,			/* flags */
2511ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2521ac4b82bSMike Smith     if (error != 0) {
2531ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2541ac4b82bSMike Smith 	return(ENOMEM);
2551ac4b82bSMike Smith     }
2561ac4b82bSMike Smith 
2571ac4b82bSMike Smith     /*
2581ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2591ac4b82bSMike Smith      * controller-visible space.
2601ac4b82bSMike Smith      *
2611ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
2621ac4b82bSMike Smith      * contiguous slab.  We may need to switch to a more complex arrangement where
2631ac4b82bSMike Smith      * we allocate in smaller chunks and keep a lookup table from slot to bus address.
2641ac4b82bSMike Smith      */
2651ac4b82bSMike Smith     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2661ac4b82bSMike Smith     if (error) {
2671ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2681ac4b82bSMike Smith 	return(ENOMEM);
2691ac4b82bSMike Smith     }
2701ac4b82bSMike Smith     bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0);
2711ac4b82bSMike Smith     return(0);
2721ac4b82bSMike Smith }
2731ac4b82bSMike Smith 
2741ac4b82bSMike Smith /********************************************************************************
2751ac4b82bSMike Smith  * Initialise the controller and softc
2761ac4b82bSMike Smith  */
2771ac4b82bSMike Smith int
2781ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
2791ac4b82bSMike Smith {
2809eee27f1SMike Smith     int			rid, error, fwminor;
2811ac4b82bSMike Smith 
2821ac4b82bSMike Smith     debug("called");
2831ac4b82bSMike Smith 
2841ac4b82bSMike Smith     /*
2851ac4b82bSMike Smith      * Initialise per-controller queues.
2861ac4b82bSMike Smith      */
2874b006d7bSMike Smith     TAILQ_INIT(&sc->mlx_work);
2881ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
2891ac4b82bSMike Smith     bufq_init(&sc->mlx_bufq);
2901ac4b82bSMike Smith 
2911ac4b82bSMike Smith     /*
2921ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
2931ac4b82bSMike Smith      */
2941ac4b82bSMike Smith     switch(sc->mlx_iftype) {
2951ac4b82bSMike Smith     case MLX_IFTYPE_3:
2961ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
2971ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
2981ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
2991ac4b82bSMike Smith 	break;
300f6b84b08SMike Smith     case MLX_IFTYPE_4:
301f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
302f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
303f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
304f6b84b08SMike Smith 	break;
3055792b7feSMike Smith     case MLX_IFTYPE_5:
3065792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3075792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3085792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
3095792b7feSMike Smith 	break;
3101ac4b82bSMike Smith     default:
3111ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype);
3121ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3131ac4b82bSMike Smith     }
3141ac4b82bSMike Smith 
3151ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3161ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3171ac4b82bSMike Smith 
3181ac4b82bSMike Smith     /*
3191ac4b82bSMike Smith      * Allocate and connect our interrupt.
3201ac4b82bSMike Smith      */
3211ac4b82bSMike Smith     rid = 0;
3221ac4b82bSMike Smith     sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
3231ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
3241ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't allocate interrupt\n");
3251ac4b82bSMike Smith 	mlx_free(sc);
3261ac4b82bSMike Smith 	return(ENXIO);
3271ac4b82bSMike Smith     }
3281ac4b82bSMike Smith     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO,  mlx_intr, sc, &sc->mlx_intr);
3291ac4b82bSMike Smith     if (error) {
3301ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't set up interrupt\n");
3311ac4b82bSMike Smith 	mlx_free(sc);
3321ac4b82bSMike Smith 	return(ENXIO);
3331ac4b82bSMike Smith     }
3341ac4b82bSMike Smith 
3351ac4b82bSMike Smith     /*
3361ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3371ac4b82bSMike Smith      */
3381ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
3391ac4b82bSMike Smith 			       1, 0, 			/* alignment, boundary */
3401ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
3411ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
3421ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
3431ac4b82bSMike Smith 			       MAXBSIZE, MLX_NSEG,	/* maxsize, nsegments */
3441ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
3451ac4b82bSMike Smith 			       0,			/* flags */
3461ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
3471ac4b82bSMike Smith     if (error != 0) {
3481ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
3491ac4b82bSMike Smith 	return(ENOMEM);
3501ac4b82bSMike Smith     }
3511ac4b82bSMike Smith 
3521ac4b82bSMike Smith     /*
3531ac4b82bSMike Smith      * Create an initial set of s/g mappings.
3541ac4b82bSMike Smith      */
3559eee27f1SMike Smith     sc->mlx_maxiop = 8;
3561ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3571ac4b82bSMike Smith     if (error != 0) {
3581ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
3591ac4b82bSMike Smith 	return(error);
3601ac4b82bSMike Smith     }
3611ac4b82bSMike Smith 
3621ac4b82bSMike Smith     /* send an ENQUIRY2 to the controller */
3639eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
3641ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
3651ac4b82bSMike Smith 	return(ENXIO);
3661ac4b82bSMike Smith     }
3671ac4b82bSMike Smith 
3689eee27f1SMike Smith     /*
3699eee27f1SMike Smith      * We don't (yet) know where the event log is up to.
3709eee27f1SMike Smith      */
3719eee27f1SMike Smith     sc->mlx_lastevent = -1;
3721ac4b82bSMike Smith 
3739eee27f1SMike Smith     /* print a little information about the controller */
3749eee27f1SMike Smith     mlx_describe_controller(sc);
3751ac4b82bSMike Smith 
3761ac4b82bSMike Smith     /*
3771ac4b82bSMike Smith      * Do quirk/feature related things.
3781ac4b82bSMike Smith      */
3799eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
3801ac4b82bSMike Smith     switch(sc->mlx_iftype) {
3811ac4b82bSMike Smith     case MLX_IFTYPE_3:
382f6b84b08SMike Smith 	/* XXX certify 3.52? */
3839eee27f1SMike Smith 	if (fwminor < 51) {
3844b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
3854b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
3861ac4b82bSMike Smith 	}
3871ac4b82bSMike Smith 	break;
388f6b84b08SMike Smith     case MLX_IFTYPE_4:
389f6b84b08SMike Smith 	/* XXX certify firmware versions? */
3909eee27f1SMike Smith 	if (fwminor < 6) {
3914b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
3924b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
393f6b84b08SMike Smith 	}
394f6b84b08SMike Smith 	break;
3955792b7feSMike Smith     case MLX_IFTYPE_5:
3969eee27f1SMike Smith 	if (fwminor < 7) {
3975792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
3985792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
3995792b7feSMike Smith 	}
4005792b7feSMike Smith 	break;
4011ac4b82bSMike Smith     default:
4021ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "interface version corrupted to %d\n", sc->mlx_iftype);
4031ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4041ac4b82bSMike Smith     }
4051ac4b82bSMike Smith 
4061ac4b82bSMike Smith     /*
4071ac4b82bSMike Smith      * Create the final set of s/g mappings now that we know how many commands
4081ac4b82bSMike Smith      * the controller actually supports.
4091ac4b82bSMike Smith      */
4109eee27f1SMike Smith     sc->mlx_maxiop = sc->mlx_enq2->me_max_commands;
4111ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4121ac4b82bSMike Smith     if (error != 0) {
4131ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
4141ac4b82bSMike Smith 	return(error);
4151ac4b82bSMike Smith     }
4161ac4b82bSMike Smith 
4171ac4b82bSMike Smith     /*
4181ac4b82bSMike Smith      * No rebuild or check is in progress.
4191ac4b82bSMike Smith      */
4201ac4b82bSMike Smith     sc->mlx_rebuild = -1;
4211ac4b82bSMike Smith     sc->mlx_check = -1;
4221ac4b82bSMike Smith 
4231ac4b82bSMike Smith     /*
4241ac4b82bSMike Smith      * Register the control device on first attach.
4251ac4b82bSMike Smith      */
4261ac4b82bSMike Smith     if (cdev_registered++ == 0)
4271ac4b82bSMike Smith 	cdevsw_add(&mlx_cdevsw);
4281ac4b82bSMike Smith 
4291ac4b82bSMike Smith     /*
4301ac4b82bSMike Smith      * Start the timeout routine.
4311ac4b82bSMike Smith      */
4321ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
4331ac4b82bSMike Smith 
4341ac4b82bSMike Smith     return(0);
4351ac4b82bSMike Smith }
4361ac4b82bSMike Smith 
4371ac4b82bSMike Smith /********************************************************************************
4381ac4b82bSMike Smith  * Locate disk resources and attach children to them.
4391ac4b82bSMike Smith  */
4401ac4b82bSMike Smith void
4411ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
4421ac4b82bSMike Smith {
4431ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
4441ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
4451ac4b82bSMike Smith     int				i, error;
4461ac4b82bSMike Smith 
4471ac4b82bSMike Smith     debug("called");
4481ac4b82bSMike Smith 
4491ac4b82bSMike Smith     /*
4501ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
4511ac4b82bSMike Smith      * don't currently have them.
4521ac4b82bSMike Smith      */
4531ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
4541ac4b82bSMike Smith     if (mes == NULL) {
4559eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
4561ac4b82bSMike Smith 	return;
4571ac4b82bSMike Smith     }
4581ac4b82bSMike Smith 
4591ac4b82bSMike Smith     /* iterate over drives returned */
4601ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
4611ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
4621ac4b82bSMike Smith 	 i++, dr++) {
4631ac4b82bSMike Smith 	/* are we already attached to this drive? */
4641ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
4651ac4b82bSMike Smith 	    /* pick up drive information */
4661ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
467f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
4681ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
4691ac4b82bSMike Smith 
4701ac4b82bSMike Smith 	    /* generate geometry information */
4711ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
4721ac4b82bSMike Smith 		dr->ms_heads = 128;
4731ac4b82bSMike Smith 		dr->ms_sectors = 32;
4741ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
4751ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
4761ac4b82bSMike Smith 		dr->ms_heads = 255;
4771ac4b82bSMike Smith 		dr->ms_sectors = 63;
4781ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
4791ac4b82bSMike Smith 	    }
480fe0d4089SMatthew N. Dodd 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
4811ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
4821ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
483fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
4841ac4b82bSMike Smith 	}
4851ac4b82bSMike Smith     }
4861ac4b82bSMike Smith     free(mes, M_DEVBUF);
4871ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
4881ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
4891ac4b82bSMike Smith 
4901ac4b82bSMike Smith     /* mark controller back up */
4911ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
4921ac4b82bSMike Smith 
4931ac4b82bSMike Smith     /* enable interrupts */
4941ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
4951ac4b82bSMike Smith }
4961ac4b82bSMike Smith 
4971ac4b82bSMike Smith /********************************************************************************
4981ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
4991ac4b82bSMike Smith  */
5001ac4b82bSMike Smith int
5011ac4b82bSMike Smith mlx_detach(device_t dev)
5021ac4b82bSMike Smith {
5031ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5045792b7feSMike Smith     struct mlxd_softc	*mlxd;
5055792b7feSMike Smith     int			i, s, error;
5061ac4b82bSMike Smith 
5071ac4b82bSMike Smith     debug("called");
5081ac4b82bSMike Smith 
5095792b7feSMike Smith     error = EBUSY;
5105792b7feSMike Smith     s = splbio();
5111ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
5125792b7feSMike Smith 	goto out;
5131ac4b82bSMike Smith 
5145792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5155792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5165792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
5175792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
5185792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
5195792b7feSMike Smith 		goto out;
5205792b7feSMike Smith 	    }
5215792b7feSMike Smith 	}
5225792b7feSMike Smith     }
5231ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
5245792b7feSMike Smith 	goto out;
5251ac4b82bSMike Smith 
5261ac4b82bSMike Smith     mlx_free(sc);
5271ac4b82bSMike Smith 
5281ac4b82bSMike Smith     /*
5291ac4b82bSMike Smith      * Deregister the control device on last detach.
5301ac4b82bSMike Smith      */
5311ac4b82bSMike Smith     if (--cdev_registered == 0)
5321ac4b82bSMike Smith 	cdevsw_remove(&mlx_cdevsw);
5335792b7feSMike Smith     error = 0;
5345792b7feSMike Smith  out:
5355792b7feSMike Smith     splx(s);
5365792b7feSMike Smith     return(error);
5371ac4b82bSMike Smith }
5381ac4b82bSMike Smith 
5391ac4b82bSMike Smith /********************************************************************************
5401ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
5411ac4b82bSMike Smith  *
5421ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
5431ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
5441ac4b82bSMike Smith  * resume normal operation.)
5451ac4b82bSMike Smith  *
5461ac4b82bSMike Smith  * Note that we can assume that the bufq on the controller is empty, as we won't
5471ac4b82bSMike Smith  * allow shutdown if any device is open.
5481ac4b82bSMike Smith  */
5491ac4b82bSMike Smith int
5501ac4b82bSMike Smith mlx_shutdown(device_t dev)
5511ac4b82bSMike Smith {
5521ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5531ac4b82bSMike Smith     int			i, s, error;
5541ac4b82bSMike Smith 
5551ac4b82bSMike Smith     debug("called");
5561ac4b82bSMike Smith 
5571ac4b82bSMike Smith     s = splbio();
5581ac4b82bSMike Smith     error = 0;
5591ac4b82bSMike Smith 
5601ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
5615792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
5621ac4b82bSMike Smith 
5631ac4b82bSMike Smith     /* flush controller */
5641ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
5651ac4b82bSMike Smith     if (mlx_flush(sc)) {
5661ac4b82bSMike Smith 	printf("failed\n");
5671ac4b82bSMike Smith     } else {
5681ac4b82bSMike Smith 	printf("done\n");
5691ac4b82bSMike Smith     }
5701ac4b82bSMike Smith 
5711ac4b82bSMike Smith     /* delete all our child devices */
5721ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5731ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5741ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
5751ac4b82bSMike Smith 		goto out;
5761ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
5771ac4b82bSMike Smith 	}
5781ac4b82bSMike Smith     }
5791ac4b82bSMike Smith 
5801ac4b82bSMike Smith  out:
5811ac4b82bSMike Smith     splx(s);
5821ac4b82bSMike Smith     return(error);
5831ac4b82bSMike Smith }
5841ac4b82bSMike Smith 
5851ac4b82bSMike Smith /********************************************************************************
5861ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
5871ac4b82bSMike Smith  */
5881ac4b82bSMike Smith int
5891ac4b82bSMike Smith mlx_suspend(device_t dev)
5901ac4b82bSMike Smith {
5911ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5921ac4b82bSMike Smith     int			s;
5931ac4b82bSMike Smith 
5941ac4b82bSMike Smith     debug("called");
5951ac4b82bSMike Smith 
5961ac4b82bSMike Smith     s = splbio();
5971ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
5981ac4b82bSMike Smith 
5991ac4b82bSMike Smith     /* flush controller */
6001ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6011ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6021ac4b82bSMike Smith 
6031ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6041ac4b82bSMike Smith     splx(s);
6051ac4b82bSMike Smith 
6061ac4b82bSMike Smith     return(0);
6071ac4b82bSMike Smith }
6081ac4b82bSMike Smith 
6091ac4b82bSMike Smith /********************************************************************************
6101ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
6111ac4b82bSMike Smith  */
6121ac4b82bSMike Smith int
6131ac4b82bSMike Smith mlx_resume(device_t dev)
6141ac4b82bSMike Smith {
6151ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6161ac4b82bSMike Smith 
6171ac4b82bSMike Smith     debug("called");
6181ac4b82bSMike Smith 
6191ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
6201ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
6211ac4b82bSMike Smith 
6221ac4b82bSMike Smith     return(0);
6231ac4b82bSMike Smith }
6241ac4b82bSMike Smith 
6251ac4b82bSMike Smith /*******************************************************************************
6261ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
6271ac4b82bSMike Smith  * status.
6281ac4b82bSMike Smith  */
6291ac4b82bSMike Smith void
6301ac4b82bSMike Smith mlx_intr(void *arg)
6311ac4b82bSMike Smith {
6321ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
6331ac4b82bSMike Smith 
6341ac4b82bSMike Smith     debug("called");
6351ac4b82bSMike Smith 
6365792b7feSMike Smith     /* collect finished commands, queue anything waiting */
6375792b7feSMike Smith     mlx_done(sc);
6381ac4b82bSMike Smith };
6391ac4b82bSMike Smith 
6401ac4b82bSMike Smith /*******************************************************************************
6411ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
6421ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
6431ac4b82bSMike Smith  */
6441ac4b82bSMike Smith int
6451ac4b82bSMike Smith mlx_submit_buf(struct mlx_softc *sc, struct buf *bp)
6461ac4b82bSMike Smith {
6474b006d7bSMike Smith     int		s;
6484b006d7bSMike Smith 
6491ac4b82bSMike Smith     debug("called");
6501ac4b82bSMike Smith 
6514b006d7bSMike Smith     s = splbio();
6521ac4b82bSMike Smith     bufq_insert_tail(&sc->mlx_bufq, bp);
6531ac4b82bSMike Smith     sc->mlx_waitbufs++;
6544b006d7bSMike Smith     splx(s);
6551ac4b82bSMike Smith     mlx_startio(sc);
6561ac4b82bSMike Smith     return(0);
6571ac4b82bSMike Smith }
6581ac4b82bSMike Smith 
6591ac4b82bSMike Smith /********************************************************************************
6601ac4b82bSMike Smith  * Accept an open operation on the control device.
6611ac4b82bSMike Smith  */
6621ac4b82bSMike Smith int
6631ac4b82bSMike Smith mlx_open(dev_t dev, int flags, int fmt, struct proc *p)
6641ac4b82bSMike Smith {
6651ac4b82bSMike Smith     int			unit = minor(dev);
6661ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
6671ac4b82bSMike Smith 
6681ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
6691ac4b82bSMike Smith     return(0);
6701ac4b82bSMike Smith }
6711ac4b82bSMike Smith 
6721ac4b82bSMike Smith /********************************************************************************
6731ac4b82bSMike Smith  * Accept the last close on the control device.
6741ac4b82bSMike Smith  */
6751ac4b82bSMike Smith int
6761ac4b82bSMike Smith mlx_close(dev_t dev, int flags, int fmt, struct proc *p)
6771ac4b82bSMike Smith {
6781ac4b82bSMike Smith     int			unit = minor(dev);
6791ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
6801ac4b82bSMike Smith 
6811ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
6821ac4b82bSMike Smith     return (0);
6831ac4b82bSMike Smith }
6841ac4b82bSMike Smith 
6851ac4b82bSMike Smith /********************************************************************************
6861ac4b82bSMike Smith  * Handle controller-specific control operations.
6871ac4b82bSMike Smith  */
6881ac4b82bSMike Smith int
6891ac4b82bSMike Smith mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
6901ac4b82bSMike Smith {
6911ac4b82bSMike Smith     int			unit = minor(dev);
6921ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
6931ac4b82bSMike Smith     int			*arg = (int *)addr;
6941ac4b82bSMike Smith     struct mlx_pause	*mp;
6951ac4b82bSMike Smith     struct mlx_sysdrive	*dr;
6961ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
6971ac4b82bSMike Smith     int			i, error;
6981ac4b82bSMike Smith 
6991ac4b82bSMike Smith     switch(cmd) {
7001ac4b82bSMike Smith 	/*
7011ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
7021ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
7031ac4b82bSMike Smith 	 * a valid unit on this controller.
7041ac4b82bSMike Smith 	 */
7051ac4b82bSMike Smith     case MLX_NEXT_CHILD:
7061ac4b82bSMike Smith 	/* search system drives */
7071ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
7081ac4b82bSMike Smith 	    /* is this one attached? */
7091ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
7101ac4b82bSMike Smith 		/* looking for the next one we come across? */
7111ac4b82bSMike Smith 		if (*arg == -1) {
7121ac4b82bSMike Smith 		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
7131ac4b82bSMike Smith 		    return(0);
7141ac4b82bSMike Smith 		}
7151ac4b82bSMike Smith 		/* we want the one after this one */
7161ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
7171ac4b82bSMike Smith 		    *arg = -1;
7181ac4b82bSMike Smith 	    }
7191ac4b82bSMike Smith 	}
7201ac4b82bSMike Smith 	return(ENOENT);
7211ac4b82bSMike Smith 
7221ac4b82bSMike Smith 	/*
7231ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
7241ac4b82bSMike Smith 	 */
7251ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
7261ac4b82bSMike Smith 	mlx_startup(sc);
7271ac4b82bSMike Smith 	return(0);
7281ac4b82bSMike Smith 
7291ac4b82bSMike Smith 	/*
7301ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
7311ac4b82bSMike Smith 	 * away.
7321ac4b82bSMike Smith 	 */
7331ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
7341ac4b82bSMike Smith 
7351ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
7361ac4b82bSMike Smith 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
7371ac4b82bSMike Smith 	    return(ENOENT);
7381ac4b82bSMike Smith 
7391ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
7401ac4b82bSMike Smith 	error = 0;
7411ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
7421ac4b82bSMike Smith 	    error = EBUSY;
7431ac4b82bSMike Smith 	    goto detach_out;
7441ac4b82bSMike Smith 	}
7451ac4b82bSMike Smith 
7461ac4b82bSMike Smith 	/* flush controller */
7471ac4b82bSMike Smith 	if (mlx_flush(sc)) {
7481ac4b82bSMike Smith 	    error = EBUSY;
7491ac4b82bSMike Smith 	    goto detach_out;
7501ac4b82bSMike Smith 	}
7511ac4b82bSMike Smith 
7521ac4b82bSMike Smith 	/* nuke drive */
7531ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
7541ac4b82bSMike Smith 	    goto detach_out;
7551ac4b82bSMike Smith 	dr->ms_disk = 0;
7561ac4b82bSMike Smith 
7571ac4b82bSMike Smith     detach_out:
7581ac4b82bSMike Smith 	if (error) {
7591ac4b82bSMike Smith 	    printf("failed\n");
7601ac4b82bSMike Smith 	} else {
7611ac4b82bSMike Smith 	    printf("done\n");
7621ac4b82bSMike Smith 	}
7631ac4b82bSMike Smith 	return(error);
7641ac4b82bSMike Smith 
7651ac4b82bSMike Smith 	/*
7661ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
7671ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
7681ac4b82bSMike Smith 	 *
7691ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
7701ac4b82bSMike Smith 	 * to do this right.
7711ac4b82bSMike Smith 	 */
7721ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
7731ac4b82bSMike Smith 	/* Does this command work on this firmware? */
7741ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
7751ac4b82bSMike Smith 	    return(EOPNOTSUPP);
7761ac4b82bSMike Smith 
7771ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
7781ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
7791ac4b82bSMike Smith 	    /* cancel a pending pause operation */
7801ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
7811ac4b82bSMike Smith 	} else {
7821ac4b82bSMike Smith 	    /* fix for legal channels */
7839eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
7841ac4b82bSMike Smith 	    /* check time values */
7851ac4b82bSMike Smith 	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
7861ac4b82bSMike Smith 		return(EINVAL);
7871ac4b82bSMike Smith 	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
7881ac4b82bSMike Smith 		return(EINVAL);
7891ac4b82bSMike Smith 
7901ac4b82bSMike Smith 	    /* check for a pause currently running */
7911ac4b82bSMike Smith 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
7921ac4b82bSMike Smith 		return(EBUSY);
7931ac4b82bSMike Smith 
7941ac4b82bSMike Smith 	    /* looks ok, go with it */
7951ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
7961ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
7971ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
7981ac4b82bSMike Smith 	}
7991ac4b82bSMike Smith 	return(0);
8001ac4b82bSMike Smith 
8011ac4b82bSMike Smith 	/*
8021ac4b82bSMike Smith 	 * Accept a command passthrough-style.
8031ac4b82bSMike Smith 	 */
8041ac4b82bSMike Smith     case MLX_COMMAND:
8051ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
8061ac4b82bSMike Smith 
8071ac4b82bSMike Smith     default:
8081ac4b82bSMike Smith 	return(ENOTTY);
8091ac4b82bSMike Smith     }
8101ac4b82bSMike Smith }
8111ac4b82bSMike Smith 
8121ac4b82bSMike Smith /********************************************************************************
8131ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
8141ac4b82bSMike Smith  */
8151ac4b82bSMike Smith int
8161ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
8171ac4b82bSMike Smith 		caddr_t addr, int32_t flag, struct proc *p)
8181ac4b82bSMike Smith {
8191ac4b82bSMike Smith     struct mlxd_rebuild		*mr = (struct mlxd_rebuild *)addr;
8201ac4b82bSMike Smith     struct mlxd_rebuild_status	*mp = (struct mlxd_rebuild_status *)addr;
8211ac4b82bSMike Smith     int				*arg = (int *)addr;
8221ac4b82bSMike Smith     int				error;
8231ac4b82bSMike Smith 
8241ac4b82bSMike Smith     switch(cmd) {
8251ac4b82bSMike Smith 	/*
8261ac4b82bSMike Smith 	 * Return the current status of this drive.
8271ac4b82bSMike Smith 	 */
8281ac4b82bSMike Smith     case MLXD_STATUS:
8291ac4b82bSMike Smith 	*arg = drive->ms_state;
8301ac4b82bSMike Smith 	return(0);
8311ac4b82bSMike Smith 
8321ac4b82bSMike Smith 	/*
8331ac4b82bSMike Smith 	 * Start a background rebuild on this drive.
8341ac4b82bSMike Smith 	 */
8351ac4b82bSMike Smith     case MLXD_REBUILDASYNC:
8361ac4b82bSMike Smith 	/* XXX lock? */
8371ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0)
8381ac4b82bSMike Smith 	    return(EBUSY);
8391ac4b82bSMike Smith 	sc->mlx_rebuild = drive - &sc->mlx_sysdrive[0];
8401ac4b82bSMike Smith 
8411ac4b82bSMike Smith 	switch (mlx_rebuild(sc, mr->rb_channel, mr->rb_target)) {
8421ac4b82bSMike Smith 	case 0:
8431ac4b82bSMike Smith 	    drive->ms_state = MLX_SYSD_REBUILD;
8441ac4b82bSMike Smith 	    error = 0;
8451ac4b82bSMike Smith 	    break;
8461ac4b82bSMike Smith 	case 0x10000:
8471ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
8481ac4b82bSMike Smith 	    break;
8491ac4b82bSMike Smith 	case 0x0002:
8501ac4b82bSMike Smith 	case 0x0106:
8511ac4b82bSMike Smith 	    error = EBUSY;
8521ac4b82bSMike Smith 	    break;
8531ac4b82bSMike Smith 	case 0x0004:
8541ac4b82bSMike Smith 	    error = EIO;
8551ac4b82bSMike Smith 	    break;
8561ac4b82bSMike Smith 	case 0x0105:
8571ac4b82bSMike Smith 	    error = ERANGE;
8581ac4b82bSMike Smith 	    break;
8591ac4b82bSMike Smith 	default:
8601ac4b82bSMike Smith 	    error = EINVAL;
8611ac4b82bSMike Smith 	    break;
8621ac4b82bSMike Smith 	}
8631ac4b82bSMike Smith 	if (error != 0)
8641ac4b82bSMike Smith 	    sc->mlx_rebuild = -1;
8651ac4b82bSMike Smith 	return(error);
8661ac4b82bSMike Smith 
8671ac4b82bSMike Smith 	/*
8681ac4b82bSMike Smith 	 * Start a background consistency check on this drive.
8691ac4b82bSMike Smith 	 */
8701ac4b82bSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
8711ac4b82bSMike Smith 	/* XXX implement */
8721ac4b82bSMike Smith 	break;
8731ac4b82bSMike Smith 
8741ac4b82bSMike Smith 	/*
8751ac4b82bSMike Smith 	 * Get the status of the current rebuild or consistency check.
8761ac4b82bSMike Smith 	 */
8771ac4b82bSMike Smith     case MLXD_REBUILDSTAT:
8781ac4b82bSMike Smith 
8791ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {	/* may be a second or so out of date */
8801ac4b82bSMike Smith 	    mp->rs_drive = sc->mlx_rebuild;
8811ac4b82bSMike Smith 	    mp->rs_size = sc->mlx_sysdrive[sc->mlx_rebuild].ms_size;
8821ac4b82bSMike Smith 	    mp->rs_remaining = sc->mlx_rebuildstat;
8831ac4b82bSMike Smith 	    return(0);
8841ac4b82bSMike Smith 	} else if (sc->mlx_check >= 0) {
8851ac4b82bSMike Smith 	    /* XXX implement */
8861ac4b82bSMike Smith 	} else {
8871ac4b82bSMike Smith 	    /* XXX should return status of last completed operation? */
8881ac4b82bSMike Smith 	    return(EINVAL);
8891ac4b82bSMike Smith 	}
8901ac4b82bSMike Smith 
8911ac4b82bSMike Smith     }
8921ac4b82bSMike Smith     return(ENOIOCTL);
8931ac4b82bSMike Smith }
8941ac4b82bSMike Smith 
8951ac4b82bSMike Smith 
8961ac4b82bSMike Smith /********************************************************************************
8971ac4b82bSMike Smith  ********************************************************************************
8981ac4b82bSMike Smith                                                                 Status Monitoring
8991ac4b82bSMike Smith  ********************************************************************************
9001ac4b82bSMike Smith  ********************************************************************************/
9011ac4b82bSMike Smith 
9021ac4b82bSMike Smith /********************************************************************************
9031ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
9041ac4b82bSMike Smith  */
9051ac4b82bSMike Smith static void
9061ac4b82bSMike Smith mlx_periodic(void *data)
9071ac4b82bSMike Smith {
9081ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
9091ac4b82bSMike Smith 
9101ac4b82bSMike Smith     debug("called");
9111ac4b82bSMike Smith 
9121ac4b82bSMike Smith     /*
9131ac4b82bSMike Smith      * Run a bus pause?
9141ac4b82bSMike Smith      */
9151ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
9161ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
9171ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
9181ac4b82bSMike Smith 
9191ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
9201ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
9211ac4b82bSMike Smith 	sysbeep(500, hz);
9221ac4b82bSMike Smith 
9231ac4b82bSMike Smith 	/*
9241ac4b82bSMike Smith 	 * Bus pause still running?
9251ac4b82bSMike Smith 	 */
9261ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
9271ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
9281ac4b82bSMike Smith 
9291ac4b82bSMike Smith 	/* time to stop bus pause? */
9301ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
9311ac4b82bSMike Smith 	    mlx_pause_action(sc);
9321ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
9331ac4b82bSMike Smith 	    sysbeep(500, hz);
9341ac4b82bSMike Smith 	} else {
9351ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
9361ac4b82bSMike Smith 	}
9371ac4b82bSMike Smith 
9381ac4b82bSMike Smith 	/*
9391ac4b82bSMike Smith 	 * Run normal periodic activities?
9401ac4b82bSMike Smith 	 */
9415792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
9421ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
9431ac4b82bSMike Smith 
9441ac4b82bSMike Smith 	/*
9451ac4b82bSMike Smith 	 * Check controller status.
9465792b7feSMike Smith 	 *
9475792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
9481ac4b82bSMike Smith 	 */
9491ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(struct mlx_enquiry), mlx_periodic_enquiry);
9501ac4b82bSMike Smith 
9511ac4b82bSMike Smith 	/*
9521ac4b82bSMike Smith 	 * Check system drive status.
9531ac4b82bSMike Smith 	 *
9541ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
9551ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
9561ac4b82bSMike Smith 	 *     online.
9571ac4b82bSMike Smith 	 */
9581ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
9591ac4b82bSMike Smith 			mlx_periodic_enquiry);
9601ac4b82bSMike Smith 
9611ac4b82bSMike Smith 	/*
9621ac4b82bSMike Smith 	 * Get drive rebuild/check status
9631ac4b82bSMike Smith 	 */
9645792b7feSMike Smith 	if (sc->mlx_rebuild >= 0)
9651ac4b82bSMike Smith 	    mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
9661ac4b82bSMike Smith     }
9671ac4b82bSMike Smith 
9685792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
9695792b7feSMike Smith     mlx_done(sc);
9701ac4b82bSMike Smith 
9711ac4b82bSMike Smith     /* reschedule another poll next second or so */
9721ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
9731ac4b82bSMike Smith }
9741ac4b82bSMike Smith 
9751ac4b82bSMike Smith /********************************************************************************
9761ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
9771ac4b82bSMike Smith  */
9781ac4b82bSMike Smith static void
9791ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
9801ac4b82bSMike Smith {
9811ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
9821ac4b82bSMike Smith 
9831ac4b82bSMike Smith     debug("called");
9841ac4b82bSMike Smith 
9851ac4b82bSMike Smith     /* Command completed OK? */
9861ac4b82bSMike Smith     if (mc->mc_status != 0) {
9871ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed\n");
9881ac4b82bSMike Smith 	goto out;
9891ac4b82bSMike Smith     }
9901ac4b82bSMike Smith 
9911ac4b82bSMike Smith     /* respond to command */
9921ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
9931ac4b82bSMike Smith 	/*
9941ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
9951ac4b82bSMike Smith 	 * checking the event log.
9961ac4b82bSMike Smith 	 */
9971ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
9981ac4b82bSMike Smith     {
9991ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
10001ac4b82bSMike Smith 
10019eee27f1SMike Smith 	if (sc->mlx_lastevent == -1) {
10029eee27f1SMike Smith 	    /* initialise our view of the event log */
10039eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
10049eee27f1SMike Smith 	} else if (me->me_event_log_seq_num != sc->mlx_lastevent) {
10051ac4b82bSMike Smith 	    /* record where current events are up to */
10061ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
10071ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "event log pointer was %d, now %d\n",
10081ac4b82bSMike Smith 			  sc->mlx_lastevent, sc->mlx_currevent);
10091ac4b82bSMike Smith 
10109eee27f1SMike Smith 	    /* drain new eventlog entries */
10111ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
10121ac4b82bSMike Smith 	}
10131ac4b82bSMike Smith 	break;
10141ac4b82bSMike Smith     }
10151ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
10161ac4b82bSMike Smith     {
10171ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
10181ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
10191ac4b82bSMike Smith 	int				i;
10201ac4b82bSMike Smith 
10211ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
10221ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
10231ac4b82bSMike Smith 	     i++) {
10241ac4b82bSMike Smith 
10251ac4b82bSMike Smith 	    /* if disk is being rebuilt, we should not check it */
10261ac4b82bSMike Smith 	    if (dr->ms_state == MLX_SYSD_REBUILD) {
10271ac4b82bSMike Smith 		/* has state been changed by controller? */
10281ac4b82bSMike Smith 		if (dr->ms_state != mes[i].sd_state) {
10291ac4b82bSMike Smith 		    switch(mes[i].sd_state) {
10301ac4b82bSMike Smith 		    case MLX_SYSD_OFFLINE:
10311ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive offline\n");
10321ac4b82bSMike Smith 			break;
10331ac4b82bSMike Smith 		    case MLX_SYSD_ONLINE:
10341ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive online\n");
10351ac4b82bSMike Smith 			break;
10361ac4b82bSMike Smith 		    case MLX_SYSD_CRITICAL:
10371ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive critical\n");
10381ac4b82bSMike Smith 			break;
10391ac4b82bSMike Smith 		    }
10401ac4b82bSMike Smith 		    /* save new state */
10411ac4b82bSMike Smith 		    dr->ms_state = mes[i].sd_state;
10421ac4b82bSMike Smith 		}
10431ac4b82bSMike Smith 	    }
10441ac4b82bSMike Smith 	}
10451ac4b82bSMike Smith 	break;
10461ac4b82bSMike Smith     }
10471ac4b82bSMike Smith     default:
10485792b7feSMike Smith 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __FUNCTION__, mc->mc_mailbox[0]);
10491ac4b82bSMike Smith 	break;
10501ac4b82bSMike Smith     }
10511ac4b82bSMike Smith 
10521ac4b82bSMike Smith  out:
10531ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
10541ac4b82bSMike Smith     mlx_releasecmd(mc);
10551ac4b82bSMike Smith }
10561ac4b82bSMike Smith 
10571ac4b82bSMike Smith /********************************************************************************
10581ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
10591ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
10601ac4b82bSMike Smith  */
10611ac4b82bSMike Smith static void
10621ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
10631ac4b82bSMike Smith {
10641ac4b82bSMike Smith     struct mlx_command	*mc;
10651ac4b82bSMike Smith     void		*result = NULL;
10661ac4b82bSMike Smith     int			error;
10671ac4b82bSMike Smith 
10681ac4b82bSMike Smith     debug("called");
10691ac4b82bSMike Smith 
10701ac4b82bSMike Smith     /* get ourselves a command buffer */
10711ac4b82bSMike Smith     error = 1;
10721ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
10731ac4b82bSMike Smith 	goto out;
10741ac4b82bSMike Smith     /* allocate the response structure */
107533c8cb18SMike Smith     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL)
10761ac4b82bSMike Smith 	goto out;
10771ac4b82bSMike Smith     /* get a command slot */
10781ac4b82bSMike Smith     if (mlx_getslot(mc))
10791ac4b82bSMike Smith 	goto out;
10801ac4b82bSMike Smith 
10811ac4b82bSMike Smith     /* map the command so the controller can see it */
10821ac4b82bSMike Smith     mc->mc_data = result;
108333c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
10841ac4b82bSMike Smith     mlx_mapcmd(mc);
10851ac4b82bSMike Smith 
10861ac4b82bSMike Smith     /* build the command to get one entry */
10871ac4b82bSMike Smith     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
10881ac4b82bSMike Smith     mc->mc_complete = mlx_periodic_eventlog_respond;
10891ac4b82bSMike Smith     mc->mc_private = mc;
10901ac4b82bSMike Smith 
10911ac4b82bSMike Smith     /* start the command */
10921ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
10931ac4b82bSMike Smith 	goto out;
10941ac4b82bSMike Smith 
10951ac4b82bSMike Smith     error = 0;			/* success */
10961ac4b82bSMike Smith  out:
109733c8cb18SMike Smith     if (error != 0) {
10981ac4b82bSMike Smith 	if (mc != NULL)
10991ac4b82bSMike Smith 	    mlx_releasecmd(mc);
110033c8cb18SMike Smith 	if (result != NULL)
11011ac4b82bSMike Smith 	    free(result, M_DEVBUF);
11021ac4b82bSMike Smith     }
110333c8cb18SMike Smith }
11041ac4b82bSMike Smith 
11051ac4b82bSMike Smith /********************************************************************************
11061ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
11071ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
11081ac4b82bSMike Smith  */
11091ac4b82bSMike Smith static char *mlx_sense_messages[] = {
11101ac4b82bSMike Smith     "because write recovery failed",
11111ac4b82bSMike Smith     "because of SCSI bus reset failure",
11121ac4b82bSMike Smith     "because of double check condition",
11131ac4b82bSMike Smith     "because it was removed",
11141ac4b82bSMike Smith     "because of gross error on SCSI chip",
11151ac4b82bSMike Smith     "because of bad tag returned from drive",
11161ac4b82bSMike Smith     "because of timeout on SCSI command",
11171ac4b82bSMike Smith     "because of reset SCSI command issued from system",
11181ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
11191ac4b82bSMike Smith     "because of 'kill drive' command from system",
11201ac4b82bSMike Smith     "because of selection timeout",
11211ac4b82bSMike Smith     "due to SCSI phase sequence error",
11221ac4b82bSMike Smith     "due to unknown status"
11231ac4b82bSMike Smith };
11241ac4b82bSMike Smith 
11251ac4b82bSMike Smith static void
11261ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
11271ac4b82bSMike Smith {
11281ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
11291ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
11301ac4b82bSMike Smith     char			*reason;
11311ac4b82bSMike Smith 
11321ac4b82bSMike Smith     debug("called");
11331ac4b82bSMike Smith 
11345792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
11351ac4b82bSMike Smith     if (mc->mc_status == 0) {
11361ac4b82bSMike Smith 
11371ac4b82bSMike Smith 	/* handle event log message */
11381ac4b82bSMike Smith 	switch(el->el_type) {
11391ac4b82bSMike Smith 	    /*
11401ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
11411ac4b82bSMike Smith 	     * The tests here are probably incomplete.
11421ac4b82bSMike Smith 	     */
11431ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
11441ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
11451ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
11461ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
11471ac4b82bSMike Smith 		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
11481ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
11491ac4b82bSMike Smith 		} else {
11501ac4b82bSMike Smith 		    reason = "for unknown reason";
11511ac4b82bSMike Smith 		}
11521ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
11531ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
11541ac4b82bSMike Smith 	    }
11551ac4b82bSMike Smith 	    /* SCSI drive was reset? */
11561ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
11571ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
11581ac4b82bSMike Smith 			      el->el_channel, el->el_target);
11591ac4b82bSMike Smith 	    }
11601ac4b82bSMike Smith 	    /* SCSI drive error? */
11611ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
11621ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
11631ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
11641ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
11651ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
11661ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
11671ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
11681ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
11691ac4b82bSMike Smith 	    }
11701ac4b82bSMike Smith 	    break;
11711ac4b82bSMike Smith 
11721ac4b82bSMike Smith 	default:
11731ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
11741ac4b82bSMike Smith 	    break;
11751ac4b82bSMike Smith 	}
11761ac4b82bSMike Smith     } else {
11771ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
11781ac4b82bSMike Smith     }
11791ac4b82bSMike Smith 
11801ac4b82bSMike Smith     /* dispose of command and data */
11811ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
11821ac4b82bSMike Smith     mlx_releasecmd(mc);
11831ac4b82bSMike Smith 
11841ac4b82bSMike Smith     /* is there another message to obtain? */
11851ac4b82bSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent)
11861ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
11871ac4b82bSMike Smith }
11881ac4b82bSMike Smith 
11891ac4b82bSMike Smith /********************************************************************************
11901ac4b82bSMike Smith  * Handle the completion of a rebuild operation.
11911ac4b82bSMike Smith  */
11921ac4b82bSMike Smith static void
11931ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
11941ac4b82bSMike Smith {
11951ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
11961ac4b82bSMike Smith     struct mlx_rebuild_stat	*mr = (struct mlx_rebuild_stat *)mc->mc_private;
11971ac4b82bSMike Smith 
11981ac4b82bSMike Smith     switch(mc->mc_status) {
11991ac4b82bSMike Smith     case 0:				/* all OK, rebuild still running */
12001ac4b82bSMike Smith 	sc->mlx_rebuildstat = mr->rb_remaining;
12011ac4b82bSMike Smith 	break;
12021ac4b82bSMike Smith 
12031ac4b82bSMike Smith     case 0x0105:			/* rebuild/check finished */
12041ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {
12051ac4b82bSMike Smith 	    device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild completed\n");
12061ac4b82bSMike Smith 	    sc->mlx_rebuild = -1;
12071ac4b82bSMike Smith 	} else if (sc->mlx_check >= 0) {
12081ac4b82bSMike Smith 	    device_printf(sc->mlx_sysdrive[sc->mlx_check].ms_disk, "consistency check completed\n");
12091ac4b82bSMike Smith 	    sc->mlx_check = -1;
12101ac4b82bSMike Smith 	} else {
12111ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");
12121ac4b82bSMike Smith 	}
12131ac4b82bSMike Smith 	break;
12141ac4b82bSMike Smith     }
12151ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12161ac4b82bSMike Smith     mlx_releasecmd(mc);
12171ac4b82bSMike Smith }
12181ac4b82bSMike Smith 
12191ac4b82bSMike Smith /********************************************************************************
12201ac4b82bSMike Smith  ********************************************************************************
12211ac4b82bSMike Smith                                                                     Channel Pause
12221ac4b82bSMike Smith  ********************************************************************************
12231ac4b82bSMike Smith  ********************************************************************************/
12241ac4b82bSMike Smith 
12251ac4b82bSMike Smith /********************************************************************************
12261ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
12271ac4b82bSMike Smith  * the pause.
12281ac4b82bSMike Smith  */
12291ac4b82bSMike Smith static void
12301ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
12311ac4b82bSMike Smith {
12321ac4b82bSMike Smith     struct mlx_command	*mc;
12331ac4b82bSMike Smith     int			failsafe, i, command;
12341ac4b82bSMike Smith 
12351ac4b82bSMike Smith     /* What are we doing here? */
12361ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
12371ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
12381ac4b82bSMike Smith 	failsafe = 0;
12391ac4b82bSMike Smith 
12401ac4b82bSMike Smith     } else {
12411ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
12421ac4b82bSMike Smith 
12431ac4b82bSMike Smith 	/*
12441ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
12451ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
12461ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
12471ac4b82bSMike Smith 	 */
12481ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
12491ac4b82bSMike Smith 	if (failsafe > 0xf) {
12501ac4b82bSMike Smith 	    failsafe = 0xf;
12511ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
12521ac4b82bSMike Smith 	}
12531ac4b82bSMike Smith     }
12541ac4b82bSMike Smith 
12551ac4b82bSMike Smith     /* build commands for every channel requested */
12569eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
12571ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
12581ac4b82bSMike Smith 
12591ac4b82bSMike Smith 	    /* get ourselves a command buffer */
12601ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
12611ac4b82bSMike Smith 		goto fail;
12621ac4b82bSMike Smith 	    /* get a command slot */
12631ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
12641ac4b82bSMike Smith 	    if (mlx_getslot(mc))
12651ac4b82bSMike Smith 		goto fail;
12661ac4b82bSMike Smith 
12671ac4b82bSMike Smith 	    /* build the command */
12681ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
12691ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
12701ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
12711ac4b82bSMike Smith 	    if (mlx_start(mc))
12721ac4b82bSMike Smith 		goto fail;
12731ac4b82bSMike Smith 	    /* command submitted OK */
12741ac4b82bSMike Smith 	    return;
12751ac4b82bSMike Smith 
12761ac4b82bSMike Smith 	fail:
12771ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
12781ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
12791ac4b82bSMike Smith 	    if (mc != NULL)
12801ac4b82bSMike Smith 		mlx_releasecmd(mc);
12811ac4b82bSMike Smith 	}
12821ac4b82bSMike Smith     }
12831ac4b82bSMike Smith }
12841ac4b82bSMike Smith 
12851ac4b82bSMike Smith static void
12861ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
12871ac4b82bSMike Smith {
12881ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
12891ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
12901ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
12911ac4b82bSMike Smith 
12921ac4b82bSMike Smith     if (mc->mc_status != 0) {
12931ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
12941ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
12951ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
12961ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
129772c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
12981ac4b82bSMike Smith     } else {
12991ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
13001ac4b82bSMike Smith     }
13011ac4b82bSMike Smith     mlx_releasecmd(mc);
13021ac4b82bSMike Smith }
13031ac4b82bSMike Smith 
13041ac4b82bSMike Smith /********************************************************************************
13051ac4b82bSMike Smith  ********************************************************************************
13061ac4b82bSMike Smith                                                                Command Submission
13071ac4b82bSMike Smith  ********************************************************************************
13081ac4b82bSMike Smith  ********************************************************************************/
13091ac4b82bSMike Smith 
13101ac4b82bSMike Smith /********************************************************************************
13111ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
13121ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
13131ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
13141ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
13151ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
13161ac4b82bSMike Smith  */
13171ac4b82bSMike Smith static void *
13181ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
13191ac4b82bSMike Smith {
13201ac4b82bSMike Smith     struct mlx_command	*mc;
13211ac4b82bSMike Smith     void		*result;
13221ac4b82bSMike Smith     int			error;
13231ac4b82bSMike Smith 
13241ac4b82bSMike Smith     debug("called");
13251ac4b82bSMike Smith 
13261ac4b82bSMike Smith     /* get ourselves a command buffer */
13271ac4b82bSMike Smith     error = 1;
13281ac4b82bSMike Smith     result = NULL;
13291ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
13301ac4b82bSMike Smith 	goto out;
13311ac4b82bSMike Smith     /* allocate the response structure */
13321ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
13331ac4b82bSMike Smith 	goto out;
13341ac4b82bSMike Smith     /* get a command slot */
13351ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
13361ac4b82bSMike Smith     if (mlx_getslot(mc))
13371ac4b82bSMike Smith 	goto out;
13381ac4b82bSMike Smith 
13391ac4b82bSMike Smith     /* map the command so the controller can see it */
13401ac4b82bSMike Smith     mc->mc_data = result;
13411ac4b82bSMike Smith     mc->mc_length = bufsize;
13421ac4b82bSMike Smith     mlx_mapcmd(mc);
13431ac4b82bSMike Smith 
13441ac4b82bSMike Smith     /* build an enquiry command */
13451ac4b82bSMike Smith     mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
13461ac4b82bSMike Smith 
13471ac4b82bSMike Smith     /* do we want a completion callback? */
13481ac4b82bSMike Smith     if (complete != NULL) {
13491ac4b82bSMike Smith 	mc->mc_complete = complete;
13501ac4b82bSMike Smith 	mc->mc_private = mc;
13511ac4b82bSMike Smith 	if ((error = mlx_start(mc)) != 0)
13521ac4b82bSMike Smith 	    goto out;
13531ac4b82bSMike Smith     } else {
13541ac4b82bSMike Smith 	/* run the command in either polled or wait mode */
13551ac4b82bSMike Smith 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
13561ac4b82bSMike Smith 	    goto out;
13571ac4b82bSMike Smith 
13581ac4b82bSMike Smith 	/* command completed OK? */
13591ac4b82bSMike Smith 	if (mc->mc_status != 0) {
13601ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc));
13611ac4b82bSMike Smith 	    goto out;
13621ac4b82bSMike Smith 	}
13631ac4b82bSMike Smith     }
13641ac4b82bSMike Smith     error = 0;			/* success */
13651ac4b82bSMike Smith  out:
13661ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
13671ac4b82bSMike Smith     if ((complete == NULL) && (mc != NULL))
13681ac4b82bSMike Smith 	mlx_releasecmd(mc);
136933c8cb18SMike Smith     /* we got an error, and we allocated a result */
13701ac4b82bSMike Smith     if ((error != 0) && (result != NULL)) {
13711ac4b82bSMike Smith 	free(result, M_DEVBUF);
13721ac4b82bSMike Smith 	result = NULL;
13731ac4b82bSMike Smith     }
13741ac4b82bSMike Smith     return(result);
13751ac4b82bSMike Smith }
13761ac4b82bSMike Smith 
13771ac4b82bSMike Smith 
13781ac4b82bSMike Smith /********************************************************************************
13791ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
13801ac4b82bSMike Smith  *
13811ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
13821ac4b82bSMike Smith  * the flush operation completes or fails.
13831ac4b82bSMike Smith  */
13841ac4b82bSMike Smith static int
13851ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
13861ac4b82bSMike Smith {
13871ac4b82bSMike Smith     struct mlx_command	*mc;
13881ac4b82bSMike Smith     int			error;
13891ac4b82bSMike Smith 
13901ac4b82bSMike Smith     debug("called");
13911ac4b82bSMike Smith 
13921ac4b82bSMike Smith     /* get ourselves a command buffer */
13931ac4b82bSMike Smith     error = 1;
13941ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
13951ac4b82bSMike Smith 	goto out;
13961ac4b82bSMike Smith     /* get a command slot */
13971ac4b82bSMike Smith     if (mlx_getslot(mc))
13981ac4b82bSMike Smith 	goto out;
13991ac4b82bSMike Smith 
14001ac4b82bSMike Smith     /* build a flush command */
14011ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
14021ac4b82bSMike Smith 
14035792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
14045792b7feSMike Smith     if (mlx_poll_command(mc))
14051ac4b82bSMike Smith 	goto out;
14061ac4b82bSMike Smith 
14071ac4b82bSMike Smith     /* command completed OK? */
14081ac4b82bSMike Smith     if (mc->mc_status != 0) {
14091ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
14101ac4b82bSMike Smith 	goto out;
14111ac4b82bSMike Smith     }
14121ac4b82bSMike Smith 
14131ac4b82bSMike Smith     error = 0;			/* success */
14141ac4b82bSMike Smith  out:
14151ac4b82bSMike Smith     if (mc != NULL)
14161ac4b82bSMike Smith 	mlx_releasecmd(mc);
14171ac4b82bSMike Smith     return(error);
14181ac4b82bSMike Smith }
14191ac4b82bSMike Smith 
14201ac4b82bSMike Smith /********************************************************************************
14211ac4b82bSMike Smith  * Start a background rebuild on the nominated controller/channel/target.
14221ac4b82bSMike Smith  *
14231ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
14241ac4b82bSMike Smith  * operation has started or been refused.
14251ac4b82bSMike Smith  */
14261ac4b82bSMike Smith static int
14271ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
14281ac4b82bSMike Smith {
14291ac4b82bSMike Smith     struct mlx_command	*mc;
14301ac4b82bSMike Smith     int			error;
14311ac4b82bSMike Smith 
14321ac4b82bSMike Smith     debug("called");
14331ac4b82bSMike Smith 
14341ac4b82bSMike Smith     /* get ourselves a command buffer */
14351ac4b82bSMike Smith     error = 0x10000;
14361ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14371ac4b82bSMike Smith 	goto out;
14381ac4b82bSMike Smith     /* get a command slot */
14391ac4b82bSMike Smith     if (mlx_getslot(mc))
14401ac4b82bSMike Smith 	goto out;
14411ac4b82bSMike Smith 
14421ac4b82bSMike Smith     /* build a rebuild command */
14431ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
14441ac4b82bSMike Smith 
14451ac4b82bSMike Smith     /* run the command in either polled or wait mode */
14461ac4b82bSMike Smith     if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
14471ac4b82bSMike Smith 	goto out;
14481ac4b82bSMike Smith 
14491ac4b82bSMike Smith     /* command completed OK? */
14501ac4b82bSMike Smith     if (mc->mc_status != 0) {
14511ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
14521ac4b82bSMike Smith     } else {
14531ac4b82bSMike Smith 	device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild started");
14541ac4b82bSMike Smith     }
14551ac4b82bSMike Smith     error = mc->mc_status;
14561ac4b82bSMike Smith 
14571ac4b82bSMike Smith  out:
14581ac4b82bSMike Smith     if (mc != NULL)
14591ac4b82bSMike Smith 	mlx_releasecmd(mc);
14601ac4b82bSMike Smith     return(error);
14611ac4b82bSMike Smith }
14621ac4b82bSMike Smith 
14631ac4b82bSMike Smith /********************************************************************************
14641ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
14651ac4b82bSMike Smith  *
14661ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
14671ac4b82bSMike Smith  */
14681ac4b82bSMike Smith static int
14691ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
14701ac4b82bSMike Smith {
14711ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
14721ac4b82bSMike Smith     int			error, count;
14731ac4b82bSMike Smith 
14741ac4b82bSMike Smith     debug("called");
14751ac4b82bSMike Smith 
14761ac4b82bSMike Smith     mc->mc_complete = NULL;
14771ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
14781ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
14791ac4b82bSMike Smith 	return(error);
14801ac4b82bSMike Smith 
14811ac4b82bSMike Smith     count = 0;
14821ac4b82bSMike Smith     /* XXX better timeout? */
14831ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
14841ac4b82bSMike Smith 	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
14851ac4b82bSMike Smith     }
14861ac4b82bSMike Smith 
14871ac4b82bSMike Smith     if (mc->mc_status != 0) {
14881ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
14891ac4b82bSMike Smith 	return(EIO);
14901ac4b82bSMike Smith     }
14911ac4b82bSMike Smith     return(0);
14921ac4b82bSMike Smith }
14931ac4b82bSMike Smith 
14941ac4b82bSMike Smith 
14951ac4b82bSMike Smith /********************************************************************************
14961ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
14971ac4b82bSMike Smith  *
14981ac4b82bSMike Smith  * Should only be used when interrupts are not available. Returns 0 on
14991ac4b82bSMike Smith  * success, nonzero on error.
15001ac4b82bSMike Smith  * Successfully completed commands are dequeued.
15011ac4b82bSMike Smith  */
15021ac4b82bSMike Smith static int
15031ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
15041ac4b82bSMike Smith {
15051ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
15061ac4b82bSMike Smith     int			error, count, s;
15071ac4b82bSMike Smith 
15081ac4b82bSMike Smith     debug("called");
15091ac4b82bSMike Smith 
15101ac4b82bSMike Smith     mc->mc_complete = NULL;
15111ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
15121ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
15131ac4b82bSMike Smith 	return(error);
15141ac4b82bSMike Smith 
15151ac4b82bSMike Smith     count = 0;
15161ac4b82bSMike Smith     do {
15171ac4b82bSMike Smith 	/* poll for completion */
15181ac4b82bSMike Smith 	mlx_done(mc->mc_sc);
15191ac4b82bSMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 10000));
15201ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
15211ac4b82bSMike Smith 	s = splbio();
15224b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
15231ac4b82bSMike Smith 	splx(s);
15241ac4b82bSMike Smith 	return(0);
15251ac4b82bSMike Smith     }
15261ac4b82bSMike Smith     device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
15271ac4b82bSMike Smith     return(EIO);
15281ac4b82bSMike Smith }
15291ac4b82bSMike Smith 
15301ac4b82bSMike Smith /********************************************************************************
15311ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
15321ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
15331ac4b82bSMike Smith  *
15341ac4b82bSMike Smith  * Must be called at splbio or in an equivalent fashion that prevents
15351ac4b82bSMike Smith  * reentry or activity on the bufq..
15361ac4b82bSMike Smith  */
15371ac4b82bSMike Smith static void
15381ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
15391ac4b82bSMike Smith {
15401ac4b82bSMike Smith     struct mlx_command	*mc;
15411ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
15421ac4b82bSMike Smith     struct buf		*bp;
15431ac4b82bSMike Smith     int			blkcount;
15441ac4b82bSMike Smith     int			driveno;
15451ac4b82bSMike Smith     int			cmd;
15464b006d7bSMike Smith     int			s;
15471ac4b82bSMike Smith 
15485792b7feSMike Smith     /* avoid reentrancy */
15495792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_STARTING))
15505792b7feSMike Smith 	return;
15515792b7feSMike Smith 
15521ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
15534b006d7bSMike Smith     s = splbio();
15541ac4b82bSMike Smith     for (;;) {
15551ac4b82bSMike Smith 
15561ac4b82bSMike Smith 	/* see if there's work to be done */
15571ac4b82bSMike Smith 	if ((bp = bufq_first(&sc->mlx_bufq)) == NULL)
15581ac4b82bSMike Smith 	    break;
15591ac4b82bSMike Smith 	/* get a command */
15601ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
15611ac4b82bSMike Smith 	    break;
15621ac4b82bSMike Smith 	/* get a slot for the command */
15631ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
15641ac4b82bSMike Smith 	    mlx_releasecmd(mc);
15651ac4b82bSMike Smith 	    break;
15661ac4b82bSMike Smith 	}
15671ac4b82bSMike Smith 	/* get the buf containing our work */
15681ac4b82bSMike Smith 	bufq_remove(&sc->mlx_bufq, bp);
15691ac4b82bSMike Smith 	sc->mlx_waitbufs--;
15704b006d7bSMike Smith 	splx(s);
15711ac4b82bSMike Smith 
15721ac4b82bSMike Smith 	/* connect the buf to the command */
15731ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
15741ac4b82bSMike Smith 	mc->mc_private = bp;
15751ac4b82bSMike Smith 	mc->mc_data = bp->b_data;
15761ac4b82bSMike Smith 	mc->mc_length = bp->b_bcount;
15771ac4b82bSMike Smith 	if (bp->b_flags & B_READ) {
15781ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAIN;
15791ac4b82bSMike Smith 	    cmd = MLX_CMD_READOLDSG;
15801ac4b82bSMike Smith 	} else {
15811ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAOUT;
15821ac4b82bSMike Smith 	    cmd = MLX_CMD_WRITEOLDSG;
15831ac4b82bSMike Smith 	}
15841ac4b82bSMike Smith 
15851ac4b82bSMike Smith 	/* map the command so the controller can work with it */
15861ac4b82bSMike Smith 	mlx_mapcmd(mc);
15871ac4b82bSMike Smith 
15881ac4b82bSMike Smith 	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
1589f6b84b08SMike Smith 	mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1;
15905792b7feSMike Smith 	driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
159197adfbafSMike Smith 	blkcount = (bp->b_bcount + MLX_BLKSIZE - 1) / MLX_BLKSIZE;
15921ac4b82bSMike Smith 
1593cd4ace0cSMike Smith 	if ((bp->b_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
15941ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n",
1595cd4ace0cSMike Smith 			  bp->b_pblkno, blkcount, sc->mlx_sysdrive[driveno].ms_size);
15961ac4b82bSMike Smith 
15971ac4b82bSMike Smith 	/*
15981ac4b82bSMike Smith 	 * Build the I/O command.  Note that the SG list type bits are set to zero,
15991ac4b82bSMike Smith 	 * denoting the format of SG list that we are using.
16001ac4b82bSMike Smith 	 */
16011ac4b82bSMike Smith 	mlx_make_type5(mc, cmd,
16021ac4b82bSMike Smith 		       blkcount & 0xff, 				/* xfer length low byte */
1603f01f2af6SMike Smith 		       (driveno << 3) | ((blkcount >> 8) & 0x07),	/* target and length high 3 bits */
1604cd4ace0cSMike Smith 		       bp->b_pblkno,					/* physical block number */
16051ac4b82bSMike Smith 		       mc->mc_sgphys,					/* location of SG list */
16061ac4b82bSMike Smith 		       mc->mc_nsgent & 0x3f);				/* size of SG list (top 2 bits clear) */
16071ac4b82bSMike Smith 
16081ac4b82bSMike Smith 
16091ac4b82bSMike Smith 	/* try to give command to controller */
16101ac4b82bSMike Smith 	if (mlx_start(mc) != 0) {
16111ac4b82bSMike Smith 	    /* fail the command */
16121ac4b82bSMike Smith 	    mc->mc_status = MLX_STATUS_WEDGED;
16131ac4b82bSMike Smith 	    mlx_completeio(mc);
16141ac4b82bSMike Smith 	}
16154b006d7bSMike Smith 	s = splbio();
16161ac4b82bSMike Smith     }
16174b006d7bSMike Smith     splx(s);
16185792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_STARTING);
16191ac4b82bSMike Smith }
16201ac4b82bSMike Smith 
16211ac4b82bSMike Smith /********************************************************************************
16221ac4b82bSMike Smith  * Handle completion of an I/O command.
16231ac4b82bSMike Smith  */
16241ac4b82bSMike Smith static void
16251ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
16261ac4b82bSMike Smith {
16271ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16281ac4b82bSMike Smith     struct buf		*bp = (struct buf *)mc->mc_private;
1629f6b84b08SMike Smith     struct mlxd_softc	*mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1;
16301ac4b82bSMike Smith 
16311ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
16321ac4b82bSMike Smith 	bp->b_error = EIO;
16331ac4b82bSMike Smith 	bp->b_flags |= B_ERROR;
16341ac4b82bSMike Smith 
16351ac4b82bSMike Smith 	switch(mc->mc_status) {
16361ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
16371ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1638f6b84b08SMike Smith 	    /* should signal this with a return code */
16391ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
16401ac4b82bSMike Smith 	    break;
16411ac4b82bSMike Smith 
16421ac4b82bSMike Smith 	default:				/* other I/O error */
16431ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
16441ac4b82bSMike Smith #if 0
1645cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
1646cd4ace0cSMike Smith 			  bp->b_bcount, bp->b_bcount / MLX_BLKSIZE, bp->b_pblkno);
16471ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
16481ac4b82bSMike Smith #endif
16491ac4b82bSMike Smith 	    break;
16501ac4b82bSMike Smith 	}
16511ac4b82bSMike Smith     }
16521ac4b82bSMike Smith     mlx_releasecmd(mc);
16531ac4b82bSMike Smith     mlxd_intr(bp);
16541ac4b82bSMike Smith }
16551ac4b82bSMike Smith 
16561ac4b82bSMike Smith /********************************************************************************
16571ac4b82bSMike Smith  * Take a command from user-space and try to run it.
16581ac4b82bSMike Smith  */
16591ac4b82bSMike Smith static int
16601ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
16611ac4b82bSMike Smith {
16621ac4b82bSMike Smith     struct mlx_command	*mc;
16631ac4b82bSMike Smith     void		*kbuf;
16641ac4b82bSMike Smith     int			error;
16651ac4b82bSMike Smith 
16661ac4b82bSMike Smith     kbuf = NULL;
16671ac4b82bSMike Smith     mc = NULL;
16681ac4b82bSMike Smith     error = ENOMEM;
16691ac4b82bSMike Smith     /* get a kernel buffer for the transfer */
16701ac4b82bSMike Smith     if (mu->mu_datasize > 0) {
16711ac4b82bSMike Smith 	if ((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL)
16721ac4b82bSMike Smith 	    goto out;
16731ac4b82bSMike Smith 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) < sizeof(u_int32_t)))) {
16741ac4b82bSMike Smith 	    error = EINVAL;
16751ac4b82bSMike Smith 	    goto out;
16761ac4b82bSMike Smith 	}
16771ac4b82bSMike Smith     }
16781ac4b82bSMike Smith     /* get ourselves a command buffer */
16791ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16801ac4b82bSMike Smith 	goto out;
16811ac4b82bSMike Smith 
16821ac4b82bSMike Smith     /* copy the command and data */
16831ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
16841ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))))
16851ac4b82bSMike Smith 	goto out;
16861ac4b82bSMike Smith 
16871ac4b82bSMike Smith     /* get a command slot */
16881ac4b82bSMike Smith     if (mlx_getslot(mc))
16891ac4b82bSMike Smith 	goto out;
16901ac4b82bSMike Smith 
16911ac4b82bSMike Smith     /* map the command so the controller can see it */
16921ac4b82bSMike Smith     mc->mc_data = kbuf;
16931ac4b82bSMike Smith     mc->mc_length = mu->mu_datasize;
16941ac4b82bSMike Smith     mlx_mapcmd(mc);
16951ac4b82bSMike Smith 
16961ac4b82bSMike Smith     /* if there's a data buffer, fix up the command */
16971ac4b82bSMike Smith     if (mu->mu_datasize > 0) {
16981ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_length        & 0xff;
16991ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_length >> 8)  & 0xff;
17001ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_length >> 16) & 0xff;
17011ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_length >> 24) & 0xff;
17021ac4b82bSMike Smith     }
17031ac4b82bSMike Smith 
17041ac4b82bSMike Smith     /* submit the command and wait */
17051ac4b82bSMike Smith     if ((error = mlx_wait_command(mc)) != 0)
17061ac4b82bSMike Smith 	goto out;
17071ac4b82bSMike Smith 
17081ac4b82bSMike Smith     /* copy out status and data */
17091ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
17101ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
17111ac4b82bSMike Smith 	goto out;
17121ac4b82bSMike Smith     error = 0;
17131ac4b82bSMike Smith 
17141ac4b82bSMike Smith  out:
17151ac4b82bSMike Smith     mlx_releasecmd(mc);
17161ac4b82bSMike Smith     if (kbuf != NULL)
17171ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
17181ac4b82bSMike Smith     return(error);
17191ac4b82bSMike Smith }
17201ac4b82bSMike Smith 
17211ac4b82bSMike Smith /********************************************************************************
17221ac4b82bSMike Smith  ********************************************************************************
17231ac4b82bSMike Smith                                                         Command I/O to Controller
17241ac4b82bSMike Smith  ********************************************************************************
17251ac4b82bSMike Smith  ********************************************************************************/
17261ac4b82bSMike Smith 
17271ac4b82bSMike Smith /********************************************************************************
17281ac4b82bSMike Smith  * Find a free command slot for (mc).
17291ac4b82bSMike Smith  *
17301ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
17311ac4b82bSMike Smith  * 4 slots free for priority commands.
17321ac4b82bSMike Smith  */
17331ac4b82bSMike Smith static int
17341ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
17351ac4b82bSMike Smith {
17361ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17379eee27f1SMike Smith     int			s, slot;
17381ac4b82bSMike Smith 
17391ac4b82bSMike Smith     debug("called  mc %p  sc %p", mc, sc);
17401ac4b82bSMike Smith 
17411ac4b82bSMike Smith     /* enforce slot-usage limit */
17429eee27f1SMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ?
17439eee27f1SMike Smith 			     sc->mlx_maxiop : sc->mlx_maxiop - 4))
17441ac4b82bSMike Smith 	return(EBUSY);
17451ac4b82bSMike Smith 
17461ac4b82bSMike Smith     /*
17471ac4b82bSMike Smith      * Allocate an outstanding command slot
17481ac4b82bSMike Smith      *
17491ac4b82bSMike Smith      * XXX linear search is slow
17501ac4b82bSMike Smith      */
17511ac4b82bSMike Smith     s = splbio();
17521ac4b82bSMike Smith     for (slot = 0; slot < sc->mlx_maxiop; slot++) {
17531ac4b82bSMike Smith 	debug("try slot %d", slot);
17541ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
17551ac4b82bSMike Smith 	    break;
17561ac4b82bSMike Smith     }
17571ac4b82bSMike Smith     if (slot < sc->mlx_maxiop) {
17581ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
17591ac4b82bSMike Smith 	sc->mlx_busycmds++;
17601ac4b82bSMike Smith     }
17611ac4b82bSMike Smith     splx(s);
17621ac4b82bSMike Smith 
17631ac4b82bSMike Smith     /* out of slots? */
17641ac4b82bSMike Smith     if (slot >= sc->mlx_maxiop)
17651ac4b82bSMike Smith 	return(EBUSY);
17661ac4b82bSMike Smith 
17671ac4b82bSMike Smith     debug("got slot %d", slot);
17681ac4b82bSMike Smith     mc->mc_slot = slot;
17691ac4b82bSMike Smith     return(0);
17701ac4b82bSMike Smith }
17711ac4b82bSMike Smith 
17721ac4b82bSMike Smith /********************************************************************************
17731ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
17741ac4b82bSMike Smith  */
17751ac4b82bSMike Smith static void
17761ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
17771ac4b82bSMike Smith {
17781ac4b82bSMike Smith     struct mlx_command	*mc = (struct mlx_command *)arg;
17791ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17801ac4b82bSMike Smith     struct mlx_sgentry	*sg;
17811ac4b82bSMike Smith     int			i;
17821ac4b82bSMike Smith 
17831ac4b82bSMike Smith     debug("called");
17841ac4b82bSMike Smith 
17851ac4b82bSMike Smith     /* get base address of s/g table */
17861ac4b82bSMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
17871ac4b82bSMike Smith 
17881ac4b82bSMike Smith     /* save s/g table information in command */
17891ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
17901ac4b82bSMike Smith     mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
17911ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
17921ac4b82bSMike Smith 
17931ac4b82bSMike Smith     /* populate s/g table */
17941ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
17951ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
17961ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
17971ac4b82bSMike Smith     }
17981ac4b82bSMike Smith }
17991ac4b82bSMike Smith 
18001ac4b82bSMike Smith static void
18011ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc)
18021ac4b82bSMike Smith {
18031ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18041ac4b82bSMike Smith 
18051ac4b82bSMike Smith     debug("called");
18061ac4b82bSMike Smith 
18071ac4b82bSMike Smith     /* if the command involves data at all */
18081ac4b82bSMike Smith     if (mc->mc_data != NULL) {
18091ac4b82bSMike Smith 
18101ac4b82bSMike Smith 	/* map the data buffer into bus space and build the s/g list */
18111ac4b82bSMike Smith 	bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length,
18121ac4b82bSMike Smith 			mlx_setup_dmamap, mc, 0);
18131ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
18141ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD);
18151ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
18161ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE);
18171ac4b82bSMike Smith     }
18181ac4b82bSMike Smith }
18191ac4b82bSMike Smith 
18201ac4b82bSMike Smith static void
18211ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
18221ac4b82bSMike Smith {
18231ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18241ac4b82bSMike Smith 
18251ac4b82bSMike Smith     debug("called");
18261ac4b82bSMike Smith 
18271ac4b82bSMike Smith     /* if the command involved data at all */
18281ac4b82bSMike Smith     if (mc->mc_data != NULL) {
18291ac4b82bSMike Smith 
18301ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
18311ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
18321ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
18331ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
18341ac4b82bSMike Smith 
18351ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
18361ac4b82bSMike Smith     }
18371ac4b82bSMike Smith }
18381ac4b82bSMike Smith 
18391ac4b82bSMike Smith /********************************************************************************
18405792b7feSMike Smith  * Try to deliver (mc) to the controller.
18411ac4b82bSMike Smith  *
18421ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
18431ac4b82bSMike Smith  */
18441ac4b82bSMike Smith static int
18451ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
18461ac4b82bSMike Smith {
18471ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18485792b7feSMike Smith     int			i, s, done;
18491ac4b82bSMike Smith 
18501ac4b82bSMike Smith     debug("called");
18511ac4b82bSMike Smith 
18521ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
18531ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
18541ac4b82bSMike Smith 
18554b006d7bSMike Smith     /* mark the command as currently being processed */
18561ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
18571ac4b82bSMike Smith 
18585792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
18595792b7feSMike Smith     mc->mc_timeout = time_second + 60;
18601ac4b82bSMike Smith 
18611ac4b82bSMike Smith     /* spin waiting for the mailbox */
18621ac4b82bSMike Smith     for (i = 100000, done = 0; (i > 0) && !done; i--) {
18631ac4b82bSMike Smith 	s = splbio();
18644b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
18654b006d7bSMike Smith 	    done = 1;
18664b006d7bSMike Smith 	    /* move command to work queue */
18674b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
18684b006d7bSMike Smith 	}
18695792b7feSMike Smith 	splx(s);	/* drop spl to allow completion interrupts */
18701ac4b82bSMike Smith     }
18711ac4b82bSMike Smith 
18721ac4b82bSMike Smith     /* command is enqueued */
18731ac4b82bSMike Smith     if (done)
18741ac4b82bSMike Smith 	return(0);
18751ac4b82bSMike Smith 
18761ac4b82bSMike Smith     /*
18771ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
18781ac4b82bSMike Smith      * that the command was given and return it with a bad status.
18791ac4b82bSMike Smith      */
18801ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
18811ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
18821ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
18835792b7feSMike Smith     mlx_complete(sc);
18841ac4b82bSMike Smith     return(EIO);
18851ac4b82bSMike Smith }
18861ac4b82bSMike Smith 
18871ac4b82bSMike Smith /********************************************************************************
18885792b7feSMike Smith  * Poll the controller (sc) for completed commands.
18895792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
18905792b7feSMike Smith  * new commands may be posted.
18911ac4b82bSMike Smith  *
18925792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
18931ac4b82bSMike Smith  */
18941ac4b82bSMike Smith static int
18951ac4b82bSMike Smith mlx_done(struct mlx_softc *sc)
18961ac4b82bSMike Smith {
18971ac4b82bSMike Smith     struct mlx_command	*mc;
18985792b7feSMike Smith     int			s, result;
18991ac4b82bSMike Smith     u_int8_t		slot;
19001ac4b82bSMike Smith     u_int16_t		status;
19011ac4b82bSMike Smith 
19021ac4b82bSMike Smith     debug("called");
19031ac4b82bSMike Smith 
19045792b7feSMike Smith     result = 0;
19051ac4b82bSMike Smith 
19065792b7feSMike Smith     /* loop collecting completed commands */
19074b006d7bSMike Smith     s = splbio();
19085792b7feSMike Smith     for (;;) {
19095792b7feSMike Smith 	/* poll for a completed command's identifier and status */
19101ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
19115792b7feSMike Smith 	    result = 1;
19121ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
19131ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
19141ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
19151ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
19161ac4b82bSMike Smith 
19171ac4b82bSMike Smith 		    /* free slot for reuse */
19181ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
19191ac4b82bSMike Smith 		    sc->mlx_busycmds--;
19201ac4b82bSMike Smith 		} else {
19211ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
19221ac4b82bSMike Smith 		}
19231ac4b82bSMike Smith 	    } else {
19241ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
19251ac4b82bSMike Smith 	    }
19265792b7feSMike Smith 	} else {
19275792b7feSMike Smith 	    break;
19281ac4b82bSMike Smith 	}
19295792b7feSMike Smith     }
19301ac4b82bSMike Smith 
19315792b7feSMike Smith     /* if we've completed any commands, try posting some more */
19325792b7feSMike Smith     if (result)
19335792b7feSMike Smith 	mlx_startio(sc);
19345792b7feSMike Smith 
19355792b7feSMike Smith     /* handle completion and timeouts */
19365792b7feSMike Smith     mlx_complete(sc);
19375792b7feSMike Smith 
19385792b7feSMike Smith     return(result);
19391ac4b82bSMike Smith }
19401ac4b82bSMike Smith 
19411ac4b82bSMike Smith /********************************************************************************
19425792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
19431ac4b82bSMike Smith  */
19441ac4b82bSMike Smith static void
19451ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
19461ac4b82bSMike Smith {
19471ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
19481ac4b82bSMike Smith     int			s, count;
19491ac4b82bSMike Smith 
19501ac4b82bSMike Smith     debug("called");
19511ac4b82bSMike Smith 
19525792b7feSMike Smith     /* avoid reentrancy  XXX might want to signal and request a restart */
19535792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
19545792b7feSMike Smith 	return;
19555792b7feSMike Smith 
19561ac4b82bSMike Smith     s = splbio();
19571ac4b82bSMike Smith     count = 0;
19581ac4b82bSMike Smith 
19595792b7feSMike Smith     /* scan the list of busy/done commands */
19604b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
19611ac4b82bSMike Smith     while (mc != NULL) {
19621ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
19631ac4b82bSMike Smith 
19645792b7feSMike Smith 	/* Command has been completed in some fashion */
19654b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
19664b006d7bSMike Smith 
19675792b7feSMike Smith 	    /* unmap the command's data buffer */
19685792b7feSMike Smith 	    mlx_unmapcmd(mc);
19691ac4b82bSMike Smith 	    /*
19701ac4b82bSMike Smith 	     * Does the command have a completion handler?
19711ac4b82bSMike Smith 	     */
19721ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
19731ac4b82bSMike Smith 		/* remove from list and give to handler */
19744b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
19751ac4b82bSMike Smith 		mc->mc_complete(mc);
19761ac4b82bSMike Smith 
19771ac4b82bSMike Smith 		/*
19781ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
19791ac4b82bSMike Smith 		 */
19801ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
19811ac4b82bSMike Smith 
19821ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
19834b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
19841ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
19851ac4b82bSMike Smith 
19861ac4b82bSMike Smith 		/*
19871ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
19881ac4b82bSMike Smith 		 */
19891ac4b82bSMike Smith 	    } else {
19901ac4b82bSMike Smith 	    }
19914b006d7bSMike Smith 	}
19921ac4b82bSMike Smith 	mc = nc;
19931ac4b82bSMike Smith     }
19941ac4b82bSMike Smith     splx(s);
19951ac4b82bSMike Smith 
19965792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_COMPLETING);
19971ac4b82bSMike Smith }
19981ac4b82bSMike Smith 
19991ac4b82bSMike Smith /********************************************************************************
20001ac4b82bSMike Smith  ********************************************************************************
20011ac4b82bSMike Smith                                                         Command Buffer Management
20021ac4b82bSMike Smith  ********************************************************************************
20031ac4b82bSMike Smith  ********************************************************************************/
20041ac4b82bSMike Smith 
20051ac4b82bSMike Smith /********************************************************************************
20061ac4b82bSMike Smith  * Get a new command buffer.
20071ac4b82bSMike Smith  *
20081ac4b82bSMike Smith  * This may return NULL in low-memory cases.
20091ac4b82bSMike Smith  *
20101ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
20111ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
20121ac4b82bSMike Smith  *
20131ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
20141ac4b82bSMike Smith  *
20151ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
20161ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
20171ac4b82bSMike Smith  *     using a buffer.
20181ac4b82bSMike Smith  */
20191ac4b82bSMike Smith static struct mlx_command *
20201ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
20211ac4b82bSMike Smith {
20221ac4b82bSMike Smith     struct mlx_command	*mc;
20231ac4b82bSMike Smith     int			error;
20241ac4b82bSMike Smith     int			s;
20251ac4b82bSMike Smith 
20261ac4b82bSMike Smith     debug("called");
20271ac4b82bSMike Smith 
20281ac4b82bSMike Smith     s = splbio();
20291ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
20301ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
20311ac4b82bSMike Smith     splx(s);
20321ac4b82bSMike Smith 
20331ac4b82bSMike Smith     /* allocate a new command buffer? */
20341ac4b82bSMike Smith     if (mc == NULL) {
20351ac4b82bSMike Smith 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT);
20361ac4b82bSMike Smith 	if (mc != NULL) {
20371ac4b82bSMike Smith 	    bzero(mc, sizeof(*mc));
20381ac4b82bSMike Smith 	    mc->mc_sc = sc;
20391ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
20401ac4b82bSMike Smith 	    if (error) {
20411ac4b82bSMike Smith 		free(mc, M_DEVBUF);
20421ac4b82bSMike Smith 		return(NULL);
20431ac4b82bSMike Smith 	    }
20441ac4b82bSMike Smith 	}
20451ac4b82bSMike Smith     }
20461ac4b82bSMike Smith     return(mc);
20471ac4b82bSMike Smith }
20481ac4b82bSMike Smith 
20491ac4b82bSMike Smith /********************************************************************************
20501ac4b82bSMike Smith  * Release a command buffer for recycling.
20511ac4b82bSMike Smith  *
20521ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
20531ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
20541ac4b82bSMike Smith  */
20551ac4b82bSMike Smith static void
20561ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
20571ac4b82bSMike Smith {
20581ac4b82bSMike Smith     int		s;
20591ac4b82bSMike Smith 
20601ac4b82bSMike Smith     debug("called");
20611ac4b82bSMike Smith 
20621ac4b82bSMike Smith     s = splbio();
20631ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
20641ac4b82bSMike Smith     splx(s);
20651ac4b82bSMike Smith }
20661ac4b82bSMike Smith 
20671ac4b82bSMike Smith /********************************************************************************
20681ac4b82bSMike Smith  * Permanently discard a command buffer.
20691ac4b82bSMike Smith  */
20701ac4b82bSMike Smith static void
20711ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
20721ac4b82bSMike Smith {
20731ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20741ac4b82bSMike Smith 
20751ac4b82bSMike Smith     debug("called");
20761ac4b82bSMike Smith 
20771ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
20781ac4b82bSMike Smith     free(mc, M_DEVBUF);
20791ac4b82bSMike Smith }
20801ac4b82bSMike Smith 
20811ac4b82bSMike Smith 
20821ac4b82bSMike Smith /********************************************************************************
20831ac4b82bSMike Smith  ********************************************************************************
20841ac4b82bSMike Smith                                                 Type 3 interface accessor methods
20851ac4b82bSMike Smith  ********************************************************************************
20861ac4b82bSMike Smith  ********************************************************************************/
20871ac4b82bSMike Smith 
20881ac4b82bSMike Smith /********************************************************************************
20891ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
20901ac4b82bSMike Smith  * (the controller is not ready to take a command).
20911ac4b82bSMike Smith  *
20921ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
20931ac4b82bSMike Smith  */
20941ac4b82bSMike Smith static int
20951ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
20961ac4b82bSMike Smith {
20971ac4b82bSMike Smith     int		i;
20981ac4b82bSMike Smith 
20991ac4b82bSMike Smith     debug("called");
21001ac4b82bSMike Smith 
21011ac4b82bSMike Smith     /* ready for our command? */
21021ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
21031ac4b82bSMike Smith 	/* copy mailbox data to window */
21041ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
21051ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
21061ac4b82bSMike Smith 
21071ac4b82bSMike Smith 	/* post command */
2108f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
21091ac4b82bSMike Smith 	return(1);
21101ac4b82bSMike Smith     }
21111ac4b82bSMike Smith     return(0);
21121ac4b82bSMike Smith }
21131ac4b82bSMike Smith 
21141ac4b82bSMike Smith /********************************************************************************
21151ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
21161ac4b82bSMike Smith  * and recover the slot number and status code.
21171ac4b82bSMike Smith  *
21181ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
21191ac4b82bSMike Smith  */
21201ac4b82bSMike Smith static int
21211ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
21221ac4b82bSMike Smith {
21231ac4b82bSMike Smith 
21241ac4b82bSMike Smith     debug("called");
21251ac4b82bSMike Smith 
21261ac4b82bSMike Smith     /* status available? */
21271ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
21281ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
21291ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
21301ac4b82bSMike Smith 
21311ac4b82bSMike Smith 	/* acknowledge completion */
2132f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2133f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
21341ac4b82bSMike Smith 	return(1);
21351ac4b82bSMike Smith     }
21361ac4b82bSMike Smith     return(0);
21371ac4b82bSMike Smith }
21381ac4b82bSMike Smith 
21391ac4b82bSMike Smith /********************************************************************************
21401ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
21411ac4b82bSMike Smith  *
21421ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
21431ac4b82bSMike Smith  */
21441ac4b82bSMike Smith static void
21451ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
21461ac4b82bSMike Smith {
21471ac4b82bSMike Smith     debug("called");
21481ac4b82bSMike Smith 
21491ac4b82bSMike Smith     switch(action) {
21501ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
21511ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
21521ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
21531ac4b82bSMike Smith 	break;
21541ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
21551ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
21561ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
21571ac4b82bSMike Smith 	break;
21581ac4b82bSMike Smith     }
21591ac4b82bSMike Smith }
21601ac4b82bSMike Smith 
21611ac4b82bSMike Smith 
21621ac4b82bSMike Smith /********************************************************************************
21631ac4b82bSMike Smith  ********************************************************************************
2164f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2165f6b84b08SMike Smith  ********************************************************************************
2166f6b84b08SMike Smith  ********************************************************************************/
2167f6b84b08SMike Smith 
2168f6b84b08SMike Smith /********************************************************************************
2169f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2170f6b84b08SMike Smith  * (the controller is not ready to take a command).
2171f6b84b08SMike Smith  *
2172f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2173f6b84b08SMike Smith  */
2174f6b84b08SMike Smith static int
2175f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2176f6b84b08SMike Smith {
2177f6b84b08SMike Smith     int		i;
2178f6b84b08SMike Smith 
2179f6b84b08SMike Smith     debug("called");
2180f6b84b08SMike Smith 
2181f6b84b08SMike Smith     /* ready for our command? */
2182f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2183f6b84b08SMike Smith 	/* copy mailbox data to window */
2184f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2185f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2186f6b84b08SMike Smith 
2187f6b84b08SMike Smith 	/* post command */
2188f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2189f6b84b08SMike Smith 	return(1);
2190f6b84b08SMike Smith     }
2191f6b84b08SMike Smith     return(0);
2192f6b84b08SMike Smith }
2193f6b84b08SMike Smith 
2194f6b84b08SMike Smith /********************************************************************************
2195f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2196f6b84b08SMike Smith  * and recover the slot number and status code.
2197f6b84b08SMike Smith  *
2198f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2199f6b84b08SMike Smith  */
2200f6b84b08SMike Smith static int
2201f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2202f6b84b08SMike Smith {
2203f6b84b08SMike Smith 
2204f6b84b08SMike Smith     debug("called");
2205f6b84b08SMike Smith 
2206f6b84b08SMike Smith     /* status available? */
2207f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2208f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2209f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2210f6b84b08SMike Smith 
2211f6b84b08SMike Smith 	/* acknowledge completion */
2212f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2213f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2214f6b84b08SMike Smith 	return(1);
2215f6b84b08SMike Smith     }
2216f6b84b08SMike Smith     return(0);
2217f6b84b08SMike Smith }
2218f6b84b08SMike Smith 
2219f6b84b08SMike Smith /********************************************************************************
2220f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2221f6b84b08SMike Smith  *
2222f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2223f6b84b08SMike Smith  */
2224f6b84b08SMike Smith static void
2225f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2226f6b84b08SMike Smith {
2227f6b84b08SMike Smith     debug("called");
2228f6b84b08SMike Smith 
2229f6b84b08SMike Smith     switch(action) {
2230f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2231f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2232f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2233f6b84b08SMike Smith 	break;
2234f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2235f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2236f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2237f6b84b08SMike Smith 	break;
2238f6b84b08SMike Smith     }
2239f6b84b08SMike Smith }
2240f6b84b08SMike Smith 
2241f6b84b08SMike Smith 
2242f6b84b08SMike Smith /********************************************************************************
2243f6b84b08SMike Smith  ********************************************************************************
22445792b7feSMike Smith                                                 Type 5 interface accessor methods
22455792b7feSMike Smith  ********************************************************************************
22465792b7feSMike Smith  ********************************************************************************/
22475792b7feSMike Smith 
22485792b7feSMike Smith /********************************************************************************
22495792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
22505792b7feSMike Smith  * (the controller is not ready to take a command).
22515792b7feSMike Smith  *
22525792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
22535792b7feSMike Smith  */
22545792b7feSMike Smith static int
22555792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
22565792b7feSMike Smith {
22575792b7feSMike Smith     int		i;
22585792b7feSMike Smith 
22595792b7feSMike Smith     debug("called");
22605792b7feSMike Smith 
22615792b7feSMike Smith     /* ready for our command? */
22625792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
22635792b7feSMike Smith 	/* copy mailbox data to window */
22645792b7feSMike Smith 	for (i = 0; i < 13; i++)
22655792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
22665792b7feSMike Smith 
22675792b7feSMike Smith 	/* post command */
22685792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
22695792b7feSMike Smith 	return(1);
22705792b7feSMike Smith     }
22715792b7feSMike Smith     return(0);
22725792b7feSMike Smith }
22735792b7feSMike Smith 
22745792b7feSMike Smith /********************************************************************************
22755792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
22765792b7feSMike Smith  * and recover the slot number and status code.
22775792b7feSMike Smith  *
22785792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
22795792b7feSMike Smith  */
22805792b7feSMike Smith static int
22815792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
22825792b7feSMike Smith {
22835792b7feSMike Smith 
22845792b7feSMike Smith     debug("called");
22855792b7feSMike Smith 
22865792b7feSMike Smith     /* status available? */
22875792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
22885792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
22895792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
22905792b7feSMike Smith 
22915792b7feSMike Smith 	/* acknowledge completion */
22925792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
22935792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
22945792b7feSMike Smith 	return(1);
22955792b7feSMike Smith     }
22965792b7feSMike Smith     return(0);
22975792b7feSMike Smith }
22985792b7feSMike Smith 
22995792b7feSMike Smith /********************************************************************************
23005792b7feSMike Smith  * Enable/disable interrupts as requested.
23015792b7feSMike Smith  *
23025792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
23035792b7feSMike Smith  */
23045792b7feSMike Smith static void
23055792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
23065792b7feSMike Smith {
23075792b7feSMike Smith     debug("called");
23085792b7feSMike Smith 
23095792b7feSMike Smith     switch(action) {
23105792b7feSMike Smith     case MLX_INTACTION_DISABLE:
23115792b7feSMike Smith 	MLX_V5_PUT_IER(sc, MLX_V5_IER_DISINT);
23125792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
23135792b7feSMike Smith 	break;
23145792b7feSMike Smith     case MLX_INTACTION_ENABLE:
23155792b7feSMike Smith 	MLX_V5_PUT_IER(sc, 0);
23165792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
23175792b7feSMike Smith 	break;
23185792b7feSMike Smith     }
23195792b7feSMike Smith }
23205792b7feSMike Smith 
23215792b7feSMike Smith 
23225792b7feSMike Smith /********************************************************************************
23235792b7feSMike Smith  ********************************************************************************
23241ac4b82bSMike Smith                                                                         Debugging
23251ac4b82bSMike Smith  ********************************************************************************
23261ac4b82bSMike Smith  ********************************************************************************/
23271ac4b82bSMike Smith 
23281ac4b82bSMike Smith /********************************************************************************
23291ac4b82bSMike Smith  * Return a status message describing (mc)
23301ac4b82bSMike Smith  */
23311ac4b82bSMike Smith static char *mlx_status_messages[] = {
23321ac4b82bSMike Smith     "normal completion",			/* 00 */
23331ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
23341ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
23351ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
23361ac4b82bSMike Smith     "bad data encountered",			/* 04 */
23371ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
23381ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
23391ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
23401ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
23411ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
23421ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
23431ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
23441ac4b82bSMike Smith     "channel is busy",				/* 12 */
23451ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
23461ac4b82bSMike Smith };
23471ac4b82bSMike Smith 
23481ac4b82bSMike Smith static struct
23491ac4b82bSMike Smith {
23501ac4b82bSMike Smith     int		command;
23511ac4b82bSMike Smith     u_int16_t	status;
23521ac4b82bSMike Smith     int		msg;
23531ac4b82bSMike Smith } mlx_messages[] = {
23541ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x0001,	 1},
23551ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x0002,	 1},
23561ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x0105,	 3},
23571ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x010c,	 4},
23581ac4b82bSMike Smith     {MLX_CMD_WRITEOLDSG,	0x0001,	 1},
23591ac4b82bSMike Smith     {MLX_CMD_WRITEOLDSG,	0x0002,	 1},
23601ac4b82bSMike Smith     {MLX_CMD_WRITEOLDSG,	0x0105,	 3},
23611ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
23621ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
23631ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
23641ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
23651ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
23661ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
23671ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
23681ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
23691ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
23701ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
23711ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
23721ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
23731ac4b82bSMike Smith     {-1, 0, 0}
23741ac4b82bSMike Smith };
23751ac4b82bSMike Smith 
23761ac4b82bSMike Smith static char *
23771ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
23781ac4b82bSMike Smith {
23791ac4b82bSMike Smith     static char	unkmsg[80];
23801ac4b82bSMike Smith     int		i;
23811ac4b82bSMike Smith 
23821ac4b82bSMike Smith     /* look up message in table */
23831ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
23841ac4b82bSMike Smith 	if ((mc->mc_mailbox[0] == mlx_messages[i].command) &&
2385466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
23861ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
23871ac4b82bSMike Smith 
23881ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
23891ac4b82bSMike Smith     return(unkmsg);
23901ac4b82bSMike Smith }
23911ac4b82bSMike Smith 
23921ac4b82bSMike Smith /*******************************************************************************
23931ac4b82bSMike Smith  * Return a string describing the controller (hwid)
23941ac4b82bSMike Smith  */
23955792b7feSMike Smith static struct
23965792b7feSMike Smith {
23975792b7feSMike Smith     int		hwid;
23985792b7feSMike Smith     char	*name;
23995792b7feSMike Smith } mlx_controller_names[] = {
24005792b7feSMike Smith     {0x01,	"960P/PD"},
24015792b7feSMike Smith     {0x02,	"960PL"},
24025792b7feSMike Smith     {0x10,	"960PG"},
24035792b7feSMike Smith     {0x11,	"960PJ"},
24049eee27f1SMike Smith     {0x12,	"960PR"},
24059eee27f1SMike Smith     {0x13,	"960PT"},
24069eee27f1SMike Smith     {0x14,	"960PTL0"},
24079eee27f1SMike Smith     {0x15,	"960PRL"},
24089eee27f1SMike Smith     {0x16,	"960PTL1"},
24099eee27f1SMike Smith     {0x20,	"1164PVX"},
24105792b7feSMike Smith     {-1, NULL}
24115792b7feSMike Smith };
24125792b7feSMike Smith 
24139eee27f1SMike Smith static void
24149eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
24151ac4b82bSMike Smith {
24161ac4b82bSMike Smith     static char		buf[80];
24175792b7feSMike Smith     char		*model;
24189eee27f1SMike Smith     int			i;
24191ac4b82bSMike Smith 
24205792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
24219eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
24225792b7feSMike Smith 	    model = mlx_controller_names[i].name;
24231ac4b82bSMike Smith 	    break;
24241ac4b82bSMike Smith 	}
24255792b7feSMike Smith     }
24265792b7feSMike Smith     if (model == NULL) {
24279eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
24285792b7feSMike Smith 	model = buf;
24295792b7feSMike Smith     }
24309eee27f1SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%d, %dMB RAM\n",
24319eee27f1SMike Smith 		  model,
24329eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
24339eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
24349eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
24359eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
24369eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2437b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
24389eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
24399eee27f1SMike Smith 
24409eee27f1SMike Smith     if (bootverbose) {
24419eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
24429eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
24439eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
24449eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
24459eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
24469eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
24479eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
24489eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
24499eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
24509eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
24519eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
24529eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
24539eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
24549eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
24559eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
24569eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
24579eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
24589eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
24599eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
24609eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
24619eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
24629eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
24639eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
24649eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
24659eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
24669eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
24679eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
24689eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
24699eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
24709eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
24719eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
24729eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
24739eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
24749eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
24759eee27f1SMike Smith 
24769eee27f1SMike Smith     }
24771ac4b82bSMike Smith }
24781ac4b82bSMike Smith 
24791ac4b82bSMike Smith /********************************************************************************
24801ac4b82bSMike Smith  ********************************************************************************
24811ac4b82bSMike Smith                                                                 Utility Functions
24821ac4b82bSMike Smith  ********************************************************************************
24831ac4b82bSMike Smith  ********************************************************************************/
24841ac4b82bSMike Smith 
24851ac4b82bSMike Smith /********************************************************************************
24861ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
24871ac4b82bSMike Smith  */
24881ac4b82bSMike Smith static struct mlx_sysdrive *
24891ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
24901ac4b82bSMike Smith {
24911ac4b82bSMike Smith     int		i;
24921ac4b82bSMike Smith 
24931ac4b82bSMike Smith     /* search system drives */
24941ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
24951ac4b82bSMike Smith 	/* is this one attached? */
24961ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
24971ac4b82bSMike Smith 	    /* is this the one? */
24981ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
24991ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
25001ac4b82bSMike Smith 	}
25011ac4b82bSMike Smith     }
25021ac4b82bSMike Smith     return(NULL);
25031ac4b82bSMike Smith }
2504