xref: /freebsd/sys/dev/mlx/mlx.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
11ac4b82bSMike Smith /*-
2*718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*718cf2ccSPedro F. Giffuni  *
41ac4b82bSMike Smith  * Copyright (c) 1999 Michael Smith
51ac4b82bSMike Smith  * All rights reserved.
61ac4b82bSMike Smith  *
71ac4b82bSMike Smith  * Redistribution and use in source and binary forms, with or without
81ac4b82bSMike Smith  * modification, are permitted provided that the following conditions
91ac4b82bSMike Smith  * are met:
101ac4b82bSMike Smith  * 1. Redistributions of source code must retain the above copyright
111ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer.
121ac4b82bSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
131ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer in the
141ac4b82bSMike Smith  *    documentation and/or other materials provided with the distribution.
151ac4b82bSMike Smith  *
161ac4b82bSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171ac4b82bSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181ac4b82bSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191ac4b82bSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201ac4b82bSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211ac4b82bSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221ac4b82bSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231ac4b82bSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241ac4b82bSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251ac4b82bSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261ac4b82bSMike Smith  * SUCH DAMAGE.
271ac4b82bSMike Smith  *
281ac4b82bSMike Smith  *	$FreeBSD$
291ac4b82bSMike Smith  */
301ac4b82bSMike Smith 
311ac4b82bSMike Smith /*
321ac4b82bSMike Smith  * Driver for the Mylex DAC960 family of RAID controllers.
331ac4b82bSMike Smith  */
341ac4b82bSMike Smith 
351ac4b82bSMike Smith #include <sys/param.h>
361ac4b82bSMike Smith #include <sys/systm.h>
37b04e4c12SJohn Baldwin #include <sys/bio.h>
380fca6f8bSJohn Baldwin #include <sys/lock.h>
391ac4b82bSMike Smith #include <sys/malloc.h>
400fca6f8bSJohn Baldwin #include <sys/mutex.h>
411ac4b82bSMike Smith #include <sys/kernel.h>
420fca6f8bSJohn Baldwin #include <sys/sx.h>
431ac4b82bSMike Smith 
441ac4b82bSMike Smith #include <sys/bus.h>
451ac4b82bSMike Smith #include <sys/conf.h>
46da8bb3a3SMike Smith #include <sys/stat.h>
471ac4b82bSMike Smith 
481ac4b82bSMike Smith #include <machine/resource.h>
491ac4b82bSMike Smith #include <machine/bus.h>
501ac4b82bSMike Smith #include <machine/clock.h>
511ac4b82bSMike Smith #include <sys/rman.h>
521ac4b82bSMike Smith 
53891619a6SPoul-Henning Kamp #include <geom/geom_disk.h>
54891619a6SPoul-Henning Kamp 
551ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
561ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
571ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
581ac4b82bSMike Smith 
591ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
60dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
617ac40f5fSPoul-Henning Kamp 	.d_open =	mlx_open,
627ac40f5fSPoul-Henning Kamp 	.d_close =	mlx_close,
637ac40f5fSPoul-Henning Kamp 	.d_ioctl =	mlx_ioctl,
647ac40f5fSPoul-Henning Kamp 	.d_name =	"mlx",
651ac4b82bSMike Smith };
661ac4b82bSMike Smith 
671ac4b82bSMike Smith devclass_t	mlx_devclass;
681ac4b82bSMike Smith 
691ac4b82bSMike Smith /*
701ac4b82bSMike Smith  * Per-interface accessor methods
711ac4b82bSMike Smith  */
721ac4b82bSMike Smith static int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
731ac4b82bSMike Smith static int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
741ac4b82bSMike Smith static void			mlx_v3_intaction(struct mlx_softc *sc, int action);
750fca6f8bSJohn Baldwin static int			mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
761ac4b82bSMike Smith 
77f6b84b08SMike Smith static int			mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
78f6b84b08SMike Smith static int			mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
79f6b84b08SMike Smith static void			mlx_v4_intaction(struct mlx_softc *sc, int action);
800fca6f8bSJohn Baldwin static int			mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
81f6b84b08SMike Smith 
825792b7feSMike Smith static int			mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
835792b7feSMike Smith static int			mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
845792b7feSMike Smith static void			mlx_v5_intaction(struct mlx_softc *sc, int action);
850fca6f8bSJohn Baldwin static int			mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
865792b7feSMike Smith 
871ac4b82bSMike Smith /*
881ac4b82bSMike Smith  * Status monitoring
891ac4b82bSMike Smith  */
901ac4b82bSMike Smith static void			mlx_periodic(void *data);
911ac4b82bSMike Smith static void			mlx_periodic_enquiry(struct mlx_command *mc);
921ac4b82bSMike Smith static void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
931ac4b82bSMike Smith static void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
941ac4b82bSMike Smith static void			mlx_periodic_rebuild(struct mlx_command *mc);
951ac4b82bSMike Smith 
961ac4b82bSMike Smith /*
971ac4b82bSMike Smith  * Channel Pause
981ac4b82bSMike Smith  */
991ac4b82bSMike Smith static void			mlx_pause_action(struct mlx_softc *sc);
1001ac4b82bSMike Smith static void			mlx_pause_done(struct mlx_command *mc);
1011ac4b82bSMike Smith 
1021ac4b82bSMike Smith /*
1031ac4b82bSMike Smith  * Command submission.
1041ac4b82bSMike Smith  */
1051ac4b82bSMike Smith static void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
1061ac4b82bSMike Smith 					     void (*complete)(struct mlx_command *mc));
1071ac4b82bSMike Smith static int			mlx_flush(struct mlx_softc *sc);
108421f2f7dSMike Smith static int			mlx_check(struct mlx_softc *sc, int drive);
1091ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1101ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1111ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1121b4404f9SScott Long void				mlx_startio_cb(void *arg,
1131b4404f9SScott Long 					       bus_dma_segment_t *segs,
1141b4404f9SScott Long 					       int nsegments, int error);
1151ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1161ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1171b4404f9SScott Long static int			mlx_user_command(struct mlx_softc *sc,
1181b4404f9SScott Long 						 struct mlx_usercommand *mu);
1191b4404f9SScott Long void				mlx_user_cb(void *arg, bus_dma_segment_t *segs,
1201b4404f9SScott Long 					    int nsegments, int error);
1211ac4b82bSMike Smith 
1221ac4b82bSMike Smith /*
1231ac4b82bSMike Smith  * Command buffer allocation.
1241ac4b82bSMike Smith  */
1251ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1261ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1271ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1281ac4b82bSMike Smith 
1291ac4b82bSMike Smith /*
1301ac4b82bSMike Smith  * Command management.
1311ac4b82bSMike Smith  */
1321ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1331b4404f9SScott Long static void			mlx_setup_dmamap(struct mlx_command *mc,
1341b4404f9SScott Long 						 bus_dma_segment_t *segs,
1351b4404f9SScott Long 						 int nsegments, int error);
1361ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1370fca6f8bSJohn Baldwin static int			mlx_shutdown_locked(struct mlx_softc *sc);
1381ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1390fca6f8bSJohn Baldwin static int			mlx_done(struct mlx_softc *sc, int startio);
1401ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1411ac4b82bSMike Smith 
1421ac4b82bSMike Smith /*
1431ac4b82bSMike Smith  * Debugging.
1441ac4b82bSMike Smith  */
1451ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1469eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
147da8bb3a3SMike Smith static int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
1481ac4b82bSMike Smith 
1491ac4b82bSMike Smith /*
1501ac4b82bSMike Smith  * Utility functions.
1511ac4b82bSMike Smith  */
1521ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1531ac4b82bSMike Smith 
1541ac4b82bSMike Smith /********************************************************************************
1551ac4b82bSMike Smith  ********************************************************************************
1561ac4b82bSMike Smith                                                                 Public Interfaces
1571ac4b82bSMike Smith  ********************************************************************************
1581ac4b82bSMike Smith  ********************************************************************************/
1591ac4b82bSMike Smith 
1601ac4b82bSMike Smith /********************************************************************************
1611ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1621ac4b82bSMike Smith  *
1631ac4b82bSMike Smith  * Should not be called if the controller is active.
1641ac4b82bSMike Smith  */
1651ac4b82bSMike Smith void
1661ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1671ac4b82bSMike Smith {
1681ac4b82bSMike Smith     struct mlx_command	*mc;
1691ac4b82bSMike Smith 
170da8bb3a3SMike Smith     debug_called(1);
1711ac4b82bSMike Smith 
1720fca6f8bSJohn Baldwin     /* destroy control device */
1730fca6f8bSJohn Baldwin     if (sc->mlx_dev_t != NULL)
1740fca6f8bSJohn Baldwin 	destroy_dev(sc->mlx_dev_t);
1750fca6f8bSJohn Baldwin 
1760fca6f8bSJohn Baldwin     if (sc->mlx_intr)
1770fca6f8bSJohn Baldwin 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1780fca6f8bSJohn Baldwin 
1791ac4b82bSMike Smith     /* cancel status timeout */
1800fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
1810fca6f8bSJohn Baldwin     callout_stop(&sc->mlx_timeout);
1821ac4b82bSMike Smith 
1831ac4b82bSMike Smith     /* throw away any command buffers */
1841ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1851ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1861ac4b82bSMike Smith 	mlx_freecmd(mc);
1871ac4b82bSMike Smith     }
1880fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
1890fca6f8bSJohn Baldwin     callout_drain(&sc->mlx_timeout);
1901ac4b82bSMike Smith 
1911ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1921ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1931ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1941ac4b82bSMike Smith 
1951ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
196aced5239SJohn Baldwin     if (sc->mlx_sgbusaddr)
197aced5239SJohn Baldwin 	bus_dmamap_unload(sc->mlx_sg_dmat, sc->mlx_sg_dmamap);
1981ac4b82bSMike Smith     if (sc->mlx_sgtable)
1991ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2001ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2011ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2021ac4b82bSMike Smith 
2031ac4b82bSMike Smith     /* disconnect the interrupt handler */
2041ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
2051ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
2061ac4b82bSMike Smith 
2071ac4b82bSMike Smith     /* destroy the parent DMA tag */
2081ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
2091ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
2101ac4b82bSMike Smith 
2111ac4b82bSMike Smith     /* release the register window mapping */
2121ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
2139b11c7baSMatthew N. Dodd 	bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem);
2149eee27f1SMike Smith 
2159eee27f1SMike Smith     /* free controller enquiry data */
2169eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
2179eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
218da8bb3a3SMike Smith 
2190fca6f8bSJohn Baldwin     sx_destroy(&sc->mlx_config_lock);
2200fca6f8bSJohn Baldwin     mtx_destroy(&sc->mlx_io_lock);
2211ac4b82bSMike Smith }
2221ac4b82bSMike Smith 
2231ac4b82bSMike Smith /********************************************************************************
2241ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2251ac4b82bSMike Smith  */
2261ac4b82bSMike Smith static void
2271ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2281ac4b82bSMike Smith {
2291ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2301ac4b82bSMike Smith 
231da8bb3a3SMike Smith     debug_called(1);
2321ac4b82bSMike Smith 
2331ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2341ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2351ac4b82bSMike Smith }
2361ac4b82bSMike Smith 
2371ac4b82bSMike Smith static int
2381ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2391ac4b82bSMike Smith {
2401ac4b82bSMike Smith     size_t	segsize;
241baff09dbSMike Smith     int		error, ncmd;
2421ac4b82bSMike Smith 
243da8bb3a3SMike Smith     debug_called(1);
2441ac4b82bSMike Smith 
2451ac4b82bSMike Smith     /* destroy any existing mappings */
246aced5239SJohn Baldwin     if (sc->mlx_sgbusaddr)
247aced5239SJohn Baldwin 	bus_dmamap_unload(sc->mlx_sg_dmat, sc->mlx_sg_dmamap);
2481ac4b82bSMike Smith     if (sc->mlx_sgtable)
2491ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2501ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2511ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
252aced5239SJohn Baldwin     sc->mlx_sgbusaddr = 0;
253aced5239SJohn Baldwin     sc->mlx_sgtable = NULL;
254aced5239SJohn Baldwin     sc->mlx_sg_dmat = NULL;
2551ac4b82bSMike Smith 
2561ac4b82bSMike Smith     /*
2571ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
258baff09dbSMike Smith      * the s/g lists we will need.  If we're called early on, we don't know how
259baff09dbSMike Smith      * many commands we're going to be asked to support, so only allocate enough
260baff09dbSMike Smith      * for a couple.
2611ac4b82bSMike Smith      */
262baff09dbSMike Smith     if (sc->mlx_enq2 == NULL) {
263baff09dbSMike Smith 	ncmd = 2;
264baff09dbSMike Smith     } else {
265baff09dbSMike Smith 	ncmd = sc->mlx_enq2->me_max_commands;
266baff09dbSMike Smith     }
267baff09dbSMike Smith     segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd;
2681ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2691ac4b82bSMike Smith 			       1, 0, 			/* alignment,boundary */
2701ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2711ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2721ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2731ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2741ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2751ac4b82bSMike Smith 			       0,			/* flags */
276fc3e87b3SScott Long 			       NULL, NULL,		/* lockfunc, lockarg */
2771ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2781ac4b82bSMike Smith     if (error != 0) {
2791ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2801ac4b82bSMike Smith 	return(ENOMEM);
2811ac4b82bSMike Smith     }
2821ac4b82bSMike Smith 
2831ac4b82bSMike Smith     /*
2841ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2851ac4b82bSMike Smith      * controller-visible space.
2861ac4b82bSMike Smith      *
2871ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
288fc3e87b3SScott Long      * contiguous slab.  We may need to switch to a more complex arrangement
289fc3e87b3SScott Long      * where we allocate in smaller chunks and keep a lookup table from slot
290fc3e87b3SScott Long      * to bus address.
2911ac4b82bSMike Smith      */
292fc3e87b3SScott Long     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable,
293fc3e87b3SScott Long 			     BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2941ac4b82bSMike Smith     if (error) {
2951ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2961ac4b82bSMike Smith 	return(ENOMEM);
2971ac4b82bSMike Smith     }
2981b4404f9SScott Long     (void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable,
299fc3e87b3SScott Long 			  segsize, mlx_dma_map_sg, sc, 0);
3001ac4b82bSMike Smith     return(0);
3011ac4b82bSMike Smith }
3021ac4b82bSMike Smith 
3031ac4b82bSMike Smith /********************************************************************************
3041ac4b82bSMike Smith  * Initialise the controller and softc
3051ac4b82bSMike Smith  */
3061ac4b82bSMike Smith int
3071ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
3081ac4b82bSMike Smith {
309da8bb3a3SMike Smith     struct mlx_enquiry_old	*meo;
310da8bb3a3SMike Smith     int				rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
3111ac4b82bSMike Smith 
312da8bb3a3SMike Smith     debug_called(1);
3131ac4b82bSMike Smith 
3141ac4b82bSMike Smith     /*
3151ac4b82bSMike Smith      * Initialise per-controller queues.
3161ac4b82bSMike Smith      */
3174b006d7bSMike Smith     TAILQ_INIT(&sc->mlx_work);
3181ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
319b04e4c12SJohn Baldwin     bioq_init(&sc->mlx_bioq);
3201ac4b82bSMike Smith 
3211ac4b82bSMike Smith     /*
3221ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
3231ac4b82bSMike Smith      */
3241ac4b82bSMike Smith     switch(sc->mlx_iftype) {
325da8bb3a3SMike Smith     case MLX_IFTYPE_2:
3261ac4b82bSMike Smith     case MLX_IFTYPE_3:
3271ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
3281ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
3291ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
330da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v3_fw_handshake;
3311ac4b82bSMike Smith 	break;
332f6b84b08SMike Smith     case MLX_IFTYPE_4:
333f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
334f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
335f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
336da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
337f6b84b08SMike Smith 	break;
3385792b7feSMike Smith     case MLX_IFTYPE_5:
3395792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3405792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3415792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
342da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
3435792b7feSMike Smith 	break;
3441ac4b82bSMike Smith     default:
3451ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3461ac4b82bSMike Smith     }
3471ac4b82bSMike Smith 
3481ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3490fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
3501ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3510fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
3521ac4b82bSMike Smith 
3531ac4b82bSMike Smith     /*
354da8bb3a3SMike Smith      * Wait for the controller to come ready, handshake with the firmware if required.
355da8bb3a3SMike Smith      * This is typically only necessary on platforms where the controller BIOS does not
356da8bb3a3SMike Smith      * run.
357da8bb3a3SMike Smith      */
358da8bb3a3SMike Smith     hsmsg = 0;
359da8bb3a3SMike Smith     DELAY(1000);
3600fca6f8bSJohn Baldwin     while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2,
3610fca6f8bSJohn Baldwin 	hsmsg == 0)) != 0) {
362da8bb3a3SMike Smith 	/* report first time around... */
363da8bb3a3SMike Smith 	if (hsmsg == 0) {
364da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
365da8bb3a3SMike Smith 	    hsmsg = 1;
366da8bb3a3SMike Smith 	}
367da8bb3a3SMike Smith 	/* did we get a real message? */
368da8bb3a3SMike Smith 	if (hscode == 2) {
369da8bb3a3SMike Smith 	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
370da8bb3a3SMike Smith 	    /* fatal initialisation error? */
371da8bb3a3SMike Smith 	    if (hscode != 0) {
372da8bb3a3SMike Smith 		return(ENXIO);
373da8bb3a3SMike Smith 	    }
374da8bb3a3SMike Smith 	}
375da8bb3a3SMike Smith     }
376da8bb3a3SMike Smith     if (hsmsg == 1)
377da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "initialisation complete.\n");
378da8bb3a3SMike Smith 
379da8bb3a3SMike Smith     /*
3801ac4b82bSMike Smith      * Allocate and connect our interrupt.
3811ac4b82bSMike Smith      */
3821ac4b82bSMike Smith     rid = 0;
3835f96beb9SNate Lawson     sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid,
3845f96beb9SNate Lawson         RF_SHAREABLE | RF_ACTIVE);
3851ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
386421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't allocate interrupt\n");
3871ac4b82bSMike Smith 	return(ENXIO);
3881ac4b82bSMike Smith     }
3890fca6f8bSJohn Baldwin     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO |
3900fca6f8bSJohn Baldwin 	INTR_ENTROPY | INTR_MPSAFE, NULL, mlx_intr, sc, &sc->mlx_intr);
3911ac4b82bSMike Smith     if (error) {
392421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't set up interrupt\n");
3931ac4b82bSMike Smith 	return(ENXIO);
3941ac4b82bSMike Smith     }
3951ac4b82bSMike Smith 
3961ac4b82bSMike Smith     /*
3971ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3981ac4b82bSMike Smith      */
3991ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
4001b4404f9SScott Long 			       1, 0, 			/* align, boundary */
4011ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
4021ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
4031ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
4046f954fb3SAlexander Motin 			       MLX_MAXPHYS,		/* maxsize */
4056f954fb3SAlexander Motin 			       MLX_NSEG,		/* nsegments */
4061ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
4071ac4b82bSMike Smith 			       0,			/* flags */
408f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
4090fca6f8bSJohn Baldwin 			       &sc->mlx_io_lock,	/* lockarg */
4101ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
4111ac4b82bSMike Smith     if (error != 0) {
4121ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
4131ac4b82bSMike Smith 	return(ENOMEM);
4141ac4b82bSMike Smith     }
4151ac4b82bSMike Smith 
4161ac4b82bSMike Smith     /*
4171b4404f9SScott Long      * Create some initial scatter/gather mappings so we can run the probe
4181b4404f9SScott Long      * commands.
4191ac4b82bSMike Smith      */
4201ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4211ac4b82bSMike Smith     if (error != 0) {
422421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
4231ac4b82bSMike Smith 	return(error);
4241ac4b82bSMike Smith     }
4251ac4b82bSMike Smith 
426baff09dbSMike Smith     /*
427baff09dbSMike Smith      * We don't (yet) know where the event log is up to.
428baff09dbSMike Smith      */
429baff09dbSMike Smith     sc->mlx_currevent = -1;
430baff09dbSMike Smith 
431baff09dbSMike Smith     /*
432baff09dbSMike Smith      * Obtain controller feature information
433baff09dbSMike Smith      */
4340fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
4359eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
4360fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
4371ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
4381ac4b82bSMike Smith 	return(ENXIO);
4391ac4b82bSMike Smith     }
4401ac4b82bSMike Smith 
4419eee27f1SMike Smith     /*
4421ac4b82bSMike Smith      * Do quirk/feature related things.
4431ac4b82bSMike Smith      */
4449eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
4451ac4b82bSMike Smith     switch(sc->mlx_iftype) {
446da8bb3a3SMike Smith     case MLX_IFTYPE_2:
447da8bb3a3SMike Smith 	/* These controllers don't report the firmware version in the ENQUIRY2 response */
448da8bb3a3SMike Smith 	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
4490fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
450da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
451da8bb3a3SMike Smith 	    return(ENXIO);
452da8bb3a3SMike Smith 	}
453da8bb3a3SMike Smith 	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
454da8bb3a3SMike Smith 
455572f2440SWarner Losh 	/* XXX require 2.42 or better (PCI) */
456da8bb3a3SMike Smith 	if (meo->me_fwminor < 42) {
457da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
458da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
459da8bb3a3SMike Smith 	}
460caa32ef5SColin Percival 	free(meo, M_DEVBUF);
461da8bb3a3SMike Smith 	break;
4621ac4b82bSMike Smith     case MLX_IFTYPE_3:
463f6b84b08SMike Smith 	/* XXX certify 3.52? */
4649eee27f1SMike Smith 	if (fwminor < 51) {
4654b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4664b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
4671ac4b82bSMike Smith 	}
4681ac4b82bSMike Smith 	break;
469f6b84b08SMike Smith     case MLX_IFTYPE_4:
470f6b84b08SMike Smith 	/* XXX certify firmware versions? */
4719eee27f1SMike Smith 	if (fwminor < 6) {
4724b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4734b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
474f6b84b08SMike Smith 	}
475f6b84b08SMike Smith 	break;
4765792b7feSMike Smith     case MLX_IFTYPE_5:
4779eee27f1SMike Smith 	if (fwminor < 7) {
4785792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4795792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
4805792b7feSMike Smith 	}
4815792b7feSMike Smith 	break;
4821ac4b82bSMike Smith     default:
4830fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
4841ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4851ac4b82bSMike Smith     }
4860fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
4871ac4b82bSMike Smith 
4881ac4b82bSMike Smith     /*
489baff09dbSMike Smith      * Create the final scatter/gather mappings now that we have characterised the controller.
4901ac4b82bSMike Smith      */
4911ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4921ac4b82bSMike Smith     if (error != 0) {
493baff09dbSMike Smith 	device_printf(sc->mlx_dev, "can't make final s/g list mapping\n");
4941ac4b82bSMike Smith 	return(error);
4951ac4b82bSMike Smith     }
4961ac4b82bSMike Smith 
4971ac4b82bSMike Smith     /*
498421f2f7dSMike Smith      * No user-requested background operation is in progress.
4991ac4b82bSMike Smith      */
500421f2f7dSMike Smith     sc->mlx_background = 0;
501421f2f7dSMike Smith     sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
5021ac4b82bSMike Smith 
5031ac4b82bSMike Smith     /*
504da8bb3a3SMike Smith      * Create the control device.
5051ac4b82bSMike Smith      */
506e78a5a3fSEd Schouten     sc->mlx_dev_t = make_dev(&mlx_cdevsw, 0, UID_ROOT, GID_OPERATOR,
507da8bb3a3SMike Smith 			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
508e78a5a3fSEd Schouten     sc->mlx_dev_t->si_drv1 = sc;
5091ac4b82bSMike Smith 
5101ac4b82bSMike Smith     /*
5111ac4b82bSMike Smith      * Start the timeout routine.
5121ac4b82bSMike Smith      */
5130fca6f8bSJohn Baldwin     callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc);
5141ac4b82bSMike Smith 
515da8bb3a3SMike Smith     /* print a little information about the controller */
516da8bb3a3SMike Smith     mlx_describe_controller(sc);
517da8bb3a3SMike Smith 
5181ac4b82bSMike Smith     return(0);
5191ac4b82bSMike Smith }
5201ac4b82bSMike Smith 
5211ac4b82bSMike Smith /********************************************************************************
5221ac4b82bSMike Smith  * Locate disk resources and attach children to them.
5231ac4b82bSMike Smith  */
5241ac4b82bSMike Smith void
5251ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
5261ac4b82bSMike Smith {
5271ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
5281ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
5291ac4b82bSMike Smith     int				i, error;
5301ac4b82bSMike Smith 
531da8bb3a3SMike Smith     debug_called(1);
5321ac4b82bSMike Smith 
5331ac4b82bSMike Smith     /*
5341ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
5351ac4b82bSMike Smith      * don't currently have them.
5361ac4b82bSMike Smith      */
5370fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
5381ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
5390fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
5401ac4b82bSMike Smith     if (mes == NULL) {
5419eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
5421ac4b82bSMike Smith 	return;
5431ac4b82bSMike Smith     }
5441ac4b82bSMike Smith 
5451ac4b82bSMike Smith     /* iterate over drives returned */
5460fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
5471ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
5481ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
5491ac4b82bSMike Smith 	 i++, dr++) {
5501ac4b82bSMike Smith 	/* are we already attached to this drive? */
5511ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
5521ac4b82bSMike Smith 	    /* pick up drive information */
5531ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
554f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
5551ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
5561ac4b82bSMike Smith 
5571ac4b82bSMike Smith 	    /* generate geometry information */
5581ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
5591ac4b82bSMike Smith 		dr->ms_heads = 128;
5601ac4b82bSMike Smith 		dr->ms_sectors = 32;
5611ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
5621ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
5631ac4b82bSMike Smith 		dr->ms_heads = 255;
5641ac4b82bSMike Smith 		dr->ms_sectors = 63;
5651ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
5661ac4b82bSMike Smith 	    }
567fe0d4089SMatthew N. Dodd 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
5681ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
5691ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
570fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
5711ac4b82bSMike Smith 	}
5721ac4b82bSMike Smith     }
5731ac4b82bSMike Smith     free(mes, M_DEVBUF);
5741ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
5751ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
5761ac4b82bSMike Smith 
5771ac4b82bSMike Smith     /* mark controller back up */
5780fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
5791ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5801ac4b82bSMike Smith 
5811ac4b82bSMike Smith     /* enable interrupts */
5821ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
5830fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
5840fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
5851ac4b82bSMike Smith }
5861ac4b82bSMike Smith 
5871ac4b82bSMike Smith /********************************************************************************
5881ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
5891ac4b82bSMike Smith  */
5901ac4b82bSMike Smith int
5911ac4b82bSMike Smith mlx_detach(device_t dev)
5921ac4b82bSMike Smith {
5931ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5945792b7feSMike Smith     struct mlxd_softc	*mlxd;
5950fca6f8bSJohn Baldwin     int			i, error;
5961ac4b82bSMike Smith 
597da8bb3a3SMike Smith     debug_called(1);
5981ac4b82bSMike Smith 
5995792b7feSMike Smith     error = EBUSY;
6000fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
6011ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
6025792b7feSMike Smith 	goto out;
6031ac4b82bSMike Smith 
6045792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6055792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6065792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
6075792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
6085792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
6095792b7feSMike Smith 		goto out;
6105792b7feSMike Smith 	    }
6115792b7feSMike Smith 	}
6125792b7feSMike Smith     }
6131ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
6145792b7feSMike Smith 	goto out;
6150fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
6161ac4b82bSMike Smith 
6171ac4b82bSMike Smith     mlx_free(sc);
6181ac4b82bSMike Smith 
6190fca6f8bSJohn Baldwin     return (0);
6205792b7feSMike Smith  out:
6210fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
6225792b7feSMike Smith     return(error);
6231ac4b82bSMike Smith }
6241ac4b82bSMike Smith 
6251ac4b82bSMike Smith /********************************************************************************
6261ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
6271ac4b82bSMike Smith  *
6281ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
6291ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
6301ac4b82bSMike Smith  * resume normal operation.)
6311ac4b82bSMike Smith  *
6328177437dSPoul-Henning Kamp  * Note that we can assume that the bioq on the controller is empty, as we won't
6331ac4b82bSMike Smith  * allow shutdown if any device is open.
6341ac4b82bSMike Smith  */
6351ac4b82bSMike Smith int
6361ac4b82bSMike Smith mlx_shutdown(device_t dev)
6371ac4b82bSMike Smith {
6381ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6390fca6f8bSJohn Baldwin     int			error;
6400fca6f8bSJohn Baldwin 
6410fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
6420fca6f8bSJohn Baldwin     error = mlx_shutdown_locked(sc);
6430fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
6440fca6f8bSJohn Baldwin     return (error);
6450fca6f8bSJohn Baldwin }
6460fca6f8bSJohn Baldwin 
6470fca6f8bSJohn Baldwin static int
6480fca6f8bSJohn Baldwin mlx_shutdown_locked(struct mlx_softc *sc)
6490fca6f8bSJohn Baldwin {
6500fca6f8bSJohn Baldwin     int			i, error;
6511ac4b82bSMike Smith 
652da8bb3a3SMike Smith     debug_called(1);
6531ac4b82bSMike Smith 
6540fca6f8bSJohn Baldwin     MLX_CONFIG_ASSERT_LOCKED(sc);
6551ac4b82bSMike Smith 
6560fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
6571ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
6585792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6591ac4b82bSMike Smith 
6601ac4b82bSMike Smith     /* flush controller */
6611ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6621ac4b82bSMike Smith     if (mlx_flush(sc)) {
6631ac4b82bSMike Smith 	printf("failed\n");
6641ac4b82bSMike Smith     } else {
6651ac4b82bSMike Smith 	printf("done\n");
6661ac4b82bSMike Smith     }
6670fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
6681ac4b82bSMike Smith 
6691ac4b82bSMike Smith     /* delete all our child devices */
6701ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6711ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6721ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
6730fca6f8bSJohn Baldwin 		return (error);
6741ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
6751ac4b82bSMike Smith 	}
6761ac4b82bSMike Smith     }
6771ac4b82bSMike Smith 
6780fca6f8bSJohn Baldwin     return (0);
6791ac4b82bSMike Smith }
6801ac4b82bSMike Smith 
6811ac4b82bSMike Smith /********************************************************************************
6821ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
6831ac4b82bSMike Smith  */
6841ac4b82bSMike Smith int
6851ac4b82bSMike Smith mlx_suspend(device_t dev)
6861ac4b82bSMike Smith {
6871ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6881ac4b82bSMike Smith 
689da8bb3a3SMike Smith     debug_called(1);
6901ac4b82bSMike Smith 
6910fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
6921ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
6931ac4b82bSMike Smith 
6941ac4b82bSMike Smith     /* flush controller */
6951ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6961ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6971ac4b82bSMike Smith 
6981ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6990fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7001ac4b82bSMike Smith 
7011ac4b82bSMike Smith     return(0);
7021ac4b82bSMike Smith }
7031ac4b82bSMike Smith 
7041ac4b82bSMike Smith /********************************************************************************
7051ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
7061ac4b82bSMike Smith  */
7071ac4b82bSMike Smith int
7081ac4b82bSMike Smith mlx_resume(device_t dev)
7091ac4b82bSMike Smith {
7101ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
7111ac4b82bSMike Smith 
712da8bb3a3SMike Smith     debug_called(1);
7131ac4b82bSMike Smith 
7140fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7151ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
7161ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
7170fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7181ac4b82bSMike Smith 
7191ac4b82bSMike Smith     return(0);
7201ac4b82bSMike Smith }
7211ac4b82bSMike Smith 
7221ac4b82bSMike Smith /*******************************************************************************
7231ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
7241ac4b82bSMike Smith  * status.
7251ac4b82bSMike Smith  */
7261ac4b82bSMike Smith void
7271ac4b82bSMike Smith mlx_intr(void *arg)
7281ac4b82bSMike Smith {
7291ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
7301ac4b82bSMike Smith 
731da8bb3a3SMike Smith     debug_called(1);
7321ac4b82bSMike Smith 
7335792b7feSMike Smith     /* collect finished commands, queue anything waiting */
7340fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7350fca6f8bSJohn Baldwin     mlx_done(sc, 1);
7360fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7371ac4b82bSMike Smith };
7381ac4b82bSMike Smith 
7391ac4b82bSMike Smith /*******************************************************************************
7401ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
7411ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
7421ac4b82bSMike Smith  */
7431ac4b82bSMike Smith int
744b04e4c12SJohn Baldwin mlx_submit_buf(struct mlx_softc *sc, struct bio *bp)
7451ac4b82bSMike Smith {
7464b006d7bSMike Smith 
747da8bb3a3SMike Smith     debug_called(1);
7481ac4b82bSMike Smith 
7490fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
750b04e4c12SJohn Baldwin     bioq_insert_tail(&sc->mlx_bioq, bp);
7511ac4b82bSMike Smith     sc->mlx_waitbufs++;
7521ac4b82bSMike Smith     mlx_startio(sc);
7531ac4b82bSMike Smith     return(0);
7541ac4b82bSMike Smith }
7551ac4b82bSMike Smith 
7561ac4b82bSMike Smith /********************************************************************************
7571ac4b82bSMike Smith  * Accept an open operation on the control device.
7581ac4b82bSMike Smith  */
7591ac4b82bSMike Smith int
76089c9c53dSPoul-Henning Kamp mlx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
7611ac4b82bSMike Smith {
762e78a5a3fSEd Schouten     struct mlx_softc	*sc = dev->si_drv1;
7631ac4b82bSMike Smith 
7640fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
7650fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7661ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
7670fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7680fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
7691ac4b82bSMike Smith     return(0);
7701ac4b82bSMike Smith }
7711ac4b82bSMike Smith 
7721ac4b82bSMike Smith /********************************************************************************
7731ac4b82bSMike Smith  * Accept the last close on the control device.
7741ac4b82bSMike Smith  */
7751ac4b82bSMike Smith int
77689c9c53dSPoul-Henning Kamp mlx_close(struct cdev *dev, int flags, int fmt, struct thread *td)
7771ac4b82bSMike Smith {
778e78a5a3fSEd Schouten     struct mlx_softc	*sc = dev->si_drv1;
7791ac4b82bSMike Smith 
7800fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
7810fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7821ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
7830fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7840fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
7851ac4b82bSMike Smith     return (0);
7861ac4b82bSMike Smith }
7871ac4b82bSMike Smith 
7881ac4b82bSMike Smith /********************************************************************************
7891ac4b82bSMike Smith  * Handle controller-specific control operations.
7901ac4b82bSMike Smith  */
7911ac4b82bSMike Smith int
79289c9c53dSPoul-Henning Kamp mlx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
7931ac4b82bSMike Smith {
794e78a5a3fSEd Schouten     struct mlx_softc		*sc = dev->si_drv1;
795421f2f7dSMike Smith     struct mlx_rebuild_request	*rb = (struct mlx_rebuild_request *)addr;
796421f2f7dSMike Smith     struct mlx_rebuild_status	*rs = (struct mlx_rebuild_status *)addr;
7971ac4b82bSMike Smith     int				*arg = (int *)addr;
7981ac4b82bSMike Smith     struct mlx_pause		*mp;
7991ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
8001ac4b82bSMike Smith     struct mlxd_softc		*mlxd;
8011ac4b82bSMike Smith     int				i, error;
8021ac4b82bSMike Smith 
8031ac4b82bSMike Smith     switch(cmd) {
8041ac4b82bSMike Smith 	/*
8051ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
8061ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
8071ac4b82bSMike Smith 	 * a valid unit on this controller.
8081ac4b82bSMike Smith 	 */
8091ac4b82bSMike Smith     case MLX_NEXT_CHILD:
8101ac4b82bSMike Smith 	/* search system drives */
8110fca6f8bSJohn Baldwin 	MLX_CONFIG_LOCK(sc);
8121ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
8131ac4b82bSMike Smith 	    /* is this one attached? */
8141ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
8151ac4b82bSMike Smith 		/* looking for the next one we come across? */
8161ac4b82bSMike Smith 		if (*arg == -1) {
81729e6fa3aSStephane E. Potvin 		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
8180fca6f8bSJohn Baldwin 		    MLX_CONFIG_UNLOCK(sc);
8191ac4b82bSMike Smith 		    return(0);
8201ac4b82bSMike Smith 		}
8211ac4b82bSMike Smith 		/* we want the one after this one */
8221ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
8231ac4b82bSMike Smith 		    *arg = -1;
8241ac4b82bSMike Smith 	    }
8251ac4b82bSMike Smith 	}
8260fca6f8bSJohn Baldwin 	MLX_CONFIG_UNLOCK(sc);
8271ac4b82bSMike Smith 	return(ENOENT);
8281ac4b82bSMike Smith 
8291ac4b82bSMike Smith 	/*
8301ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
8311ac4b82bSMike Smith 	 */
8321ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
8330fca6f8bSJohn Baldwin 	mtx_lock(&Giant);
8341ac4b82bSMike Smith 	mlx_startup(sc);
8350fca6f8bSJohn Baldwin 	mtx_unlock(&Giant);
8361ac4b82bSMike Smith 	return(0);
8371ac4b82bSMike Smith 
8381ac4b82bSMike Smith 	/*
8391ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
8401ac4b82bSMike Smith 	 * away.
8411ac4b82bSMike Smith 	 */
8421ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
8430fca6f8bSJohn Baldwin 	MLX_CONFIG_LOCK(sc);
8441ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
8450fca6f8bSJohn Baldwin 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) {
8460fca6f8bSJohn Baldwin 	    MLX_CONFIG_UNLOCK(sc);
8471ac4b82bSMike Smith 	    return(ENOENT);
8480fca6f8bSJohn Baldwin 	}
8491ac4b82bSMike Smith 
8501ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
8511ac4b82bSMike Smith 	error = 0;
8521ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
8531ac4b82bSMike Smith 	    error = EBUSY;
8541ac4b82bSMike Smith 	    goto detach_out;
8551ac4b82bSMike Smith 	}
8561ac4b82bSMike Smith 
8571ac4b82bSMike Smith 	/* flush controller */
8580fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
8591ac4b82bSMike Smith 	if (mlx_flush(sc)) {
8600fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
8611ac4b82bSMike Smith 	    error = EBUSY;
8621ac4b82bSMike Smith 	    goto detach_out;
8631ac4b82bSMike Smith 	}
8640fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
8651ac4b82bSMike Smith 
8661ac4b82bSMike Smith 	/* nuke drive */
8671ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
8681ac4b82bSMike Smith 	    goto detach_out;
8691ac4b82bSMike Smith 	dr->ms_disk = 0;
8701ac4b82bSMike Smith 
8711ac4b82bSMike Smith     detach_out:
8720fca6f8bSJohn Baldwin 	MLX_CONFIG_UNLOCK(sc);
8731ac4b82bSMike Smith 	if (error) {
8741ac4b82bSMike Smith 	    printf("failed\n");
8751ac4b82bSMike Smith 	} else {
8761ac4b82bSMike Smith 	    printf("done\n");
8771ac4b82bSMike Smith 	}
8781ac4b82bSMike Smith 	return(error);
8791ac4b82bSMike Smith 
8801ac4b82bSMike Smith 	/*
8811ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
8821ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
8831ac4b82bSMike Smith 	 *
8841ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
8851ac4b82bSMike Smith 	 * to do this right.
8861ac4b82bSMike Smith 	 */
8871ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
8881ac4b82bSMike Smith 	/* Does this command work on this firmware? */
8891ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
8901ac4b82bSMike Smith 	    return(EOPNOTSUPP);
8911ac4b82bSMike Smith 
8920fca6f8bSJohn Baldwin 	/* check time values */
8931ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
8940fca6f8bSJohn Baldwin 	if ((mp->mp_when < 0) || (mp->mp_when > 3600))
8950fca6f8bSJohn Baldwin 	    return(EINVAL);
8960fca6f8bSJohn Baldwin 	if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
8970fca6f8bSJohn Baldwin 	    return(EINVAL);
8980fca6f8bSJohn Baldwin 
8990fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
9001ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
9011ac4b82bSMike Smith 	    /* cancel a pending pause operation */
9021ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
9031ac4b82bSMike Smith 	} else {
9041ac4b82bSMike Smith 	    /* fix for legal channels */
9059eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
9061ac4b82bSMike Smith 
9071ac4b82bSMike Smith 	    /* check for a pause currently running */
9080fca6f8bSJohn Baldwin 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) {
9090fca6f8bSJohn Baldwin 		MLX_IO_UNLOCK(sc);
9101ac4b82bSMike Smith 		return(EBUSY);
9110fca6f8bSJohn Baldwin 	    }
9121ac4b82bSMike Smith 
9131ac4b82bSMike Smith 	    /* looks ok, go with it */
9141ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
9151ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
9161ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
9171ac4b82bSMike Smith 	}
9180fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
9191ac4b82bSMike Smith 	return(0);
9201ac4b82bSMike Smith 
9211ac4b82bSMike Smith 	/*
9221ac4b82bSMike Smith 	 * Accept a command passthrough-style.
9231ac4b82bSMike Smith 	 */
9241ac4b82bSMike Smith     case MLX_COMMAND:
9251ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
9261ac4b82bSMike Smith 
927421f2f7dSMike Smith 	/*
928421f2f7dSMike Smith 	 * Start a rebuild on a given SCSI disk
929421f2f7dSMike Smith 	 */
930421f2f7dSMike Smith     case MLX_REBUILDASYNC:
9310fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
932421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
9330fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
934421f2f7dSMike Smith 	    rb->rr_status = 0x0106;
935421f2f7dSMike Smith 	    return(EBUSY);
936421f2f7dSMike Smith 	}
937421f2f7dSMike Smith 	rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
938421f2f7dSMike Smith 	switch (rb->rr_status) {
939421f2f7dSMike Smith 	case 0:
940421f2f7dSMike Smith 	    error = 0;
941421f2f7dSMike Smith 	    break;
942421f2f7dSMike Smith 	case 0x10000:
943421f2f7dSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
944421f2f7dSMike Smith 	    break;
945421f2f7dSMike Smith 	case 0x0002:
946421f2f7dSMike Smith 	    error = EBUSY;
947421f2f7dSMike Smith 	    break;
948421f2f7dSMike Smith 	case 0x0104:
949421f2f7dSMike Smith 	    error = EIO;
950421f2f7dSMike Smith 	    break;
951421f2f7dSMike Smith 	case 0x0105:
952421f2f7dSMike Smith 	    error = ERANGE;
953421f2f7dSMike Smith 	    break;
954421f2f7dSMike Smith 	case 0x0106:
955421f2f7dSMike Smith 	    error = EBUSY;
956421f2f7dSMike Smith 	    break;
957421f2f7dSMike Smith 	default:
958421f2f7dSMike Smith 	    error = EINVAL;
959421f2f7dSMike Smith 	    break;
960421f2f7dSMike Smith 	}
961421f2f7dSMike Smith 	if (error == 0)
962421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_REBUILD;
9630fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
964421f2f7dSMike Smith 	return(error);
965421f2f7dSMike Smith 
966421f2f7dSMike Smith 	/*
967421f2f7dSMike Smith 	 * Get the status of the current rebuild or consistency check.
968421f2f7dSMike Smith 	 */
969421f2f7dSMike Smith     case MLX_REBUILDSTAT:
9700fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
971421f2f7dSMike Smith 	*rs = sc->mlx_rebuildstat;
9720fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
973421f2f7dSMike Smith 	return(0);
974421f2f7dSMike Smith 
975421f2f7dSMike Smith 	/*
976421f2f7dSMike Smith 	 * Return the per-controller system drive number matching the
977421f2f7dSMike Smith 	 * disk device number in (arg), if it happens to belong to us.
978421f2f7dSMike Smith 	 */
979421f2f7dSMike Smith     case MLX_GET_SYSDRIVE:
980421f2f7dSMike Smith 	error = ENOENT;
9810fca6f8bSJohn Baldwin 	MLX_CONFIG_LOCK(sc);
9820fca6f8bSJohn Baldwin 	mtx_lock(&Giant);
983421f2f7dSMike Smith 	mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg);
9840fca6f8bSJohn Baldwin 	mtx_unlock(&Giant);
985421f2f7dSMike Smith 	if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
986421f2f7dSMike Smith 	    (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
987421f2f7dSMike Smith 	    error = 0;
988421f2f7dSMike Smith 	    *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
989421f2f7dSMike Smith 	}
9900fca6f8bSJohn Baldwin 	MLX_CONFIG_UNLOCK(sc);
991421f2f7dSMike Smith 	return(error);
992421f2f7dSMike Smith 
9931ac4b82bSMike Smith     default:
9941ac4b82bSMike Smith 	return(ENOTTY);
9951ac4b82bSMike Smith     }
9961ac4b82bSMike Smith }
9971ac4b82bSMike Smith 
9981ac4b82bSMike Smith /********************************************************************************
9991ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
10001ac4b82bSMike Smith  */
10011ac4b82bSMike Smith int
10021ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
1003b40ce416SJulian Elischer 		caddr_t addr, int32_t flag, struct thread *td)
10041ac4b82bSMike Smith {
10051ac4b82bSMike Smith     int				*arg = (int *)addr;
1006421f2f7dSMike Smith     int				error, result;
10071ac4b82bSMike Smith 
10081ac4b82bSMike Smith     switch(cmd) {
10091ac4b82bSMike Smith 	/*
10101ac4b82bSMike Smith 	 * Return the current status of this drive.
10111ac4b82bSMike Smith 	 */
10121ac4b82bSMike Smith     case MLXD_STATUS:
10130fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
10141ac4b82bSMike Smith 	*arg = drive->ms_state;
10150fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
10161ac4b82bSMike Smith 	return(0);
10171ac4b82bSMike Smith 
10181ac4b82bSMike Smith 	/*
1019421f2f7dSMike Smith 	 * Start a background consistency check on this drive.
10201ac4b82bSMike Smith 	 */
1021421f2f7dSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
10220fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
1023421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
10240fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
1025421f2f7dSMike Smith 	    *arg = 0x0106;
10261ac4b82bSMike Smith 	    return(EBUSY);
1027421f2f7dSMike Smith 	}
1028421f2f7dSMike Smith 	result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
1029421f2f7dSMike Smith 	switch (result) {
10301ac4b82bSMike Smith 	case 0:
10311ac4b82bSMike Smith 	    error = 0;
10321ac4b82bSMike Smith 	    break;
10331ac4b82bSMike Smith 	case 0x10000:
10341ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
10351ac4b82bSMike Smith 	    break;
10361ac4b82bSMike Smith 	case 0x0002:
10371ac4b82bSMike Smith 	    error = EIO;
10381ac4b82bSMike Smith 	    break;
10391ac4b82bSMike Smith 	case 0x0105:
10401ac4b82bSMike Smith 	    error = ERANGE;
10411ac4b82bSMike Smith 	    break;
1042421f2f7dSMike Smith 	case 0x0106:
1043421f2f7dSMike Smith 	    error = EBUSY;
1044421f2f7dSMike Smith 	    break;
10451ac4b82bSMike Smith 	default:
10461ac4b82bSMike Smith 	    error = EINVAL;
10471ac4b82bSMike Smith 	    break;
10481ac4b82bSMike Smith 	}
1049421f2f7dSMike Smith 	if (error == 0)
1050421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_CHECK;
10510fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
1052421f2f7dSMike Smith 	*arg = result;
10531ac4b82bSMike Smith 	return(error);
10541ac4b82bSMike Smith 
10551ac4b82bSMike Smith     }
10561ac4b82bSMike Smith     return(ENOIOCTL);
10571ac4b82bSMike Smith }
10581ac4b82bSMike Smith 
10591ac4b82bSMike Smith 
10601ac4b82bSMike Smith /********************************************************************************
10611ac4b82bSMike Smith  ********************************************************************************
10621ac4b82bSMike Smith                                                                 Status Monitoring
10631ac4b82bSMike Smith  ********************************************************************************
10641ac4b82bSMike Smith  ********************************************************************************/
10651ac4b82bSMike Smith 
10661ac4b82bSMike Smith /********************************************************************************
10671ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
10681ac4b82bSMike Smith  */
10691ac4b82bSMike Smith static void
10701ac4b82bSMike Smith mlx_periodic(void *data)
10711ac4b82bSMike Smith {
10721ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
10731ac4b82bSMike Smith 
1074da8bb3a3SMike Smith     debug_called(1);
10750fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
10761ac4b82bSMike Smith 
10771ac4b82bSMike Smith     /*
10781ac4b82bSMike Smith      * Run a bus pause?
10791ac4b82bSMike Smith      */
10801ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
10811ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
10821ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
10831ac4b82bSMike Smith 
10841ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
10851ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
10861ac4b82bSMike Smith 	sysbeep(500, hz);
10871ac4b82bSMike Smith 
10881ac4b82bSMike Smith 	/*
10891ac4b82bSMike Smith 	 * Bus pause still running?
10901ac4b82bSMike Smith 	 */
10911ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
10921ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
10931ac4b82bSMike Smith 
10941ac4b82bSMike Smith 	/* time to stop bus pause? */
10951ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
10961ac4b82bSMike Smith 	    mlx_pause_action(sc);
10971ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
10981ac4b82bSMike Smith 	    sysbeep(500, hz);
10991ac4b82bSMike Smith 	} else {
11001ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
11011ac4b82bSMike Smith 	}
11021ac4b82bSMike Smith 
11031ac4b82bSMike Smith 	/*
11041ac4b82bSMike Smith 	 * Run normal periodic activities?
11051ac4b82bSMike Smith 	 */
11065792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
11071ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
11081ac4b82bSMike Smith 
11091ac4b82bSMike Smith 	/*
11101ac4b82bSMike Smith 	 * Check controller status.
11115792b7feSMike Smith 	 *
11125792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
11131ac4b82bSMike Smith 	 */
1114da8bb3a3SMike Smith 	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
1115da8bb3a3SMike Smith 		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
11161ac4b82bSMike Smith 
11171ac4b82bSMike Smith 	/*
11181ac4b82bSMike Smith 	 * Check system drive status.
11191ac4b82bSMike Smith 	 *
11201ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
11211ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
11221ac4b82bSMike Smith 	 *     online.
11231ac4b82bSMike Smith 	 */
11241ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
11251ac4b82bSMike Smith 			mlx_periodic_enquiry);
11261ac4b82bSMike Smith 
11271ac4b82bSMike Smith     }
11281ac4b82bSMike Smith 
1129421f2f7dSMike Smith     /* get drive rebuild/check status */
1130421f2f7dSMike Smith     /* XXX should check sc->mlx_background if this is only valid while in progress */
1131421f2f7dSMike Smith     mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
1132421f2f7dSMike Smith 
11335792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
11340fca6f8bSJohn Baldwin     mlx_done(sc, 1);
11351ac4b82bSMike Smith 
11361ac4b82bSMike Smith     /* reschedule another poll next second or so */
11370fca6f8bSJohn Baldwin     callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc);
11381ac4b82bSMike Smith }
11391ac4b82bSMike Smith 
11401ac4b82bSMike Smith /********************************************************************************
11411ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
11421ac4b82bSMike Smith  */
11431ac4b82bSMike Smith static void
11441ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
11451ac4b82bSMike Smith {
11461ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
11471ac4b82bSMike Smith 
1148da8bb3a3SMike Smith     debug_called(1);
11490fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
11501ac4b82bSMike Smith 
11511ac4b82bSMike Smith     /* Command completed OK? */
11521ac4b82bSMike Smith     if (mc->mc_status != 0) {
1153da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
11541ac4b82bSMike Smith 	goto out;
11551ac4b82bSMike Smith     }
11561ac4b82bSMike Smith 
11571ac4b82bSMike Smith     /* respond to command */
11581ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
11591ac4b82bSMike Smith 	/*
1160da8bb3a3SMike Smith 	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
1161da8bb3a3SMike Smith 	 * pointer yet.
1162da8bb3a3SMike Smith 	 */
1163da8bb3a3SMike Smith     case MLX_CMD_ENQUIRY_OLD:
1164da8bb3a3SMike Smith     {
1165da8bb3a3SMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
1166da8bb3a3SMike Smith 	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
1167da8bb3a3SMike Smith 	int				i;
1168da8bb3a3SMike Smith 
1169da8bb3a3SMike Smith 	/* convert data in-place to new format */
1170da8bb3a3SMike Smith 	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
1171da8bb3a3SMike Smith 	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1172da8bb3a3SMike Smith 	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1173da8bb3a3SMike Smith 	}
1174da8bb3a3SMike Smith 	me->me_misc_flags        = 0;
1175da8bb3a3SMike Smith 	me->me_rebuild_count     = meo->me_rebuild_count;
1176da8bb3a3SMike Smith 	me->me_dead_count        = meo->me_dead_count;
1177da8bb3a3SMike Smith 	me->me_critical_sd_count = meo->me_critical_sd_count;
1178da8bb3a3SMike Smith 	me->me_event_log_seq_num = 0;
1179da8bb3a3SMike Smith 	me->me_offline_sd_count  = meo->me_offline_sd_count;
1180da8bb3a3SMike Smith 	me->me_max_commands      = meo->me_max_commands;
1181da8bb3a3SMike Smith 	me->me_rebuild_flag      = meo->me_rebuild_flag;
1182da8bb3a3SMike Smith 	me->me_fwmajor           = meo->me_fwmajor;
1183da8bb3a3SMike Smith 	me->me_fwminor           = meo->me_fwminor;
1184da8bb3a3SMike Smith 	me->me_status_flags      = meo->me_status_flags;
1185da8bb3a3SMike Smith 	me->me_flash_age         = meo->me_flash_age;
1186da8bb3a3SMike Smith 	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
1187da8bb3a3SMike Smith 	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
1188da8bb3a3SMike Smith 		me->me_drvsize[i] = 0;		/* drive beyond supported range */
1189da8bb3a3SMike Smith 	    } else {
1190da8bb3a3SMike Smith 		me->me_drvsize[i] = meo->me_drvsize[i];
1191da8bb3a3SMike Smith 	    }
1192da8bb3a3SMike Smith 	}
1193da8bb3a3SMike Smith 	me->me_num_sys_drvs = meo->me_num_sys_drvs;
1194da8bb3a3SMike Smith     }
1195da8bb3a3SMike Smith     /* FALLTHROUGH */
1196da8bb3a3SMike Smith 
1197da8bb3a3SMike Smith 	/*
11981ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
11991ac4b82bSMike Smith 	 * checking the event log.
12001ac4b82bSMike Smith 	 */
12011ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
12021ac4b82bSMike Smith     {
12031ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
12041ac4b82bSMike Smith 
1205421f2f7dSMike Smith 	if (sc->mlx_currevent == -1) {
12069eee27f1SMike Smith 	    /* initialise our view of the event log */
12079eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
12085d278f5cSMike Smith 	} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
12091ac4b82bSMike Smith 	    /* record where current events are up to */
12101ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
1211da8bb3a3SMike Smith 	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
12121ac4b82bSMike Smith 
12135d278f5cSMike Smith 	    /* mark the event log as busy */
12140fca6f8bSJohn Baldwin 	    sc->mlx_flags |= MLX_EVENTLOG_BUSY;
12155d278f5cSMike Smith 
12169eee27f1SMike Smith 	    /* drain new eventlog entries */
12171ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
12181ac4b82bSMike Smith 	}
12191ac4b82bSMike Smith 	break;
12201ac4b82bSMike Smith     }
12211ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
12221ac4b82bSMike Smith     {
12231ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
12241ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
12251ac4b82bSMike Smith 	int				i;
12261ac4b82bSMike Smith 
12271ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
12281ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
12291ac4b82bSMike Smith 	     i++) {
12301ac4b82bSMike Smith 
12311ac4b82bSMike Smith 	    /* has state been changed by controller? */
12321ac4b82bSMike Smith 	    if (dr->ms_state != mes[i].sd_state) {
12331ac4b82bSMike Smith 		switch(mes[i].sd_state) {
12341ac4b82bSMike Smith 		case MLX_SYSD_OFFLINE:
12351ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive offline\n");
12361ac4b82bSMike Smith 		    break;
12371ac4b82bSMike Smith 		case MLX_SYSD_ONLINE:
12381ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive online\n");
12391ac4b82bSMike Smith 		    break;
12401ac4b82bSMike Smith 		case MLX_SYSD_CRITICAL:
12411ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive critical\n");
12421ac4b82bSMike Smith 		    break;
12431ac4b82bSMike Smith 		}
12441ac4b82bSMike Smith 		/* save new state */
12451ac4b82bSMike Smith 		dr->ms_state = mes[i].sd_state;
12461ac4b82bSMike Smith 	    }
12471ac4b82bSMike Smith 	}
12481ac4b82bSMike Smith 	break;
12491ac4b82bSMike Smith     }
12501ac4b82bSMike Smith     default:
12516e551fb6SDavid E. O'Brien 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]);
12521ac4b82bSMike Smith 	break;
12531ac4b82bSMike Smith     }
12541ac4b82bSMike Smith 
12551ac4b82bSMike Smith  out:
12561ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12571ac4b82bSMike Smith     mlx_releasecmd(mc);
12581ac4b82bSMike Smith }
12591ac4b82bSMike Smith 
12601b4404f9SScott Long static void
12611b4404f9SScott Long mlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
12621b4404f9SScott Long {
12631b4404f9SScott Long     struct mlx_command *mc;
12641b4404f9SScott Long 
12651b4404f9SScott Long     mc = (struct mlx_command *)arg;
12661b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
12671b4404f9SScott Long 
12681b4404f9SScott Long     /* build the command to get one entry */
12691b4404f9SScott Long     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1,
12701b4404f9SScott Long 		   mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
12711b4404f9SScott Long     mc->mc_complete = mlx_periodic_eventlog_respond;
12721b4404f9SScott Long     mc->mc_private = mc;
12731b4404f9SScott Long 
12741b4404f9SScott Long     /* start the command */
12751b4404f9SScott Long     if (mlx_start(mc) != 0) {
12761b4404f9SScott Long 	mlx_releasecmd(mc);
12771b4404f9SScott Long 	free(mc->mc_data, M_DEVBUF);
12781b4404f9SScott Long 	mc->mc_data = NULL;
12791b4404f9SScott Long     }
12801b4404f9SScott Long 
12811b4404f9SScott Long }
12821b4404f9SScott Long 
12831ac4b82bSMike Smith /********************************************************************************
12841ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
12851ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
12861ac4b82bSMike Smith  */
12871ac4b82bSMike Smith static void
12881ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
12891ac4b82bSMike Smith {
12901ac4b82bSMike Smith     struct mlx_command	*mc;
12911ac4b82bSMike Smith     void		*result = NULL;
12921b4404f9SScott Long     int			error = 0;
12931ac4b82bSMike Smith 
1294da8bb3a3SMike Smith     debug_called(1);
12950fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
12961ac4b82bSMike Smith 
12971ac4b82bSMike Smith     /* get ourselves a command buffer */
12981ac4b82bSMike Smith     error = 1;
12991ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
13001ac4b82bSMike Smith 	goto out;
13011b4404f9SScott Long 
13021ac4b82bSMike Smith     /* allocate the response structure */
13031b4404f9SScott Long     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF,
13041b4404f9SScott Long 			 M_NOWAIT)) == NULL)
13051ac4b82bSMike Smith 	goto out;
13061b4404f9SScott Long 
13071ac4b82bSMike Smith     /* get a command slot */
13081ac4b82bSMike Smith     if (mlx_getslot(mc))
13091ac4b82bSMike Smith 	goto out;
13101ac4b82bSMike Smith 
13111ac4b82bSMike Smith     /* map the command so the controller can see it */
13121ac4b82bSMike Smith     mc->mc_data = result;
131333c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
13141b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
13151b4404f9SScott Long 			    mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT);
13161ac4b82bSMike Smith 
13171ac4b82bSMike Smith  out:
131833c8cb18SMike Smith     if (error != 0) {
13191ac4b82bSMike Smith 	if (mc != NULL)
13201ac4b82bSMike Smith 	    mlx_releasecmd(mc);
13211b4404f9SScott Long 	if ((result != NULL) && (mc->mc_data != NULL))
13221ac4b82bSMike Smith 	    free(result, M_DEVBUF);
13231ac4b82bSMike Smith     }
132433c8cb18SMike Smith }
13251ac4b82bSMike Smith 
13261ac4b82bSMike Smith /********************************************************************************
13271ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
13281ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
13291ac4b82bSMike Smith  */
13301ac4b82bSMike Smith static char *mlx_sense_messages[] = {
13311ac4b82bSMike Smith     "because write recovery failed",
13321ac4b82bSMike Smith     "because of SCSI bus reset failure",
13331ac4b82bSMike Smith     "because of double check condition",
13341ac4b82bSMike Smith     "because it was removed",
13351ac4b82bSMike Smith     "because of gross error on SCSI chip",
13361ac4b82bSMike Smith     "because of bad tag returned from drive",
13371ac4b82bSMike Smith     "because of timeout on SCSI command",
13381ac4b82bSMike Smith     "because of reset SCSI command issued from system",
13391ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
13401ac4b82bSMike Smith     "because of 'kill drive' command from system",
13411ac4b82bSMike Smith     "because of selection timeout",
13421ac4b82bSMike Smith     "due to SCSI phase sequence error",
13431ac4b82bSMike Smith     "due to unknown status"
13441ac4b82bSMike Smith };
13451ac4b82bSMike Smith 
13461ac4b82bSMike Smith static void
13471ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
13481ac4b82bSMike Smith {
13491ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
13501ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
13511ac4b82bSMike Smith     char			*reason;
13521ac4b82bSMike Smith 
1353da8bb3a3SMike Smith     debug_called(1);
13540fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
13551ac4b82bSMike Smith 
13565792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
13571ac4b82bSMike Smith     if (mc->mc_status == 0) {
13581ac4b82bSMike Smith 
13591ac4b82bSMike Smith 	/* handle event log message */
13601ac4b82bSMike Smith 	switch(el->el_type) {
13611ac4b82bSMike Smith 	    /*
13621ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
13631ac4b82bSMike Smith 	     * The tests here are probably incomplete.
13641ac4b82bSMike Smith 	     */
13651ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
13661ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
13671ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
13681ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
136973a1170aSPedro F. Giffuni 		if (el->el_asq < nitems(mlx_sense_messages)) {
13701ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
13711ac4b82bSMike Smith 		} else {
13721ac4b82bSMike Smith 		    reason = "for unknown reason";
13731ac4b82bSMike Smith 		}
13741ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
13751ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
13761ac4b82bSMike Smith 	    }
13771ac4b82bSMike Smith 	    /* SCSI drive was reset? */
13781ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
13791ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
13801ac4b82bSMike Smith 			      el->el_channel, el->el_target);
13811ac4b82bSMike Smith 	    }
13821ac4b82bSMike Smith 	    /* SCSI drive error? */
13831ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
13841ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
13851ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
13861ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
13871ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
13881ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
13891ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
13901ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
13911ac4b82bSMike Smith 	    }
13921ac4b82bSMike Smith 	    break;
13931ac4b82bSMike Smith 
13941ac4b82bSMike Smith 	default:
13951ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
13961ac4b82bSMike Smith 	    break;
13971ac4b82bSMike Smith 	}
13981ac4b82bSMike Smith     } else {
13991ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
14005d278f5cSMike Smith 	/* give up on all the outstanding messages, as we may have come unsynched */
14015d278f5cSMike Smith 	sc->mlx_lastevent = sc->mlx_currevent;
14021ac4b82bSMike Smith     }
14031ac4b82bSMike Smith 
14041ac4b82bSMike Smith     /* dispose of command and data */
14051ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
14061ac4b82bSMike Smith     mlx_releasecmd(mc);
14071ac4b82bSMike Smith 
14081ac4b82bSMike Smith     /* is there another message to obtain? */
14095d278f5cSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent) {
14101ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
14115d278f5cSMike Smith     } else {
14125d278f5cSMike Smith 	/* clear log-busy status */
14130fca6f8bSJohn Baldwin 	sc->mlx_flags &= ~MLX_EVENTLOG_BUSY;
14145d278f5cSMike Smith     }
14151ac4b82bSMike Smith }
14161ac4b82bSMike Smith 
14171ac4b82bSMike Smith /********************************************************************************
1418421f2f7dSMike Smith  * Handle check/rebuild operations in progress.
14191ac4b82bSMike Smith  */
14201ac4b82bSMike Smith static void
14211ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
14221ac4b82bSMike Smith {
14231ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
1424421f2f7dSMike Smith     struct mlx_rebuild_status	*mr = (struct mlx_rebuild_status *)mc->mc_data;
14251ac4b82bSMike Smith 
14260fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
14271ac4b82bSMike Smith     switch(mc->mc_status) {
1428421f2f7dSMike Smith     case 0:				/* operation running, update stats */
1429421f2f7dSMike Smith 	sc->mlx_rebuildstat = *mr;
1430421f2f7dSMike Smith 
1431421f2f7dSMike Smith 	/* spontaneous rebuild/check? */
1432421f2f7dSMike Smith 	if (sc->mlx_background == 0) {
1433421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
1434421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
1435421f2f7dSMike Smith 	}
14361ac4b82bSMike Smith 	break;
14371ac4b82bSMike Smith 
1438421f2f7dSMike Smith     case 0x0105:			/* nothing running, finalise stats and report */
1439421f2f7dSMike Smith 	switch(sc->mlx_background) {
1440421f2f7dSMike Smith 	case MLX_BACKGROUND_CHECK:
1441421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");	/* XXX print drive? */
1442421f2f7dSMike Smith 	    break;
1443421f2f7dSMike Smith 	case MLX_BACKGROUND_REBUILD:
1444421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "drive rebuild completed\n");	/* XXX print channel/target? */
1445421f2f7dSMike Smith 	    break;
1446421f2f7dSMike Smith 	case MLX_BACKGROUND_SPONTANEOUS:
1447421f2f7dSMike Smith 	default:
1448421f2f7dSMike Smith 	    /* if we have previously been non-idle, report the transition */
1449421f2f7dSMike Smith 	    if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
1450421f2f7dSMike Smith 		device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
14511ac4b82bSMike Smith 	    }
1452421f2f7dSMike Smith 	}
1453421f2f7dSMike Smith 	sc->mlx_background = 0;
1454421f2f7dSMike Smith 	sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
14551ac4b82bSMike Smith 	break;
14561ac4b82bSMike Smith     }
14571ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
14581ac4b82bSMike Smith     mlx_releasecmd(mc);
14591ac4b82bSMike Smith }
14601ac4b82bSMike Smith 
14611ac4b82bSMike Smith /********************************************************************************
14621ac4b82bSMike Smith  ********************************************************************************
14631ac4b82bSMike Smith                                                                     Channel Pause
14641ac4b82bSMike Smith  ********************************************************************************
14651ac4b82bSMike Smith  ********************************************************************************/
14661ac4b82bSMike Smith 
14671ac4b82bSMike Smith /********************************************************************************
14681ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
14691ac4b82bSMike Smith  * the pause.
14701ac4b82bSMike Smith  */
14711ac4b82bSMike Smith static void
14721ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
14731ac4b82bSMike Smith {
14741ac4b82bSMike Smith     struct mlx_command	*mc;
14751ac4b82bSMike Smith     int			failsafe, i, command;
14761ac4b82bSMike Smith 
14770fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
14780fca6f8bSJohn Baldwin 
14791ac4b82bSMike Smith     /* What are we doing here? */
14801ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
14811ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
14821ac4b82bSMike Smith 	failsafe = 0;
14831ac4b82bSMike Smith 
14841ac4b82bSMike Smith     } else {
14851ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
14861ac4b82bSMike Smith 
14871ac4b82bSMike Smith 	/*
14881ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
14891ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
14901ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
14911ac4b82bSMike Smith 	 */
14921ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
14931ac4b82bSMike Smith 	if (failsafe > 0xf) {
14941ac4b82bSMike Smith 	    failsafe = 0xf;
14951ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
14961ac4b82bSMike Smith 	}
14971ac4b82bSMike Smith     }
14981ac4b82bSMike Smith 
14991ac4b82bSMike Smith     /* build commands for every channel requested */
15009eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
15011ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
15021ac4b82bSMike Smith 
15031ac4b82bSMike Smith 	    /* get ourselves a command buffer */
15041ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
15051ac4b82bSMike Smith 		goto fail;
15061ac4b82bSMike Smith 	    /* get a command slot */
15071ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
15081ac4b82bSMike Smith 	    if (mlx_getslot(mc))
15091ac4b82bSMike Smith 		goto fail;
15101ac4b82bSMike Smith 
15111ac4b82bSMike Smith 	    /* build the command */
15121ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
15131ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
15141ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
15151ac4b82bSMike Smith 	    if (mlx_start(mc))
15161ac4b82bSMike Smith 		goto fail;
15171ac4b82bSMike Smith 	    /* command submitted OK */
15181ac4b82bSMike Smith 	    return;
15191ac4b82bSMike Smith 
15201ac4b82bSMike Smith 	fail:
15211ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
15221ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
15231ac4b82bSMike Smith 	    if (mc != NULL)
15241ac4b82bSMike Smith 		mlx_releasecmd(mc);
15251ac4b82bSMike Smith 	}
15261ac4b82bSMike Smith     }
15271ac4b82bSMike Smith }
15281ac4b82bSMike Smith 
15291ac4b82bSMike Smith static void
15301ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
15311ac4b82bSMike Smith {
15321ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
15331ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
15341ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
15351ac4b82bSMike Smith 
15360fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
15371ac4b82bSMike Smith     if (mc->mc_status != 0) {
15381ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
15391ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
15401ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
15411ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
154272c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
15431ac4b82bSMike Smith     } else {
15441ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
15451ac4b82bSMike Smith     }
15461ac4b82bSMike Smith     mlx_releasecmd(mc);
15471ac4b82bSMike Smith }
15481ac4b82bSMike Smith 
15491ac4b82bSMike Smith /********************************************************************************
15501ac4b82bSMike Smith  ********************************************************************************
15511ac4b82bSMike Smith                                                                Command Submission
15521ac4b82bSMike Smith  ********************************************************************************
15531ac4b82bSMike Smith  ********************************************************************************/
15541ac4b82bSMike Smith 
15551b4404f9SScott Long static void
15561b4404f9SScott Long mlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
15571b4404f9SScott Long {
15581b4404f9SScott Long     struct mlx_softc *sc;
15591b4404f9SScott Long     struct mlx_command *mc;
15601b4404f9SScott Long 
15611b4404f9SScott Long     mc = (struct mlx_command *)arg;
15621b4404f9SScott Long     if (error)
15631b4404f9SScott Long 	return;
15641b4404f9SScott Long 
15651b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
15661b4404f9SScott Long 
15671b4404f9SScott Long     /* build an enquiry command */
15681b4404f9SScott Long     sc = mc->mc_sc;
15691b4404f9SScott Long     mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
15701b4404f9SScott Long 
15711b4404f9SScott Long     /* do we want a completion callback? */
15721b4404f9SScott Long     if (mc->mc_complete != NULL) {
15731b4404f9SScott Long 	if ((error = mlx_start(mc)) != 0)
15741b4404f9SScott Long 	    return;
15751b4404f9SScott Long     } else {
15761b4404f9SScott Long 	/* run the command in either polled or wait mode */
15771b4404f9SScott Long 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) :
15781b4404f9SScott Long 						mlx_poll_command(mc))
15791b4404f9SScott Long 	    return;
15801b4404f9SScott Long 
15811b4404f9SScott Long 	/* command completed OK? */
15821b4404f9SScott Long 	if (mc->mc_status != 0) {
15831b4404f9SScott Long 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n",
15841b4404f9SScott Long 			  mlx_diagnose_command(mc));
15851b4404f9SScott Long 	    return;
15861b4404f9SScott Long 	}
15871b4404f9SScott Long     }
15881b4404f9SScott Long }
15891b4404f9SScott Long 
15901ac4b82bSMike Smith /********************************************************************************
15911ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
15921ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
15931ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
15941ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
15951ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
15961ac4b82bSMike Smith  */
15971ac4b82bSMike Smith static void *
15981ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
15991ac4b82bSMike Smith {
16001ac4b82bSMike Smith     struct mlx_command	*mc;
16011ac4b82bSMike Smith     void		*result;
16021ac4b82bSMike Smith     int			error;
16031ac4b82bSMike Smith 
1604da8bb3a3SMike Smith     debug_called(1);
16050fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
16061ac4b82bSMike Smith 
16071ac4b82bSMike Smith     /* get ourselves a command buffer */
16081ac4b82bSMike Smith     error = 1;
16091ac4b82bSMike Smith     result = NULL;
16101ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16111ac4b82bSMike Smith 	goto out;
16121ac4b82bSMike Smith     /* allocate the response structure */
16131ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
16141ac4b82bSMike Smith 	goto out;
16151ac4b82bSMike Smith     /* get a command slot */
16161ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
16171ac4b82bSMike Smith     if (mlx_getslot(mc))
16181ac4b82bSMike Smith 	goto out;
16191ac4b82bSMike Smith 
16201ac4b82bSMike Smith     /* map the command so the controller can see it */
16211ac4b82bSMike Smith     mc->mc_data = result;
16221ac4b82bSMike Smith     mc->mc_length = bufsize;
16231b4404f9SScott Long     mc->mc_command = command;
16241ac4b82bSMike Smith 
16251ac4b82bSMike Smith     if (complete != NULL) {
16261ac4b82bSMike Smith 	mc->mc_complete = complete;
16271ac4b82bSMike Smith 	mc->mc_private = mc;
16281b4404f9SScott Long     }
16291ac4b82bSMike Smith 
16301b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
16311b4404f9SScott Long 			    mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT);
16321b4404f9SScott Long 
16331ac4b82bSMike Smith  out:
16341ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
163523691262SSam Leffler     if ((mc != NULL) && (mc->mc_complete == NULL))
16361ac4b82bSMike Smith 	mlx_releasecmd(mc);
163733c8cb18SMike Smith     /* we got an error, and we allocated a result */
1638d4881905SScott Long     if ((error != 0) && (result != NULL)) {
1639d4881905SScott Long 	free(result, M_DEVBUF);
1640a7700303SScott Long 	result = NULL;
16411ac4b82bSMike Smith     }
16421ac4b82bSMike Smith     return(result);
16431ac4b82bSMike Smith }
16441ac4b82bSMike Smith 
16451ac4b82bSMike Smith 
16461ac4b82bSMike Smith /********************************************************************************
16471ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
16481ac4b82bSMike Smith  *
16491ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
16501ac4b82bSMike Smith  * the flush operation completes or fails.
16511ac4b82bSMike Smith  */
16521ac4b82bSMike Smith static int
16531ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
16541ac4b82bSMike Smith {
16551ac4b82bSMike Smith     struct mlx_command	*mc;
16561ac4b82bSMike Smith     int			error;
16571ac4b82bSMike Smith 
1658da8bb3a3SMike Smith     debug_called(1);
16590fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
16601ac4b82bSMike Smith 
16611ac4b82bSMike Smith     /* get ourselves a command buffer */
16621ac4b82bSMike Smith     error = 1;
16631ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16641ac4b82bSMike Smith 	goto out;
16651ac4b82bSMike Smith     /* get a command slot */
16661ac4b82bSMike Smith     if (mlx_getslot(mc))
16671ac4b82bSMike Smith 	goto out;
16681ac4b82bSMike Smith 
16691ac4b82bSMike Smith     /* build a flush command */
16701ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
16711ac4b82bSMike Smith 
16725792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
16735792b7feSMike Smith     if (mlx_poll_command(mc))
16741ac4b82bSMike Smith 	goto out;
16751ac4b82bSMike Smith 
16761ac4b82bSMike Smith     /* command completed OK? */
16771ac4b82bSMike Smith     if (mc->mc_status != 0) {
16781ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
16791ac4b82bSMike Smith 	goto out;
16801ac4b82bSMike Smith     }
16811ac4b82bSMike Smith 
16821ac4b82bSMike Smith     error = 0;			/* success */
16831ac4b82bSMike Smith  out:
16841ac4b82bSMike Smith     if (mc != NULL)
16851ac4b82bSMike Smith 	mlx_releasecmd(mc);
16861ac4b82bSMike Smith     return(error);
16871ac4b82bSMike Smith }
16881ac4b82bSMike Smith 
16891ac4b82bSMike Smith /********************************************************************************
1690421f2f7dSMike Smith  * Start a background consistency check on (drive).
1691421f2f7dSMike Smith  *
1692421f2f7dSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
1693421f2f7dSMike Smith  * operation has started or been refused.
1694421f2f7dSMike Smith  */
1695421f2f7dSMike Smith static int
1696421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive)
1697421f2f7dSMike Smith {
1698421f2f7dSMike Smith     struct mlx_command	*mc;
1699421f2f7dSMike Smith     int			error;
1700421f2f7dSMike Smith 
1701421f2f7dSMike Smith     debug_called(1);
17020fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
1703421f2f7dSMike Smith 
1704421f2f7dSMike Smith     /* get ourselves a command buffer */
1705421f2f7dSMike Smith     error = 0x10000;
1706421f2f7dSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
1707421f2f7dSMike Smith 	goto out;
1708421f2f7dSMike Smith     /* get a command slot */
1709421f2f7dSMike Smith     if (mlx_getslot(mc))
1710421f2f7dSMike Smith 	goto out;
1711421f2f7dSMike Smith 
1712421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
1713421f2f7dSMike Smith     mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
1714421f2f7dSMike Smith 
1715421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1716421f2f7dSMike Smith     if (mlx_wait_command(mc))
1717421f2f7dSMike Smith 	goto out;
1718421f2f7dSMike Smith 
1719421f2f7dSMike Smith     /* command completed OK? */
1720421f2f7dSMike Smith     if (mc->mc_status != 0) {
1721421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
1722421f2f7dSMike Smith     } else {
1723421f2f7dSMike Smith 	device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
1724421f2f7dSMike Smith     }
1725421f2f7dSMike Smith     error = mc->mc_status;
1726421f2f7dSMike Smith 
1727421f2f7dSMike Smith  out:
1728421f2f7dSMike Smith     if (mc != NULL)
1729421f2f7dSMike Smith 	mlx_releasecmd(mc);
1730421f2f7dSMike Smith     return(error);
1731421f2f7dSMike Smith }
1732421f2f7dSMike Smith 
1733421f2f7dSMike Smith /********************************************************************************
1734421f2f7dSMike Smith  * Start a background rebuild of the physical drive at (channel),(target).
17351ac4b82bSMike Smith  *
17361ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
17371ac4b82bSMike Smith  * operation has started or been refused.
17381ac4b82bSMike Smith  */
17391ac4b82bSMike Smith static int
17401ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
17411ac4b82bSMike Smith {
17421ac4b82bSMike Smith     struct mlx_command	*mc;
17431ac4b82bSMike Smith     int			error;
17441ac4b82bSMike Smith 
1745da8bb3a3SMike Smith     debug_called(1);
17460fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
17471ac4b82bSMike Smith 
17481ac4b82bSMike Smith     /* get ourselves a command buffer */
17491ac4b82bSMike Smith     error = 0x10000;
17501ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
17511ac4b82bSMike Smith 	goto out;
17521ac4b82bSMike Smith     /* get a command slot */
17531ac4b82bSMike Smith     if (mlx_getslot(mc))
17541ac4b82bSMike Smith 	goto out;
17551ac4b82bSMike Smith 
1756421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
17571ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
17581ac4b82bSMike Smith 
1759421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1760421f2f7dSMike Smith     if (mlx_wait_command(mc))
17611ac4b82bSMike Smith 	goto out;
17621ac4b82bSMike Smith 
17631ac4b82bSMike Smith     /* command completed OK? */
17641ac4b82bSMike Smith     if (mc->mc_status != 0) {
17651ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
17661ac4b82bSMike Smith     } else {
1767421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
17681ac4b82bSMike Smith     }
17691ac4b82bSMike Smith     error = mc->mc_status;
17701ac4b82bSMike Smith 
17711ac4b82bSMike Smith  out:
17721ac4b82bSMike Smith     if (mc != NULL)
17731ac4b82bSMike Smith 	mlx_releasecmd(mc);
17741ac4b82bSMike Smith     return(error);
17751ac4b82bSMike Smith }
17761ac4b82bSMike Smith 
17771ac4b82bSMike Smith /********************************************************************************
17781ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
17791ac4b82bSMike Smith  *
17801ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
17811ac4b82bSMike Smith  */
17821ac4b82bSMike Smith static int
17831ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
17841ac4b82bSMike Smith {
17851ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17861ac4b82bSMike Smith     int			error, count;
17871ac4b82bSMike Smith 
1788da8bb3a3SMike Smith     debug_called(1);
17890fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
17901ac4b82bSMike Smith 
17911ac4b82bSMike Smith     mc->mc_complete = NULL;
17921ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
17931ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
17941ac4b82bSMike Smith 	return(error);
17951ac4b82bSMike Smith 
17961ac4b82bSMike Smith     count = 0;
17971ac4b82bSMike Smith     /* XXX better timeout? */
17981ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
17990fca6f8bSJohn Baldwin 	mtx_sleep(mc->mc_private, &sc->mlx_io_lock, PRIBIO | PCATCH, "mlxwcmd", hz);
18001ac4b82bSMike Smith     }
18011ac4b82bSMike Smith 
18021ac4b82bSMike Smith     if (mc->mc_status != 0) {
1803da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
18041ac4b82bSMike Smith 	return(EIO);
18051ac4b82bSMike Smith     }
18061ac4b82bSMike Smith     return(0);
18071ac4b82bSMike Smith }
18081ac4b82bSMike Smith 
18091ac4b82bSMike Smith 
18101ac4b82bSMike Smith /********************************************************************************
18111ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
18121ac4b82bSMike Smith  *
1813da8bb3a3SMike Smith  * Should only be used when interrupts can't be relied upon. Returns 0 on
18141ac4b82bSMike Smith  * success, nonzero on error.
18151ac4b82bSMike Smith  * Successfully completed commands are dequeued.
18161ac4b82bSMike Smith  */
18171ac4b82bSMike Smith static int
18181ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
18191ac4b82bSMike Smith {
18201ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18210fca6f8bSJohn Baldwin     int			error, count;
18221ac4b82bSMike Smith 
1823da8bb3a3SMike Smith     debug_called(1);
18240fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
18251ac4b82bSMike Smith 
18261ac4b82bSMike Smith     mc->mc_complete = NULL;
18271ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
18281ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
18291ac4b82bSMike Smith 	return(error);
18301ac4b82bSMike Smith 
18311ac4b82bSMike Smith     count = 0;
18321ac4b82bSMike Smith     do {
18331ac4b82bSMike Smith 	/* poll for completion */
18340fca6f8bSJohn Baldwin 	mlx_done(mc->mc_sc, 1);
1835da8bb3a3SMike Smith 
1836da8bb3a3SMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
18371ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
18384b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
18391ac4b82bSMike Smith 	return(0);
18401ac4b82bSMike Smith     }
1841421f2f7dSMike Smith     device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
18421ac4b82bSMike Smith     return(EIO);
18431ac4b82bSMike Smith }
18441ac4b82bSMike Smith 
18451b4404f9SScott Long void
18461b4404f9SScott Long mlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
18471b4404f9SScott Long {
18481b4404f9SScott Long     struct mlx_command	*mc;
18491b4404f9SScott Long     struct mlxd_softc	*mlxd;
18501b4404f9SScott Long     struct mlx_softc	*sc;
1851b04e4c12SJohn Baldwin     struct bio		*bp;
18521b4404f9SScott Long     int			blkcount;
18531b4404f9SScott Long     int			driveno;
18541b4404f9SScott Long     int			cmd;
18551b4404f9SScott Long 
18561b4404f9SScott Long     mc = (struct mlx_command *)arg;
18571b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
18581b4404f9SScott Long 
18591b4404f9SScott Long     sc = mc->mc_sc;
18601b4404f9SScott Long     bp = mc->mc_private;
18611b4404f9SScott Long 
1862b04e4c12SJohn Baldwin     if (bp->bio_cmd == BIO_READ) {
18631b4404f9SScott Long 	mc->mc_flags |= MLX_CMD_DATAIN;
18641b4404f9SScott Long 	cmd = MLX_CMD_READSG;
18651b4404f9SScott Long     } else {
18661b4404f9SScott Long 	mc->mc_flags |= MLX_CMD_DATAOUT;
18671b4404f9SScott Long 	cmd = MLX_CMD_WRITESG;
18681b4404f9SScott Long     }
18691b4404f9SScott Long 
18701b4404f9SScott Long     /* build a suitable I/O command (assumes 512-byte rounded transfers) */
1871b04e4c12SJohn Baldwin     mlxd = bp->bio_disk->d_drv1;
18721b4404f9SScott Long     driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
1873057b4402SPedro F. Giffuni     blkcount = howmany(bp->bio_bcount, MLX_BLKSIZE);
18741b4404f9SScott Long 
1875b04e4c12SJohn Baldwin     if ((bp->bio_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
18761b4404f9SScott Long 	device_printf(sc->mlx_dev,
18771b4404f9SScott Long 		      "I/O beyond end of unit (%lld,%d > %lu)\n",
1878b04e4c12SJohn Baldwin 		      (long long)bp->bio_pblkno, blkcount,
18791b4404f9SScott Long 		      (u_long)sc->mlx_sysdrive[driveno].ms_size);
18801b4404f9SScott Long 
18811b4404f9SScott Long     /*
18821b4404f9SScott Long      * Build the I/O command.  Note that the SG list type bits are set to zero,
18831b4404f9SScott Long      * denoting the format of SG list that we are using.
18841b4404f9SScott Long      */
18851b4404f9SScott Long     if (sc->mlx_iftype == MLX_IFTYPE_2) {
18861b4404f9SScott Long 	mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD :
18871b4404f9SScott Long 						      MLX_CMD_READSG_OLD,
18881b4404f9SScott Long 		       blkcount & 0xff, 	/* xfer length low byte */
1889b04e4c12SJohn Baldwin 		       bp->bio_pblkno,		/* physical block number */
18901b4404f9SScott Long 		       driveno,			/* target drive number */
18911b4404f9SScott Long 		       mc->mc_sgphys,		/* location of SG list */
18921b4404f9SScott Long 		       mc->mc_nsgent & 0x3f);	/* size of SG list */
18931b4404f9SScott Long 	} else {
18941b4404f9SScott Long 	mlx_make_type5(mc, cmd,
18951b4404f9SScott Long 		       blkcount & 0xff, 	/* xfer length low byte */
18961b4404f9SScott Long 		       (driveno << 3) | ((blkcount >> 8) & 0x07),
18971b4404f9SScott Long 						/* target+length high 3 bits */
1898b04e4c12SJohn Baldwin 		       bp->bio_pblkno,		/* physical block number */
18991b4404f9SScott Long 		       mc->mc_sgphys,		/* location of SG list */
19001b4404f9SScott Long 		       mc->mc_nsgent & 0x3f);	/* size of SG list */
19011b4404f9SScott Long     }
19021b4404f9SScott Long 
19031b4404f9SScott Long     /* try to give command to controller */
19041b4404f9SScott Long     if (mlx_start(mc) != 0) {
19051b4404f9SScott Long 	/* fail the command */
19061b4404f9SScott Long 	mc->mc_status = MLX_STATUS_WEDGED;
19071b4404f9SScott Long 	mlx_completeio(mc);
19081b4404f9SScott Long     }
19090fca6f8bSJohn Baldwin 
19100fca6f8bSJohn Baldwin     sc->mlx_state &= ~MLX_STATE_QFROZEN;
19111b4404f9SScott Long }
19121b4404f9SScott Long 
19131ac4b82bSMike Smith /********************************************************************************
19141ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
19151ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
19161ac4b82bSMike Smith  */
19171ac4b82bSMike Smith static void
19181ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
19191ac4b82bSMike Smith {
19201ac4b82bSMike Smith     struct mlx_command	*mc;
1921b04e4c12SJohn Baldwin     struct bio		*bp;
19221b4404f9SScott Long     int			error;
19231ac4b82bSMike Smith 
19240fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
19255792b7feSMike Smith 
19261ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
19271ac4b82bSMike Smith     for (;;) {
19280fca6f8bSJohn Baldwin 	if (sc->mlx_state & MLX_STATE_QFROZEN)
19290fca6f8bSJohn Baldwin 	    break;
19301ac4b82bSMike Smith 
19311ac4b82bSMike Smith 	/* see if there's work to be done */
1932b04e4c12SJohn Baldwin 	if ((bp = bioq_first(&sc->mlx_bioq)) == NULL)
19331ac4b82bSMike Smith 	    break;
19341ac4b82bSMike Smith 	/* get a command */
19351ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
19361ac4b82bSMike Smith 	    break;
19371ac4b82bSMike Smith 	/* get a slot for the command */
19381ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
19391ac4b82bSMike Smith 	    mlx_releasecmd(mc);
19401ac4b82bSMike Smith 	    break;
19411ac4b82bSMike Smith 	}
19421ac4b82bSMike Smith 	/* get the buf containing our work */
1943b04e4c12SJohn Baldwin 	bioq_remove(&sc->mlx_bioq, bp);
19441ac4b82bSMike Smith 	sc->mlx_waitbufs--;
19451ac4b82bSMike Smith 
19461ac4b82bSMike Smith 	/* connect the buf to the command */
19471ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
19481ac4b82bSMike Smith 	mc->mc_private = bp;
1949b04e4c12SJohn Baldwin 	mc->mc_data = bp->bio_data;
1950b04e4c12SJohn Baldwin 	mc->mc_length = bp->bio_bcount;
19511ac4b82bSMike Smith 
19521ac4b82bSMike Smith 	/* map the command so the controller can work with it */
19531b4404f9SScott Long 	error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
19541b4404f9SScott Long 				mc->mc_length, mlx_startio_cb, mc, 0);
19551b4404f9SScott Long 	if (error == EINPROGRESS) {
19560fca6f8bSJohn Baldwin 	    sc->mlx_state |= MLX_STATE_QFROZEN;
19571b4404f9SScott Long 	    break;
1958da8bb3a3SMike Smith 	}
19591ac4b82bSMike Smith     }
19601ac4b82bSMike Smith }
19611ac4b82bSMike Smith 
19621ac4b82bSMike Smith /********************************************************************************
19631ac4b82bSMike Smith  * Handle completion of an I/O command.
19641ac4b82bSMike Smith  */
19651ac4b82bSMike Smith static void
19661ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
19671ac4b82bSMike Smith {
19681ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
1969b04e4c12SJohn Baldwin     struct bio		*bp = mc->mc_private;
1970b04e4c12SJohn Baldwin     struct mlxd_softc	*mlxd = bp->bio_disk->d_drv1;
19711ac4b82bSMike Smith 
19720fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
19731ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
1974b04e4c12SJohn Baldwin 	bp->bio_error = EIO;
1975b04e4c12SJohn Baldwin 	bp->bio_flags |= BIO_ERROR;
19761ac4b82bSMike Smith 
19771ac4b82bSMike Smith 	switch(mc->mc_status) {
19781ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
19791ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1980f6b84b08SMike Smith 	    /* should signal this with a return code */
19811ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
19821ac4b82bSMike Smith 	    break;
19831ac4b82bSMike Smith 
19841ac4b82bSMike Smith 	default:				/* other I/O error */
19851ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
19861ac4b82bSMike Smith #if 0
1987cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
1988b04e4c12SJohn Baldwin 			  bp->bio_bcount, bp->bio_bcount / MLX_BLKSIZE, bp->bio_pblkno);
19891ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
19901ac4b82bSMike Smith #endif
19911ac4b82bSMike Smith 	    break;
19921ac4b82bSMike Smith 	}
19931ac4b82bSMike Smith     }
19941ac4b82bSMike Smith     mlx_releasecmd(mc);
19951ac4b82bSMike Smith     mlxd_intr(bp);
19961ac4b82bSMike Smith }
19971ac4b82bSMike Smith 
19981b4404f9SScott Long void
19991b4404f9SScott Long mlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
20001b4404f9SScott Long {
20011b4404f9SScott Long     struct mlx_usercommand *mu;
20021b4404f9SScott Long     struct mlx_command *mc;
20031b4404f9SScott Long     struct mlx_dcdb	*dcdb;
20041b4404f9SScott Long 
20051b4404f9SScott Long     mc = (struct mlx_command *)arg;
20061b4404f9SScott Long     if (error)
20071b4404f9SScott Long 	return;
20081b4404f9SScott Long 
20091b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
20101b4404f9SScott Long 
20111b4404f9SScott Long     mu = (struct mlx_usercommand *)mc->mc_private;
20121b4404f9SScott Long     dcdb = NULL;
20131b4404f9SScott Long 
20141b4404f9SScott Long     /*
20151b4404f9SScott Long      * If this is a passthrough SCSI command, the DCDB is packed at the
20161b4404f9SScott Long      * beginning of the data area.  Fix up the DCDB to point to the correct
20171b4404f9SScott Long      * physical address and override any bufptr supplied by the caller since
20181b4404f9SScott Long      * we know what it's meant to be.
20191b4404f9SScott Long      */
20201b4404f9SScott Long     if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
20211b4404f9SScott Long 	dcdb = (struct mlx_dcdb *)mc->mc_data;
20221b4404f9SScott Long 	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
20231b4404f9SScott Long 	mu->mu_bufptr = 8;
20241b4404f9SScott Long     }
20251b4404f9SScott Long 
20261b4404f9SScott Long     /*
20271b4404f9SScott Long      * If there's a data buffer, fix up the command's buffer pointer.
20281b4404f9SScott Long      */
20291b4404f9SScott Long     if (mu->mu_datasize > 0) {
20301b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
20311b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
20321b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
20331b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
20341b4404f9SScott Long     }
20351b4404f9SScott Long     debug(0, "command fixup");
20361b4404f9SScott Long 
20371b4404f9SScott Long     /* submit the command and wait */
20381b4404f9SScott Long     if (mlx_wait_command(mc) != 0)
20391b4404f9SScott Long 	return;
20401b4404f9SScott Long 
20411b4404f9SScott Long }
20421b4404f9SScott Long 
20431ac4b82bSMike Smith /********************************************************************************
20441ac4b82bSMike Smith  * Take a command from user-space and try to run it.
2045da8bb3a3SMike Smith  *
2046da8bb3a3SMike Smith  * XXX Note that this can't perform very much in the way of error checking, and
2047da8bb3a3SMike Smith  *     as such, applications _must_ be considered trustworthy.
2048da8bb3a3SMike Smith  * XXX Commands using S/G for data are not supported.
20491ac4b82bSMike Smith  */
20501ac4b82bSMike Smith static int
20511ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
20521ac4b82bSMike Smith {
20531ac4b82bSMike Smith     struct mlx_command	*mc;
20541ac4b82bSMike Smith     void		*kbuf;
20551ac4b82bSMike Smith     int			error;
20561ac4b82bSMike Smith 
2057da8bb3a3SMike Smith     debug_called(0);
2058da8bb3a3SMike Smith 
20591ac4b82bSMike Smith     kbuf = NULL;
20601ac4b82bSMike Smith     mc = NULL;
20611ac4b82bSMike Smith     error = ENOMEM;
2062da8bb3a3SMike Smith 
2063da8bb3a3SMike Smith     /* get ourselves a command and copy in from user space */
20640fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
20650fca6f8bSJohn Baldwin     if ((mc = mlx_alloccmd(sc)) == NULL) {
20660fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
2067001ea8fbSSam Leffler 	return(error);
20680fca6f8bSJohn Baldwin     }
20691ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
2070da8bb3a3SMike Smith     debug(0, "got command buffer");
2071da8bb3a3SMike Smith 
20721b4404f9SScott Long     /*
20731b4404f9SScott Long      * if we need a buffer for data transfer, allocate one and copy in its
20741b4404f9SScott Long      * initial contents
20751b4404f9SScott Long      */
2076da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
207752c9ce25SScott Long 	if (mu->mu_datasize > MLX_MAXPHYS) {
2078aa083c3dSSam Leffler 	    error = EINVAL;
2079aa083c3dSSam Leffler 	    goto out;
2080aa083c3dSSam Leffler 	}
20810fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
2082a163d034SWarner Losh 	if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
20830fca6f8bSJohn Baldwin 	    (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) {
20840fca6f8bSJohn Baldwin 	    MLX_IO_LOCK(sc);
20851ac4b82bSMike Smith 	    goto out;
20860fca6f8bSJohn Baldwin 	}
20870fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
2088da8bb3a3SMike Smith 	debug(0, "got kernel buffer");
2089da8bb3a3SMike Smith     }
20901ac4b82bSMike Smith 
20911ac4b82bSMike Smith     /* get a command slot */
20921ac4b82bSMike Smith     if (mlx_getslot(mc))
20931ac4b82bSMike Smith 	goto out;
2094da8bb3a3SMike Smith     debug(0, "got a slot");
20951ac4b82bSMike Smith 
2096da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
2097da8bb3a3SMike Smith 
2098da8bb3a3SMike Smith 	/* range check the pointer to physical buffer address */
20991b4404f9SScott Long 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) -
21001b4404f9SScott Long 						     sizeof(u_int32_t)))) {
2101da8bb3a3SMike Smith 	    error = EINVAL;
2102da8bb3a3SMike Smith 	    goto out;
2103da8bb3a3SMike Smith 	}
2104da8bb3a3SMike Smith     }
2105da8bb3a3SMike Smith 
21061b4404f9SScott Long     /* map the command so the controller can see it */
21071b4404f9SScott Long     mc->mc_data = kbuf;
21081b4404f9SScott Long     mc->mc_length = mu->mu_datasize;
21091b4404f9SScott Long     mc->mc_private = mu;
21101b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
21111b4404f9SScott Long 			    mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT);
21120fca6f8bSJohn Baldwin     if (error)
21130fca6f8bSJohn Baldwin 	goto out;
21141ac4b82bSMike Smith 
21151ac4b82bSMike Smith     /* copy out status and data */
21161ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
21170fca6f8bSJohn Baldwin     if (mu->mu_datasize > 0) {
21180fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
21190fca6f8bSJohn Baldwin 	error = copyout(kbuf, mu->mu_buf, mu->mu_datasize);
21200fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
21210fca6f8bSJohn Baldwin     }
21221ac4b82bSMike Smith 
21231ac4b82bSMike Smith  out:
21241ac4b82bSMike Smith     mlx_releasecmd(mc);
21250fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
21261ac4b82bSMike Smith     if (kbuf != NULL)
21271ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
21281ac4b82bSMike Smith     return(error);
21291ac4b82bSMike Smith }
21301ac4b82bSMike Smith 
21311ac4b82bSMike Smith /********************************************************************************
21321ac4b82bSMike Smith  ********************************************************************************
21331ac4b82bSMike Smith                                                         Command I/O to Controller
21341ac4b82bSMike Smith  ********************************************************************************
21351ac4b82bSMike Smith  ********************************************************************************/
21361ac4b82bSMike Smith 
21371ac4b82bSMike Smith /********************************************************************************
21381ac4b82bSMike Smith  * Find a free command slot for (mc).
21391ac4b82bSMike Smith  *
21401ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
21411ac4b82bSMike Smith  * 4 slots free for priority commands.
21421ac4b82bSMike Smith  */
21431ac4b82bSMike Smith static int
21441ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
21451ac4b82bSMike Smith {
21461ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21470fca6f8bSJohn Baldwin     int			slot, limit;
21481ac4b82bSMike Smith 
2149da8bb3a3SMike Smith     debug_called(1);
21501ac4b82bSMike Smith 
21510fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
21520fca6f8bSJohn Baldwin 
2153baff09dbSMike Smith     /*
2154baff09dbSMike Smith      * Enforce slot-usage limit, if we have the required information.
2155baff09dbSMike Smith      */
2156baff09dbSMike Smith     if (sc->mlx_enq2 != NULL) {
2157baff09dbSMike Smith 	limit = sc->mlx_enq2->me_max_commands;
2158baff09dbSMike Smith     } else {
2159baff09dbSMike Smith 	limit = 2;
2160baff09dbSMike Smith     }
2161baff09dbSMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4))
21621ac4b82bSMike Smith 	return(EBUSY);
21631ac4b82bSMike Smith 
21641ac4b82bSMike Smith     /*
21651ac4b82bSMike Smith      * Allocate an outstanding command slot
21661ac4b82bSMike Smith      *
21671ac4b82bSMike Smith      * XXX linear search is slow
21681ac4b82bSMike Smith      */
2169baff09dbSMike Smith     for (slot = 0; slot < limit; slot++) {
2170da8bb3a3SMike Smith 	debug(2, "try slot %d", slot);
21711ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
21721ac4b82bSMike Smith 	    break;
21731ac4b82bSMike Smith     }
2174baff09dbSMike Smith     if (slot < limit) {
21751ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
21761ac4b82bSMike Smith 	sc->mlx_busycmds++;
21771ac4b82bSMike Smith     }
21781ac4b82bSMike Smith 
21791ac4b82bSMike Smith     /* out of slots? */
2180baff09dbSMike Smith     if (slot >= limit)
21811ac4b82bSMike Smith 	return(EBUSY);
21821ac4b82bSMike Smith 
2183da8bb3a3SMike Smith     debug(2, "got slot %d", slot);
21841ac4b82bSMike Smith     mc->mc_slot = slot;
21851ac4b82bSMike Smith     return(0);
21861ac4b82bSMike Smith }
21871ac4b82bSMike Smith 
21881ac4b82bSMike Smith /********************************************************************************
21891ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
21901ac4b82bSMike Smith  */
21911ac4b82bSMike Smith static void
21921b4404f9SScott Long mlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments,
21931b4404f9SScott Long 		 int error)
21941ac4b82bSMike Smith {
21951ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21961ac4b82bSMike Smith     struct mlx_sgentry	*sg;
21971ac4b82bSMike Smith     int			i;
21981ac4b82bSMike Smith 
2199da8bb3a3SMike Smith     debug_called(1);
22001ac4b82bSMike Smith 
2201baff09dbSMike Smith     /* XXX should be unnecessary */
2202baff09dbSMike Smith     if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
22031b4404f9SScott Long 	panic("MLX: too many s/g segments (%d, max %d)", nsegments,
22041b4404f9SScott Long 	      sc->mlx_enq2->me_max_sg);
2205baff09dbSMike Smith 
22061ac4b82bSMike Smith     /* get base address of s/g table */
2207baff09dbSMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
22081ac4b82bSMike Smith 
22091ac4b82bSMike Smith     /* save s/g table information in command */
22101ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
22111b4404f9SScott Long     mc->mc_sgphys = sc->mlx_sgbusaddr +
22121b4404f9SScott Long 		   (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
22131ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
22141ac4b82bSMike Smith 
22151ac4b82bSMike Smith     /* populate s/g table */
22161ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
22171ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
22181ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
22191ac4b82bSMike Smith     }
22201ac4b82bSMike Smith 
22211b4404f9SScott Long     /* Make sure the buffers are visible on the bus. */
22221ac4b82bSMike Smith     if (mc->mc_flags & MLX_CMD_DATAIN)
22231b4404f9SScott Long 	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
22241b4404f9SScott Long 			BUS_DMASYNC_PREREAD);
22251ac4b82bSMike Smith     if (mc->mc_flags & MLX_CMD_DATAOUT)
22261b4404f9SScott Long 	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
22271b4404f9SScott Long 			BUS_DMASYNC_PREWRITE);
22281ac4b82bSMike Smith }
22291ac4b82bSMike Smith 
22301ac4b82bSMike Smith static void
22311ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
22321ac4b82bSMike Smith {
22331ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
22341ac4b82bSMike Smith 
2235da8bb3a3SMike Smith     debug_called(1);
22361ac4b82bSMike Smith 
22371ac4b82bSMike Smith     /* if the command involved data at all */
22381ac4b82bSMike Smith     if (mc->mc_data != NULL) {
22391ac4b82bSMike Smith 
22401ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
22411ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
22421ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
22431ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
22441ac4b82bSMike Smith 
22451ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
22461ac4b82bSMike Smith     }
22471ac4b82bSMike Smith }
22481ac4b82bSMike Smith 
22491ac4b82bSMike Smith /********************************************************************************
22505792b7feSMike Smith  * Try to deliver (mc) to the controller.
22511ac4b82bSMike Smith  *
22521ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
22531ac4b82bSMike Smith  */
22541ac4b82bSMike Smith static int
22551ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
22561ac4b82bSMike Smith {
22571ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
22580fca6f8bSJohn Baldwin     int			i;
22591ac4b82bSMike Smith 
2260da8bb3a3SMike Smith     debug_called(1);
22611ac4b82bSMike Smith 
22621ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
22631ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
22641ac4b82bSMike Smith 
22654b006d7bSMike Smith     /* mark the command as currently being processed */
22661ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
22671ac4b82bSMike Smith 
22685792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
22695792b7feSMike Smith     mc->mc_timeout = time_second + 60;
22701ac4b82bSMike Smith 
22711ac4b82bSMike Smith     /* spin waiting for the mailbox */
22720fca6f8bSJohn Baldwin     for (i = 100000; i > 0; i--) {
22734b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
22744b006d7bSMike Smith 	    /* move command to work queue */
22754b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
22761ac4b82bSMike Smith 	    return (0);
22770fca6f8bSJohn Baldwin 	} else if (i > 1)
22780fca6f8bSJohn Baldwin 	    mlx_done(sc, 0);
22790fca6f8bSJohn Baldwin     }
22801ac4b82bSMike Smith 
22811ac4b82bSMike Smith     /*
22821ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
22831ac4b82bSMike Smith      * that the command was given and return it with a bad status.
22841ac4b82bSMike Smith      */
22851ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
22861ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
22871ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
22885792b7feSMike Smith     mlx_complete(sc);
22891ac4b82bSMike Smith     return(EIO);
22901ac4b82bSMike Smith }
22911ac4b82bSMike Smith 
22921ac4b82bSMike Smith /********************************************************************************
22935792b7feSMike Smith  * Poll the controller (sc) for completed commands.
22945792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
22955792b7feSMike Smith  * new commands may be posted.
22961ac4b82bSMike Smith  *
22975792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
22981ac4b82bSMike Smith  */
22991ac4b82bSMike Smith static int
23000fca6f8bSJohn Baldwin mlx_done(struct mlx_softc *sc, int startio)
23011ac4b82bSMike Smith {
23021ac4b82bSMike Smith     struct mlx_command	*mc;
23030fca6f8bSJohn Baldwin     int			result;
23041ac4b82bSMike Smith     u_int8_t		slot;
23051ac4b82bSMike Smith     u_int16_t		status;
23061ac4b82bSMike Smith 
2307da8bb3a3SMike Smith     debug_called(2);
23080fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
23091ac4b82bSMike Smith 
23105792b7feSMike Smith     result = 0;
23111ac4b82bSMike Smith 
23125792b7feSMike Smith     /* loop collecting completed commands */
23135792b7feSMike Smith     for (;;) {
23145792b7feSMike Smith 	/* poll for a completed command's identifier and status */
23151ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
23165792b7feSMike Smith 	    result = 1;
23171ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
23181ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
23191ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
23201ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
23211ac4b82bSMike Smith 
23221ac4b82bSMike Smith 		    /* free slot for reuse */
23231ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
23241ac4b82bSMike Smith 		    sc->mlx_busycmds--;
23251ac4b82bSMike Smith 		} else {
23261ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
23271ac4b82bSMike Smith 		}
23281ac4b82bSMike Smith 	    } else {
23291ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
23301ac4b82bSMike Smith 	    }
23315792b7feSMike Smith 	} else {
23325792b7feSMike Smith 	    break;
23331ac4b82bSMike Smith 	}
23345792b7feSMike Smith     }
23351ac4b82bSMike Smith 
23365792b7feSMike Smith     /* if we've completed any commands, try posting some more */
23370fca6f8bSJohn Baldwin     if (result && startio)
23385792b7feSMike Smith 	mlx_startio(sc);
23395792b7feSMike Smith 
23405792b7feSMike Smith     /* handle completion and timeouts */
23415792b7feSMike Smith     mlx_complete(sc);
23425792b7feSMike Smith 
23435792b7feSMike Smith     return(result);
23441ac4b82bSMike Smith }
23451ac4b82bSMike Smith 
23461ac4b82bSMike Smith /********************************************************************************
23475792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
23481ac4b82bSMike Smith  */
23491ac4b82bSMike Smith static void
23501ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
23511ac4b82bSMike Smith {
23521ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
23531ac4b82bSMike Smith 
2354da8bb3a3SMike Smith     debug_called(2);
23550fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
23561ac4b82bSMike Smith 
23575792b7feSMike Smith     /* scan the list of busy/done commands */
23584b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
23591ac4b82bSMike Smith     while (mc != NULL) {
23601ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
23611ac4b82bSMike Smith 
23625792b7feSMike Smith 	/* Command has been completed in some fashion */
23634b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
23644b006d7bSMike Smith 
23655792b7feSMike Smith 	    /* unmap the command's data buffer */
23665792b7feSMike Smith 	    mlx_unmapcmd(mc);
23671ac4b82bSMike Smith 	    /*
23681ac4b82bSMike Smith 	     * Does the command have a completion handler?
23691ac4b82bSMike Smith 	     */
23701ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
23711ac4b82bSMike Smith 		/* remove from list and give to handler */
23724b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
23731ac4b82bSMike Smith 		mc->mc_complete(mc);
23741ac4b82bSMike Smith 
23751ac4b82bSMike Smith 		/*
23761ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
23771ac4b82bSMike Smith 		 */
23781ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
23791ac4b82bSMike Smith 
23801ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
23814b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
23821ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
23831ac4b82bSMike Smith 
23841ac4b82bSMike Smith 		/*
23851ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
23861ac4b82bSMike Smith 		 */
23871ac4b82bSMike Smith 	    } else {
23881ac4b82bSMike Smith 	    }
23894b006d7bSMike Smith 	}
23901ac4b82bSMike Smith 	mc = nc;
23911ac4b82bSMike Smith     }
23921ac4b82bSMike Smith }
23931ac4b82bSMike Smith 
23941ac4b82bSMike Smith /********************************************************************************
23951ac4b82bSMike Smith  ********************************************************************************
23961ac4b82bSMike Smith                                                         Command Buffer Management
23971ac4b82bSMike Smith  ********************************************************************************
23981ac4b82bSMike Smith  ********************************************************************************/
23991ac4b82bSMike Smith 
24001ac4b82bSMike Smith /********************************************************************************
24011ac4b82bSMike Smith  * Get a new command buffer.
24021ac4b82bSMike Smith  *
24031ac4b82bSMike Smith  * This may return NULL in low-memory cases.
24041ac4b82bSMike Smith  *
24051ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
24061ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
24071ac4b82bSMike Smith  *
24081ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
24091ac4b82bSMike Smith  *
24101ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
24111ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
24121ac4b82bSMike Smith  *     using a buffer.
24131ac4b82bSMike Smith  */
24141ac4b82bSMike Smith static struct mlx_command *
24151ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
24161ac4b82bSMike Smith {
24171ac4b82bSMike Smith     struct mlx_command	*mc;
24181ac4b82bSMike Smith     int			error;
24191ac4b82bSMike Smith 
2420da8bb3a3SMike Smith     debug_called(1);
24211ac4b82bSMike Smith 
24220fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
24231ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
24241ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
24251ac4b82bSMike Smith 
24261ac4b82bSMike Smith     /* allocate a new command buffer? */
24271ac4b82bSMike Smith     if (mc == NULL) {
2428ca89ee27SDavid Malone 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO);
24291ac4b82bSMike Smith 	if (mc != NULL) {
24301ac4b82bSMike Smith 	    mc->mc_sc = sc;
24311ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
24321ac4b82bSMike Smith 	    if (error) {
24331ac4b82bSMike Smith 		free(mc, M_DEVBUF);
24341ac4b82bSMike Smith 		return(NULL);
24351ac4b82bSMike Smith 	    }
24361ac4b82bSMike Smith 	}
24371ac4b82bSMike Smith     }
24381ac4b82bSMike Smith     return(mc);
24391ac4b82bSMike Smith }
24401ac4b82bSMike Smith 
24411ac4b82bSMike Smith /********************************************************************************
24421ac4b82bSMike Smith  * Release a command buffer for recycling.
24431ac4b82bSMike Smith  *
24441ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
24451ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
24461ac4b82bSMike Smith  */
24471ac4b82bSMike Smith static void
24481ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
24491ac4b82bSMike Smith {
24501ac4b82bSMike Smith 
2451da8bb3a3SMike Smith     debug_called(1);
24521ac4b82bSMike Smith 
24530fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(mc->mc_sc);
24541ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
24551ac4b82bSMike Smith }
24561ac4b82bSMike Smith 
24571ac4b82bSMike Smith /********************************************************************************
24581ac4b82bSMike Smith  * Permanently discard a command buffer.
24591ac4b82bSMike Smith  */
24601ac4b82bSMike Smith static void
24611ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
24621ac4b82bSMike Smith {
24631ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
24641ac4b82bSMike Smith 
2465da8bb3a3SMike Smith     debug_called(1);
24661ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
24671ac4b82bSMike Smith     free(mc, M_DEVBUF);
24681ac4b82bSMike Smith }
24691ac4b82bSMike Smith 
24701ac4b82bSMike Smith 
24711ac4b82bSMike Smith /********************************************************************************
24721ac4b82bSMike Smith  ********************************************************************************
24731ac4b82bSMike Smith                                                 Type 3 interface accessor methods
24741ac4b82bSMike Smith  ********************************************************************************
24751ac4b82bSMike Smith  ********************************************************************************/
24761ac4b82bSMike Smith 
24771ac4b82bSMike Smith /********************************************************************************
24781ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
24791ac4b82bSMike Smith  * (the controller is not ready to take a command).
24801ac4b82bSMike Smith  */
24811ac4b82bSMike Smith static int
24821ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
24831ac4b82bSMike Smith {
24841ac4b82bSMike Smith     int		i;
24851ac4b82bSMike Smith 
2486da8bb3a3SMike Smith     debug_called(2);
24870fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
24881ac4b82bSMike Smith 
24891ac4b82bSMike Smith     /* ready for our command? */
24901ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
24911ac4b82bSMike Smith 	/* copy mailbox data to window */
24921ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
24931ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
24941ac4b82bSMike Smith 
24951ac4b82bSMike Smith 	/* post command */
2496f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
24971ac4b82bSMike Smith 	return(1);
24981ac4b82bSMike Smith     }
24991ac4b82bSMike Smith     return(0);
25001ac4b82bSMike Smith }
25011ac4b82bSMike Smith 
25021ac4b82bSMike Smith /********************************************************************************
25031ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
25041ac4b82bSMike Smith  * and recover the slot number and status code.
25051ac4b82bSMike Smith  */
25061ac4b82bSMike Smith static int
25071ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
25081ac4b82bSMike Smith {
25091ac4b82bSMike Smith 
2510da8bb3a3SMike Smith     debug_called(2);
25110fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
25121ac4b82bSMike Smith 
25131ac4b82bSMike Smith     /* status available? */
25141ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
25151ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
25161ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
25171ac4b82bSMike Smith 
25181ac4b82bSMike Smith 	/* acknowledge completion */
2519f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2520f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
25211ac4b82bSMike Smith 	return(1);
25221ac4b82bSMike Smith     }
25231ac4b82bSMike Smith     return(0);
25241ac4b82bSMike Smith }
25251ac4b82bSMike Smith 
25261ac4b82bSMike Smith /********************************************************************************
25271ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
25281ac4b82bSMike Smith  */
25291ac4b82bSMike Smith static void
25301ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
25311ac4b82bSMike Smith {
2532da8bb3a3SMike Smith     debug_called(1);
25330fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
25341ac4b82bSMike Smith 
25351ac4b82bSMike Smith     switch(action) {
25361ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
25371ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
25381ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
25391ac4b82bSMike Smith 	break;
25401ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
25411ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
25421ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
25431ac4b82bSMike Smith 	break;
25441ac4b82bSMike Smith     }
25451ac4b82bSMike Smith }
25461ac4b82bSMike Smith 
2547da8bb3a3SMike Smith /********************************************************************************
2548da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2549da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2550da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2551da8bb3a3SMike Smith  */
2552da8bb3a3SMike Smith static int
25530fca6f8bSJohn Baldwin mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
25540fca6f8bSJohn Baldwin     int first)
2555da8bb3a3SMike Smith {
2556da8bb3a3SMike Smith     u_int8_t	fwerror;
2557da8bb3a3SMike Smith 
2558da8bb3a3SMike Smith     debug_called(2);
2559da8bb3a3SMike Smith 
2560da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
25610fca6f8bSJohn Baldwin     if (first) {
2562da8bb3a3SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
2563da8bb3a3SMike Smith 	DELAY(1000);
2564da8bb3a3SMike Smith     }
2565da8bb3a3SMike Smith 
2566da8bb3a3SMike Smith     /* init in progress? */
2567da8bb3a3SMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
2568da8bb3a3SMike Smith 	return(0);
2569da8bb3a3SMike Smith 
2570da8bb3a3SMike Smith     /* test error value */
2571da8bb3a3SMike Smith     fwerror = MLX_V3_GET_FWERROR(sc);
2572da8bb3a3SMike Smith     if (!(fwerror & MLX_V3_FWERROR_PEND))
2573da8bb3a3SMike Smith 	return(1);
2574da8bb3a3SMike Smith 
2575da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2576da8bb3a3SMike Smith     *error = fwerror & ~MLX_V3_FWERROR_PEND;
2577da8bb3a3SMike Smith     *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
2578da8bb3a3SMike Smith     *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
2579da8bb3a3SMike Smith 
2580da8bb3a3SMike Smith     /* acknowledge */
2581da8bb3a3SMike Smith     MLX_V3_PUT_FWERROR(sc, 0);
2582da8bb3a3SMike Smith 
2583da8bb3a3SMike Smith     return(2);
2584da8bb3a3SMike Smith }
25851ac4b82bSMike Smith 
25861ac4b82bSMike Smith /********************************************************************************
25871ac4b82bSMike Smith  ********************************************************************************
2588f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2589f6b84b08SMike Smith  ********************************************************************************
2590f6b84b08SMike Smith  ********************************************************************************/
2591f6b84b08SMike Smith 
2592f6b84b08SMike Smith /********************************************************************************
2593f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2594f6b84b08SMike Smith  * (the controller is not ready to take a command).
2595f6b84b08SMike Smith  */
2596f6b84b08SMike Smith static int
2597f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2598f6b84b08SMike Smith {
2599f6b84b08SMike Smith     int		i;
2600f6b84b08SMike Smith 
2601da8bb3a3SMike Smith     debug_called(2);
26020fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
2603f6b84b08SMike Smith 
2604f6b84b08SMike Smith     /* ready for our command? */
2605f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2606f6b84b08SMike Smith 	/* copy mailbox data to window */
2607f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2608f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2609f6b84b08SMike Smith 
2610da8bb3a3SMike Smith 	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
26110fca6f8bSJohn Baldwin 	bus_barrier(sc->mlx_mem, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
2612da8bb3a3SMike Smith 			  BUS_SPACE_BARRIER_WRITE);
2613da8bb3a3SMike Smith 
2614f6b84b08SMike Smith 	/* post command */
2615f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2616f6b84b08SMike Smith 	return(1);
2617f6b84b08SMike Smith     }
2618f6b84b08SMike Smith     return(0);
2619f6b84b08SMike Smith }
2620f6b84b08SMike Smith 
2621f6b84b08SMike Smith /********************************************************************************
2622f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2623f6b84b08SMike Smith  * and recover the slot number and status code.
2624f6b84b08SMike Smith  */
2625f6b84b08SMike Smith static int
2626f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2627f6b84b08SMike Smith {
2628f6b84b08SMike Smith 
2629da8bb3a3SMike Smith     debug_called(2);
26300fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
2631f6b84b08SMike Smith 
2632f6b84b08SMike Smith     /* status available? */
2633f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2634f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2635f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2636f6b84b08SMike Smith 
2637f6b84b08SMike Smith 	/* acknowledge completion */
2638f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2639f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2640f6b84b08SMike Smith 	return(1);
2641f6b84b08SMike Smith     }
2642f6b84b08SMike Smith     return(0);
2643f6b84b08SMike Smith }
2644f6b84b08SMike Smith 
2645f6b84b08SMike Smith /********************************************************************************
2646f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2647f6b84b08SMike Smith  */
2648f6b84b08SMike Smith static void
2649f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2650f6b84b08SMike Smith {
2651da8bb3a3SMike Smith     debug_called(1);
26520fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
2653f6b84b08SMike Smith 
2654f6b84b08SMike Smith     switch(action) {
2655f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2656f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2657f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2658f6b84b08SMike Smith 	break;
2659f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2660f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2661f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2662f6b84b08SMike Smith 	break;
2663f6b84b08SMike Smith     }
2664f6b84b08SMike Smith }
2665f6b84b08SMike Smith 
2666da8bb3a3SMike Smith /********************************************************************************
2667da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2668da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2669da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2670da8bb3a3SMike Smith  */
2671da8bb3a3SMike Smith static int
26720fca6f8bSJohn Baldwin mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
26730fca6f8bSJohn Baldwin     int first)
2674da8bb3a3SMike Smith {
2675da8bb3a3SMike Smith     u_int8_t	fwerror;
2676da8bb3a3SMike Smith 
2677da8bb3a3SMike Smith     debug_called(2);
2678da8bb3a3SMike Smith 
2679da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
26800fca6f8bSJohn Baldwin     if (first) {
2681da8bb3a3SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2682da8bb3a3SMike Smith 	DELAY(1000);
2683da8bb3a3SMike Smith     }
2684da8bb3a3SMike Smith 
2685da8bb3a3SMike Smith     /* init in progress? */
2686da8bb3a3SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
2687da8bb3a3SMike Smith 	return(0);
2688da8bb3a3SMike Smith 
2689da8bb3a3SMike Smith     /* test error value */
2690da8bb3a3SMike Smith     fwerror = MLX_V4_GET_FWERROR(sc);
2691da8bb3a3SMike Smith     if (!(fwerror & MLX_V4_FWERROR_PEND))
2692da8bb3a3SMike Smith 	return(1);
2693da8bb3a3SMike Smith 
2694da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2695da8bb3a3SMike Smith     *error = fwerror & ~MLX_V4_FWERROR_PEND;
2696da8bb3a3SMike Smith     *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
2697da8bb3a3SMike Smith     *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
2698da8bb3a3SMike Smith 
2699da8bb3a3SMike Smith     /* acknowledge */
2700da8bb3a3SMike Smith     MLX_V4_PUT_FWERROR(sc, 0);
2701da8bb3a3SMike Smith 
2702da8bb3a3SMike Smith     return(2);
2703da8bb3a3SMike Smith }
2704f6b84b08SMike Smith 
2705f6b84b08SMike Smith /********************************************************************************
2706f6b84b08SMike Smith  ********************************************************************************
27075792b7feSMike Smith                                                 Type 5 interface accessor methods
27085792b7feSMike Smith  ********************************************************************************
27095792b7feSMike Smith  ********************************************************************************/
27105792b7feSMike Smith 
27115792b7feSMike Smith /********************************************************************************
27125792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
27135792b7feSMike Smith  * (the controller is not ready to take a command).
27145792b7feSMike Smith  */
27155792b7feSMike Smith static int
27165792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
27175792b7feSMike Smith {
27185792b7feSMike Smith     int		i;
27195792b7feSMike Smith 
2720da8bb3a3SMike Smith     debug_called(2);
27210fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
27225792b7feSMike Smith 
27235792b7feSMike Smith     /* ready for our command? */
27245792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
27255792b7feSMike Smith 	/* copy mailbox data to window */
27265792b7feSMike Smith 	for (i = 0; i < 13; i++)
27275792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
27285792b7feSMike Smith 
27295792b7feSMike Smith 	/* post command */
27305792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
27315792b7feSMike Smith 	return(1);
27325792b7feSMike Smith     }
27335792b7feSMike Smith     return(0);
27345792b7feSMike Smith }
27355792b7feSMike Smith 
27365792b7feSMike Smith /********************************************************************************
27375792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
27385792b7feSMike Smith  * and recover the slot number and status code.
27395792b7feSMike Smith  */
27405792b7feSMike Smith static int
27415792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
27425792b7feSMike Smith {
27435792b7feSMike Smith 
2744da8bb3a3SMike Smith     debug_called(2);
27450fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
27465792b7feSMike Smith 
27475792b7feSMike Smith     /* status available? */
27485792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
27495792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
27505792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
27515792b7feSMike Smith 
27525792b7feSMike Smith 	/* acknowledge completion */
27535792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
27545792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
27555792b7feSMike Smith 	return(1);
27565792b7feSMike Smith     }
27575792b7feSMike Smith     return(0);
27585792b7feSMike Smith }
27595792b7feSMike Smith 
27605792b7feSMike Smith /********************************************************************************
27615792b7feSMike Smith  * Enable/disable interrupts as requested.
27625792b7feSMike Smith  */
27635792b7feSMike Smith static void
27645792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
27655792b7feSMike Smith {
2766da8bb3a3SMike Smith     debug_called(1);
27670fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
27685792b7feSMike Smith 
27695792b7feSMike Smith     switch(action) {
27705792b7feSMike Smith     case MLX_INTACTION_DISABLE:
2771da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
27725792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
27735792b7feSMike Smith 	break;
27745792b7feSMike Smith     case MLX_INTACTION_ENABLE:
2775da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
27765792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
27775792b7feSMike Smith 	break;
27785792b7feSMike Smith     }
27795792b7feSMike Smith }
27805792b7feSMike Smith 
2781da8bb3a3SMike Smith /********************************************************************************
2782da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2783da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2784da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2785da8bb3a3SMike Smith  */
2786da8bb3a3SMike Smith static int
27870fca6f8bSJohn Baldwin mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
27880fca6f8bSJohn Baldwin     int first)
2789da8bb3a3SMike Smith {
2790da8bb3a3SMike Smith     u_int8_t	fwerror;
2791da8bb3a3SMike Smith 
2792da8bb3a3SMike Smith     debug_called(2);
2793da8bb3a3SMike Smith 
2794da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
27950fca6f8bSJohn Baldwin     if (first) {
2796da8bb3a3SMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
2797da8bb3a3SMike Smith 	DELAY(1000);
2798da8bb3a3SMike Smith     }
2799da8bb3a3SMike Smith 
2800da8bb3a3SMike Smith     /* init in progress? */
2801da8bb3a3SMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
2802da8bb3a3SMike Smith 	return(0);
2803da8bb3a3SMike Smith 
2804da8bb3a3SMike Smith     /* test for error value */
2805da8bb3a3SMike Smith     fwerror = MLX_V5_GET_FWERROR(sc);
2806da8bb3a3SMike Smith     if (!(fwerror & MLX_V5_FWERROR_PEND))
2807da8bb3a3SMike Smith 	return(1);
2808da8bb3a3SMike Smith 
2809da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2810da8bb3a3SMike Smith     *error = fwerror & ~MLX_V5_FWERROR_PEND;
2811da8bb3a3SMike Smith     *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
2812da8bb3a3SMike Smith     *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
2813da8bb3a3SMike Smith 
2814da8bb3a3SMike Smith     /* acknowledge */
2815da8bb3a3SMike Smith     MLX_V5_PUT_FWERROR(sc, 0xff);
2816da8bb3a3SMike Smith 
2817da8bb3a3SMike Smith     return(2);
2818da8bb3a3SMike Smith }
28195792b7feSMike Smith 
28205792b7feSMike Smith /********************************************************************************
28215792b7feSMike Smith  ********************************************************************************
28221ac4b82bSMike Smith                                                                         Debugging
28231ac4b82bSMike Smith  ********************************************************************************
28241ac4b82bSMike Smith  ********************************************************************************/
28251ac4b82bSMike Smith 
28261ac4b82bSMike Smith /********************************************************************************
28271ac4b82bSMike Smith  * Return a status message describing (mc)
28281ac4b82bSMike Smith  */
28291ac4b82bSMike Smith static char *mlx_status_messages[] = {
28301ac4b82bSMike Smith     "normal completion",			/* 00 */
28311ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
28321ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
28331ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
28341ac4b82bSMike Smith     "bad data encountered",			/* 04 */
28351ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
28361ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
28371ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
28381ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
28391ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
28401ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
28411ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
28421ac4b82bSMike Smith     "channel is busy",				/* 12 */
28431ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
2844da8bb3a3SMike Smith     "rebuild successfully terminated",		/* 14 */
2845da8bb3a3SMike Smith     "unsupported command",			/* 15 */
2846da8bb3a3SMike Smith     "check condition received",			/* 16 */
2847da8bb3a3SMike Smith     "device is busy",				/* 17 */
2848da8bb3a3SMike Smith     "selection or command timeout",		/* 18 */
2849da8bb3a3SMike Smith     "command terminated abnormally",		/* 19 */
2850da8bb3a3SMike Smith     ""
28511ac4b82bSMike Smith };
28521ac4b82bSMike Smith 
28531ac4b82bSMike Smith static struct
28541ac4b82bSMike Smith {
28551ac4b82bSMike Smith     int		command;
28561ac4b82bSMike Smith     u_int16_t	status;
28571ac4b82bSMike Smith     int		msg;
28581ac4b82bSMike Smith } mlx_messages[] = {
2859da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0001,	 1},
2860da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0002,	 1},
2861da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0105,	 3},
2862da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x010c,	 4},
2863da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0001,	 1},
2864da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0002,	 1},
2865da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0105,	 3},
2866da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0001,	 1},
2867da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0002,	 1},
2868da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0105,	 3},
2869da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
2870da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
2871da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
28721ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
28731ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
28741ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
28751ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
28761ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
2877da8bb3a3SMike Smith     {MLX_CMD_REBUILDASYNC,	0x0107, 14},
28781ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
28791ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
28801ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
28811ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
28821ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
28831ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
28841ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
2885da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0002, 16},
2886da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0008, 17},
2887da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000e, 18},
2888da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000f, 19},
2889da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0105,  8},
2890da8bb3a3SMike Smith 
2891da8bb3a3SMike Smith     {0,				0x0104, 14},
28921ac4b82bSMike Smith     {-1, 0, 0}
28931ac4b82bSMike Smith };
28941ac4b82bSMike Smith 
28951ac4b82bSMike Smith static char *
28961ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
28971ac4b82bSMike Smith {
28981ac4b82bSMike Smith     static char	unkmsg[80];
28991ac4b82bSMike Smith     int		i;
29001ac4b82bSMike Smith 
29011ac4b82bSMike Smith     /* look up message in table */
29021ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
2903da8bb3a3SMike Smith 	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
2904466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
29051ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
29061ac4b82bSMike Smith 
29071ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
29081ac4b82bSMike Smith     return(unkmsg);
29091ac4b82bSMike Smith }
29101ac4b82bSMike Smith 
29111ac4b82bSMike Smith /*******************************************************************************
2912da8bb3a3SMike Smith  * Print a string describing the controller (sc)
29131ac4b82bSMike Smith  */
29145792b7feSMike Smith static struct
29155792b7feSMike Smith {
29165792b7feSMike Smith     int		hwid;
29175792b7feSMike Smith     char	*name;
29185792b7feSMike Smith } mlx_controller_names[] = {
29195792b7feSMike Smith     {0x01,	"960P/PD"},
29205792b7feSMike Smith     {0x02,	"960PL"},
29215792b7feSMike Smith     {0x10,	"960PG"},
29225792b7feSMike Smith     {0x11,	"960PJ"},
29239eee27f1SMike Smith     {0x12,	"960PR"},
29249eee27f1SMike Smith     {0x13,	"960PT"},
29259eee27f1SMike Smith     {0x14,	"960PTL0"},
29269eee27f1SMike Smith     {0x15,	"960PRL"},
29279eee27f1SMike Smith     {0x16,	"960PTL1"},
29289eee27f1SMike Smith     {0x20,	"1164PVX"},
29295792b7feSMike Smith     {-1, NULL}
29305792b7feSMike Smith };
29315792b7feSMike Smith 
29329eee27f1SMike Smith static void
29339eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
29341ac4b82bSMike Smith {
29351ac4b82bSMike Smith     static char		buf[80];
29365792b7feSMike Smith     char		*model;
29379eee27f1SMike Smith     int			i;
29381ac4b82bSMike Smith 
29395792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
29409eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
29415792b7feSMike Smith 	    model = mlx_controller_names[i].name;
29421ac4b82bSMike Smith 	    break;
29431ac4b82bSMike Smith 	}
29445792b7feSMike Smith     }
29455792b7feSMike Smith     if (model == NULL) {
29469eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
29475792b7feSMike Smith 	model = buf;
29485792b7feSMike Smith     }
2949da8bb3a3SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
29509eee27f1SMike Smith 		  model,
29519eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
29529eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
29539eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
29549eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
29559eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2956b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
29579eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
29589eee27f1SMike Smith 
29599eee27f1SMike Smith     if (bootverbose) {
29609eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
29619eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
29629eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
29639eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
29649eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
29659eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
29669eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
29679eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
29689eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
29699eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
29709eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
29719eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
29729eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
29739eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
29749eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
29759eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
29769eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
29779eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
29789eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
29799eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
29809eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
29819eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
29829eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
29839eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
29849eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
29859eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
29869eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
29879eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
29889eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
29899eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
29909eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
29919eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
29929eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
29939eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
29949eee27f1SMike Smith 
29959eee27f1SMike Smith     }
29961ac4b82bSMike Smith }
29971ac4b82bSMike Smith 
2998da8bb3a3SMike Smith /*******************************************************************************
2999da8bb3a3SMike Smith  * Emit a string describing the firmware handshake status code, and return a flag
3000da8bb3a3SMike Smith  * indicating whether the code represents a fatal error.
3001da8bb3a3SMike Smith  *
3002da8bb3a3SMike Smith  * Error code interpretations are from the Linux driver, and don't directly match
3003da8bb3a3SMike Smith  * the messages printed by Mylex's BIOS.  This may change if documentation on the
3004da8bb3a3SMike Smith  * codes is forthcoming.
3005da8bb3a3SMike Smith  */
3006da8bb3a3SMike Smith static int
3007da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
3008da8bb3a3SMike Smith {
3009da8bb3a3SMike Smith     switch(error) {
3010da8bb3a3SMike Smith     case 0x00:
3011da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
3012da8bb3a3SMike Smith 	break;
3013da8bb3a3SMike Smith     case 0x08:
3014da8bb3a3SMike Smith 	/* we could be neater about this and give some indication when we receive more of them */
3015da8bb3a3SMike Smith 	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
3016da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "spinning up drives...\n");
3017da8bb3a3SMike Smith 	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
3018da8bb3a3SMike Smith 	}
3019da8bb3a3SMike Smith 	break;
3020da8bb3a3SMike Smith     case 0x30:
3021da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "configuration checksum error\n");
3022da8bb3a3SMike Smith 	break;
3023da8bb3a3SMike Smith     case 0x60:
3024da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
3025da8bb3a3SMike Smith 	break;
3026da8bb3a3SMike Smith     case 0x70:
3027da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
3028da8bb3a3SMike Smith 	break;
3029da8bb3a3SMike Smith     case 0x90:
3030da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
3031da8bb3a3SMike Smith 	break;
3032da8bb3a3SMike Smith     case 0xa0:
3033da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
3034da8bb3a3SMike Smith 	break;
3035da8bb3a3SMike Smith     case 0xb0:
3036da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
3037da8bb3a3SMike Smith 	break;
3038da8bb3a3SMike Smith     case 0xd0:
3039da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "new controller configuration found\n");
3040da8bb3a3SMike Smith 	break;
3041da8bb3a3SMike Smith     case 0xf0:
3042da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
3043da8bb3a3SMike Smith 	return(1);
3044da8bb3a3SMike Smith     default:
3045da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
3046da8bb3a3SMike Smith 	break;
3047da8bb3a3SMike Smith     }
3048da8bb3a3SMike Smith     return(0);
3049da8bb3a3SMike Smith }
3050da8bb3a3SMike Smith 
30511ac4b82bSMike Smith /********************************************************************************
30521ac4b82bSMike Smith  ********************************************************************************
30531ac4b82bSMike Smith                                                                 Utility Functions
30541ac4b82bSMike Smith  ********************************************************************************
30551ac4b82bSMike Smith  ********************************************************************************/
30561ac4b82bSMike Smith 
30571ac4b82bSMike Smith /********************************************************************************
30581ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
30591ac4b82bSMike Smith  */
30601ac4b82bSMike Smith static struct mlx_sysdrive *
30611ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
30621ac4b82bSMike Smith {
30631ac4b82bSMike Smith     int		i;
30641ac4b82bSMike Smith 
30651ac4b82bSMike Smith     /* search system drives */
30660fca6f8bSJohn Baldwin     MLX_CONFIG_ASSERT_LOCKED(sc);
30671ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
30681ac4b82bSMike Smith 	/* is this one attached? */
30691ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
30701ac4b82bSMike Smith 	    /* is this the one? */
30711ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
30721ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
30731ac4b82bSMike Smith 	}
30741ac4b82bSMike Smith     }
30751ac4b82bSMike Smith     return(NULL);
30761ac4b82bSMike Smith }
3077