xref: /freebsd/sys/dev/mlx/mlx.c (revision 29e6fa3a7fc7488afa8f17c3f93bff4bcbcff738)
11ac4b82bSMike Smith /*-
21ac4b82bSMike Smith  * Copyright (c) 1999 Michael Smith
31ac4b82bSMike Smith  * All rights reserved.
41ac4b82bSMike Smith  *
51ac4b82bSMike Smith  * Redistribution and use in source and binary forms, with or without
61ac4b82bSMike Smith  * modification, are permitted provided that the following conditions
71ac4b82bSMike Smith  * are met:
81ac4b82bSMike Smith  * 1. Redistributions of source code must retain the above copyright
91ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer.
101ac4b82bSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
111ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer in the
121ac4b82bSMike Smith  *    documentation and/or other materials provided with the distribution.
131ac4b82bSMike Smith  *
141ac4b82bSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151ac4b82bSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161ac4b82bSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171ac4b82bSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181ac4b82bSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191ac4b82bSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201ac4b82bSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211ac4b82bSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221ac4b82bSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231ac4b82bSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241ac4b82bSMike Smith  * SUCH DAMAGE.
251ac4b82bSMike Smith  *
261ac4b82bSMike Smith  *	$FreeBSD$
271ac4b82bSMike Smith  */
281ac4b82bSMike Smith 
291ac4b82bSMike Smith /*
301ac4b82bSMike Smith  * Driver for the Mylex DAC960 family of RAID controllers.
311ac4b82bSMike Smith  */
321ac4b82bSMike Smith 
331ac4b82bSMike Smith #include <sys/param.h>
341ac4b82bSMike Smith #include <sys/systm.h>
351ac4b82bSMike Smith #include <sys/malloc.h>
361ac4b82bSMike Smith #include <sys/kernel.h>
371ac4b82bSMike Smith 
381ac4b82bSMike Smith #include <sys/bus.h>
391ac4b82bSMike Smith #include <sys/conf.h>
40da8bb3a3SMike Smith #include <sys/stat.h>
411ac4b82bSMike Smith 
421ac4b82bSMike Smith #include <machine/resource.h>
431ac4b82bSMike Smith #include <machine/bus.h>
441ac4b82bSMike Smith #include <machine/clock.h>
451ac4b82bSMike Smith #include <sys/rman.h>
461ac4b82bSMike Smith 
47891619a6SPoul-Henning Kamp #include <geom/geom_disk.h>
48891619a6SPoul-Henning Kamp 
4915fd5d22SMike Smith #include <dev/mlx/mlx_compat.h>
501ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
511ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
521ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
531ac4b82bSMike Smith 
541ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
55dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
56dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
577ac40f5fSPoul-Henning Kamp 	.d_open =	mlx_open,
587ac40f5fSPoul-Henning Kamp 	.d_close =	mlx_close,
597ac40f5fSPoul-Henning Kamp 	.d_ioctl =	mlx_ioctl,
607ac40f5fSPoul-Henning Kamp 	.d_name =	"mlx",
611ac4b82bSMike Smith };
621ac4b82bSMike Smith 
631ac4b82bSMike Smith devclass_t	mlx_devclass;
641ac4b82bSMike Smith 
651ac4b82bSMike Smith /*
661ac4b82bSMike Smith  * Per-interface accessor methods
671ac4b82bSMike Smith  */
681ac4b82bSMike Smith static int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
691ac4b82bSMike Smith static int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
701ac4b82bSMike Smith static void			mlx_v3_intaction(struct mlx_softc *sc, int action);
71da8bb3a3SMike Smith static int			mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
721ac4b82bSMike Smith 
73f6b84b08SMike Smith static int			mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
74f6b84b08SMike Smith static int			mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
75f6b84b08SMike Smith static void			mlx_v4_intaction(struct mlx_softc *sc, int action);
76da8bb3a3SMike Smith static int			mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
77f6b84b08SMike Smith 
785792b7feSMike Smith static int			mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
795792b7feSMike Smith static int			mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
805792b7feSMike Smith static void			mlx_v5_intaction(struct mlx_softc *sc, int action);
81da8bb3a3SMike Smith static int			mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
825792b7feSMike Smith 
831ac4b82bSMike Smith /*
841ac4b82bSMike Smith  * Status monitoring
851ac4b82bSMike Smith  */
861ac4b82bSMike Smith static void			mlx_periodic(void *data);
871ac4b82bSMike Smith static void			mlx_periodic_enquiry(struct mlx_command *mc);
881ac4b82bSMike Smith static void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
891ac4b82bSMike Smith static void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
901ac4b82bSMike Smith static void			mlx_periodic_rebuild(struct mlx_command *mc);
911ac4b82bSMike Smith 
921ac4b82bSMike Smith /*
931ac4b82bSMike Smith  * Channel Pause
941ac4b82bSMike Smith  */
951ac4b82bSMike Smith static void			mlx_pause_action(struct mlx_softc *sc);
961ac4b82bSMike Smith static void			mlx_pause_done(struct mlx_command *mc);
971ac4b82bSMike Smith 
981ac4b82bSMike Smith /*
991ac4b82bSMike Smith  * Command submission.
1001ac4b82bSMike Smith  */
1011ac4b82bSMike Smith static void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
1021ac4b82bSMike Smith 					     void (*complete)(struct mlx_command *mc));
1031ac4b82bSMike Smith static int			mlx_flush(struct mlx_softc *sc);
104421f2f7dSMike Smith static int			mlx_check(struct mlx_softc *sc, int drive);
1051ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1061ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1071ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1081b4404f9SScott Long void				mlx_startio_cb(void *arg,
1091b4404f9SScott Long 					       bus_dma_segment_t *segs,
1101b4404f9SScott Long 					       int nsegments, int error);
1111ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1121ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1131b4404f9SScott Long static int			mlx_user_command(struct mlx_softc *sc,
1141b4404f9SScott Long 						 struct mlx_usercommand *mu);
1151b4404f9SScott Long void				mlx_user_cb(void *arg, bus_dma_segment_t *segs,
1161b4404f9SScott Long 					    int nsegments, int error);
1171ac4b82bSMike Smith 
1181ac4b82bSMike Smith /*
1191ac4b82bSMike Smith  * Command buffer allocation.
1201ac4b82bSMike Smith  */
1211ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1221ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1231ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1241ac4b82bSMike Smith 
1251ac4b82bSMike Smith /*
1261ac4b82bSMike Smith  * Command management.
1271ac4b82bSMike Smith  */
1281ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1291b4404f9SScott Long static void			mlx_setup_dmamap(struct mlx_command *mc,
1301b4404f9SScott Long 						 bus_dma_segment_t *segs,
1311b4404f9SScott Long 						 int nsegments, int error);
1321ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1331ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1341ac4b82bSMike Smith static int			mlx_done(struct mlx_softc *sc);
1351ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1361ac4b82bSMike Smith 
1371ac4b82bSMike Smith /*
1381ac4b82bSMike Smith  * Debugging.
1391ac4b82bSMike Smith  */
1401ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1419eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
142da8bb3a3SMike Smith static int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
1431ac4b82bSMike Smith 
1441ac4b82bSMike Smith /*
1451ac4b82bSMike Smith  * Utility functions.
1461ac4b82bSMike Smith  */
1471ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1481ac4b82bSMike Smith 
1491ac4b82bSMike Smith /********************************************************************************
1501ac4b82bSMike Smith  ********************************************************************************
1511ac4b82bSMike Smith                                                                 Public Interfaces
1521ac4b82bSMike Smith  ********************************************************************************
1531ac4b82bSMike Smith  ********************************************************************************/
1541ac4b82bSMike Smith 
1551ac4b82bSMike Smith /********************************************************************************
1561ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1571ac4b82bSMike Smith  *
1581ac4b82bSMike Smith  * Should not be called if the controller is active.
1591ac4b82bSMike Smith  */
1601ac4b82bSMike Smith void
1611ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1621ac4b82bSMike Smith {
1631ac4b82bSMike Smith     struct mlx_command	*mc;
1641ac4b82bSMike Smith 
165da8bb3a3SMike Smith     debug_called(1);
1661ac4b82bSMike Smith 
1671ac4b82bSMike Smith     /* cancel status timeout */
1681ac4b82bSMike Smith     untimeout(mlx_periodic, sc, sc->mlx_timeout);
1691ac4b82bSMike Smith 
1701ac4b82bSMike Smith     /* throw away any command buffers */
1711ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1721ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1731ac4b82bSMike Smith 	mlx_freecmd(mc);
1741ac4b82bSMike Smith     }
1751ac4b82bSMike Smith 
1761ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1771ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1781ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1791ac4b82bSMike Smith 
1801ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
1811ac4b82bSMike Smith     if (sc->mlx_sgtable)
1821ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1831ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1841ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1851ac4b82bSMike Smith 
1861ac4b82bSMike Smith     /* disconnect the interrupt handler */
1871ac4b82bSMike Smith     if (sc->mlx_intr)
1881ac4b82bSMike Smith 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1891ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
1901ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
1911ac4b82bSMike Smith 
1921ac4b82bSMike Smith     /* destroy the parent DMA tag */
1931ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
1941ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
1951ac4b82bSMike Smith 
1961ac4b82bSMike Smith     /* release the register window mapping */
1971ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
1989b11c7baSMatthew N. Dodd 	bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem);
1999eee27f1SMike Smith 
2009eee27f1SMike Smith     /* free controller enquiry data */
2019eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
2029eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
203da8bb3a3SMike Smith 
204da8bb3a3SMike Smith     /* destroy control device */
20589c9c53dSPoul-Henning Kamp     if (sc->mlx_dev_t != (struct cdev *)NULL)
206da8bb3a3SMike Smith 	destroy_dev(sc->mlx_dev_t);
2071ac4b82bSMike Smith }
2081ac4b82bSMike Smith 
2091ac4b82bSMike Smith /********************************************************************************
2101ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2111ac4b82bSMike Smith  */
2121ac4b82bSMike Smith static void
2131ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2141ac4b82bSMike Smith {
2151ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2161ac4b82bSMike Smith 
217da8bb3a3SMike Smith     debug_called(1);
2181ac4b82bSMike Smith 
2191ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2201ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2211ac4b82bSMike Smith }
2221ac4b82bSMike Smith 
2231ac4b82bSMike Smith static int
2241ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2251ac4b82bSMike Smith {
2261ac4b82bSMike Smith     size_t	segsize;
227baff09dbSMike Smith     int		error, ncmd;
2281ac4b82bSMike Smith 
229da8bb3a3SMike Smith     debug_called(1);
2301ac4b82bSMike Smith 
2311ac4b82bSMike Smith     /* destroy any existing mappings */
2321ac4b82bSMike Smith     if (sc->mlx_sgtable)
2331ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2341ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2351ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2361ac4b82bSMike Smith 
2371ac4b82bSMike Smith     /*
2381ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
239baff09dbSMike Smith      * the s/g lists we will need.  If we're called early on, we don't know how
240baff09dbSMike Smith      * many commands we're going to be asked to support, so only allocate enough
241baff09dbSMike Smith      * for a couple.
2421ac4b82bSMike Smith      */
243baff09dbSMike Smith     if (sc->mlx_enq2 == NULL) {
244baff09dbSMike Smith 	ncmd = 2;
245baff09dbSMike Smith     } else {
246baff09dbSMike Smith 	ncmd = sc->mlx_enq2->me_max_commands;
247baff09dbSMike Smith     }
248baff09dbSMike Smith     segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd;
2491ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2501ac4b82bSMike Smith 			       1, 0, 			/* alignment,boundary */
2511ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2521ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2531ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2541ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2551ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2561ac4b82bSMike Smith 			       0,			/* flags */
257fc3e87b3SScott Long 			       NULL, NULL,		/* lockfunc, lockarg */
2581ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2591ac4b82bSMike Smith     if (error != 0) {
2601ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2611ac4b82bSMike Smith 	return(ENOMEM);
2621ac4b82bSMike Smith     }
2631ac4b82bSMike Smith 
2641ac4b82bSMike Smith     /*
2651ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2661ac4b82bSMike Smith      * controller-visible space.
2671ac4b82bSMike Smith      *
2681ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
269fc3e87b3SScott Long      * contiguous slab.  We may need to switch to a more complex arrangement
270fc3e87b3SScott Long      * where we allocate in smaller chunks and keep a lookup table from slot
271fc3e87b3SScott Long      * to bus address.
2721ac4b82bSMike Smith      */
273fc3e87b3SScott Long     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable,
274fc3e87b3SScott Long 			     BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2751ac4b82bSMike Smith     if (error) {
2761ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2771ac4b82bSMike Smith 	return(ENOMEM);
2781ac4b82bSMike Smith     }
2791b4404f9SScott Long     (void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable,
280fc3e87b3SScott Long 			  segsize, mlx_dma_map_sg, sc, 0);
2811ac4b82bSMike Smith     return(0);
2821ac4b82bSMike Smith }
2831ac4b82bSMike Smith 
2841ac4b82bSMike Smith /********************************************************************************
2851ac4b82bSMike Smith  * Initialise the controller and softc
2861ac4b82bSMike Smith  */
2871ac4b82bSMike Smith int
2881ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
2891ac4b82bSMike Smith {
290da8bb3a3SMike Smith     struct mlx_enquiry_old	*meo;
291da8bb3a3SMike Smith     int				rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
2921ac4b82bSMike Smith 
293da8bb3a3SMike Smith     debug_called(1);
2941ac4b82bSMike Smith 
2951ac4b82bSMike Smith     /*
2961ac4b82bSMike Smith      * Initialise per-controller queues.
2971ac4b82bSMike Smith      */
2984b006d7bSMike Smith     TAILQ_INIT(&sc->mlx_work);
2991ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
30015fd5d22SMike Smith     MLX_BIO_QINIT(sc->mlx_bioq);
3011ac4b82bSMike Smith 
3021ac4b82bSMike Smith     /*
3031ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
3041ac4b82bSMike Smith      */
3051ac4b82bSMike Smith     switch(sc->mlx_iftype) {
306da8bb3a3SMike Smith     case MLX_IFTYPE_2:
3071ac4b82bSMike Smith     case MLX_IFTYPE_3:
3081ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
3091ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
3101ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
311da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v3_fw_handshake;
3121ac4b82bSMike Smith 	break;
313f6b84b08SMike Smith     case MLX_IFTYPE_4:
314f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
315f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
316f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
317da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
318f6b84b08SMike Smith 	break;
3195792b7feSMike Smith     case MLX_IFTYPE_5:
3205792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3215792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3225792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
323da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
3245792b7feSMike Smith 	break;
3251ac4b82bSMike Smith     default:
3261ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3271ac4b82bSMike Smith     }
3281ac4b82bSMike Smith 
3291ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3301ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3311ac4b82bSMike Smith 
3321ac4b82bSMike Smith     /*
333da8bb3a3SMike Smith      * Wait for the controller to come ready, handshake with the firmware if required.
334da8bb3a3SMike Smith      * This is typically only necessary on platforms where the controller BIOS does not
335da8bb3a3SMike Smith      * run.
336da8bb3a3SMike Smith      */
337da8bb3a3SMike Smith     hsmsg = 0;
338da8bb3a3SMike Smith     DELAY(1000);
339da8bb3a3SMike Smith     while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) {
340da8bb3a3SMike Smith 	/* report first time around... */
341da8bb3a3SMike Smith 	if (hsmsg == 0) {
342da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
343da8bb3a3SMike Smith 	    hsmsg = 1;
344da8bb3a3SMike Smith 	}
345da8bb3a3SMike Smith 	/* did we get a real message? */
346da8bb3a3SMike Smith 	if (hscode == 2) {
347da8bb3a3SMike Smith 	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
348da8bb3a3SMike Smith 	    /* fatal initialisation error? */
349da8bb3a3SMike Smith 	    if (hscode != 0) {
350da8bb3a3SMike Smith 		return(ENXIO);
351da8bb3a3SMike Smith 	    }
352da8bb3a3SMike Smith 	}
353da8bb3a3SMike Smith     }
354da8bb3a3SMike Smith     if (hsmsg == 1)
355da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "initialisation complete.\n");
356da8bb3a3SMike Smith 
357da8bb3a3SMike Smith     /*
3581ac4b82bSMike Smith      * Allocate and connect our interrupt.
3591ac4b82bSMike Smith      */
3601ac4b82bSMike Smith     rid = 0;
3615f96beb9SNate Lawson     sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid,
3625f96beb9SNate Lawson         RF_SHAREABLE | RF_ACTIVE);
3631ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
364421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't allocate interrupt\n");
3651ac4b82bSMike Smith 	return(ENXIO);
3661ac4b82bSMike Smith     }
367ef544f63SPaolo Pisati     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY, NULL, mlx_intr, sc, &sc->mlx_intr);
3681ac4b82bSMike Smith     if (error) {
369421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't set up interrupt\n");
3701ac4b82bSMike Smith 	return(ENXIO);
3711ac4b82bSMike Smith     }
3721ac4b82bSMike Smith 
3731ac4b82bSMike Smith     /*
3741ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3751ac4b82bSMike Smith      */
3761ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
3771b4404f9SScott Long 			       1, 0, 			/* align, boundary */
3781ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
3791ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
3801ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
381baff09dbSMike Smith 			       MAXBSIZE, MLX_NSEG,	/* maxsize, nsegments */
3821ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
3831ac4b82bSMike Smith 			       0,			/* flags */
384f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
385f6b1c44dSScott Long 			       &Giant,			/* lockarg */
3861ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
3871ac4b82bSMike Smith     if (error != 0) {
3881ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
3891ac4b82bSMike Smith 	return(ENOMEM);
3901ac4b82bSMike Smith     }
3911ac4b82bSMike Smith 
3921ac4b82bSMike Smith     /*
3931b4404f9SScott Long      * Create some initial scatter/gather mappings so we can run the probe
3941b4404f9SScott Long      * commands.
3951ac4b82bSMike Smith      */
3961ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3971ac4b82bSMike Smith     if (error != 0) {
398421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
3991ac4b82bSMike Smith 	return(error);
4001ac4b82bSMike Smith     }
4011ac4b82bSMike Smith 
402baff09dbSMike Smith     /*
403baff09dbSMike Smith      * We don't (yet) know where the event log is up to.
404baff09dbSMike Smith      */
405baff09dbSMike Smith     sc->mlx_currevent = -1;
406baff09dbSMike Smith 
407baff09dbSMike Smith     /*
408baff09dbSMike Smith      * Obtain controller feature information
409baff09dbSMike Smith      */
4109eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
4111ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
4121ac4b82bSMike Smith 	return(ENXIO);
4131ac4b82bSMike Smith     }
4141ac4b82bSMike Smith 
4159eee27f1SMike Smith     /*
4161ac4b82bSMike Smith      * Do quirk/feature related things.
4171ac4b82bSMike Smith      */
4189eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
4191ac4b82bSMike Smith     switch(sc->mlx_iftype) {
420da8bb3a3SMike Smith     case MLX_IFTYPE_2:
421da8bb3a3SMike Smith 	/* These controllers don't report the firmware version in the ENQUIRY2 response */
422da8bb3a3SMike Smith 	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
423da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
424da8bb3a3SMike Smith 	    return(ENXIO);
425da8bb3a3SMike Smith 	}
426da8bb3a3SMike Smith 	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
427da8bb3a3SMike Smith 
428da8bb3a3SMike Smith 	/* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */
429da8bb3a3SMike Smith 	if (meo->me_fwminor < 42) {
430da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
431da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
432da8bb3a3SMike Smith 	}
433caa32ef5SColin Percival 	free(meo, M_DEVBUF);
434da8bb3a3SMike Smith 	break;
4351ac4b82bSMike Smith     case MLX_IFTYPE_3:
436f6b84b08SMike Smith 	/* XXX certify 3.52? */
4379eee27f1SMike Smith 	if (fwminor < 51) {
4384b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4394b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
4401ac4b82bSMike Smith 	}
4411ac4b82bSMike Smith 	break;
442f6b84b08SMike Smith     case MLX_IFTYPE_4:
443f6b84b08SMike Smith 	/* XXX certify firmware versions? */
4449eee27f1SMike Smith 	if (fwminor < 6) {
4454b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4464b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
447f6b84b08SMike Smith 	}
448f6b84b08SMike Smith 	break;
4495792b7feSMike Smith     case MLX_IFTYPE_5:
4509eee27f1SMike Smith 	if (fwminor < 7) {
4515792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4525792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
4535792b7feSMike Smith 	}
4545792b7feSMike Smith 	break;
4551ac4b82bSMike Smith     default:
4561ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4571ac4b82bSMike Smith     }
4581ac4b82bSMike Smith 
4591ac4b82bSMike Smith     /*
460baff09dbSMike Smith      * Create the final scatter/gather mappings now that we have characterised the controller.
4611ac4b82bSMike Smith      */
4621ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4631ac4b82bSMike Smith     if (error != 0) {
464baff09dbSMike Smith 	device_printf(sc->mlx_dev, "can't make final s/g list mapping\n");
4651ac4b82bSMike Smith 	return(error);
4661ac4b82bSMike Smith     }
4671ac4b82bSMike Smith 
4681ac4b82bSMike Smith     /*
469421f2f7dSMike Smith      * No user-requested background operation is in progress.
4701ac4b82bSMike Smith      */
471421f2f7dSMike Smith     sc->mlx_background = 0;
472421f2f7dSMike Smith     sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
4731ac4b82bSMike Smith 
4741ac4b82bSMike Smith     /*
475da8bb3a3SMike Smith      * Create the control device.
4761ac4b82bSMike Smith      */
477da8bb3a3SMike Smith     sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR,
478da8bb3a3SMike Smith 			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
4791ac4b82bSMike Smith 
4801ac4b82bSMike Smith     /*
4811ac4b82bSMike Smith      * Start the timeout routine.
4821ac4b82bSMike Smith      */
4831ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
4841ac4b82bSMike Smith 
485da8bb3a3SMike Smith     /* print a little information about the controller */
486da8bb3a3SMike Smith     mlx_describe_controller(sc);
487da8bb3a3SMike Smith 
4881ac4b82bSMike Smith     return(0);
4891ac4b82bSMike Smith }
4901ac4b82bSMike Smith 
4911ac4b82bSMike Smith /********************************************************************************
4921ac4b82bSMike Smith  * Locate disk resources and attach children to them.
4931ac4b82bSMike Smith  */
4941ac4b82bSMike Smith void
4951ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
4961ac4b82bSMike Smith {
4971ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
4981ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
4991ac4b82bSMike Smith     int				i, error;
5001ac4b82bSMike Smith 
501da8bb3a3SMike Smith     debug_called(1);
5021ac4b82bSMike Smith 
5031ac4b82bSMike Smith     /*
5041ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
5051ac4b82bSMike Smith      * don't currently have them.
5061ac4b82bSMike Smith      */
5071ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
5081ac4b82bSMike Smith     if (mes == NULL) {
5099eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
5101ac4b82bSMike Smith 	return;
5111ac4b82bSMike Smith     }
5121ac4b82bSMike Smith 
5131ac4b82bSMike Smith     /* iterate over drives returned */
5141ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
5151ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
5161ac4b82bSMike Smith 	 i++, dr++) {
5171ac4b82bSMike Smith 	/* are we already attached to this drive? */
5181ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
5191ac4b82bSMike Smith 	    /* pick up drive information */
5201ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
521f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
5221ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
5231ac4b82bSMike Smith 
5241ac4b82bSMike Smith 	    /* generate geometry information */
5251ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
5261ac4b82bSMike Smith 		dr->ms_heads = 128;
5271ac4b82bSMike Smith 		dr->ms_sectors = 32;
5281ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
5291ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
5301ac4b82bSMike Smith 		dr->ms_heads = 255;
5311ac4b82bSMike Smith 		dr->ms_sectors = 63;
5321ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
5331ac4b82bSMike Smith 	    }
534fe0d4089SMatthew N. Dodd 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
5351ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
5361ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
537fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
5381ac4b82bSMike Smith 	}
5391ac4b82bSMike Smith     }
5401ac4b82bSMike Smith     free(mes, M_DEVBUF);
5411ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
5421ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
5431ac4b82bSMike Smith 
5441ac4b82bSMike Smith     /* mark controller back up */
5451ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5461ac4b82bSMike Smith 
5471ac4b82bSMike Smith     /* enable interrupts */
5481ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
5491ac4b82bSMike Smith }
5501ac4b82bSMike Smith 
5511ac4b82bSMike Smith /********************************************************************************
5521ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
5531ac4b82bSMike Smith  */
5541ac4b82bSMike Smith int
5551ac4b82bSMike Smith mlx_detach(device_t dev)
5561ac4b82bSMike Smith {
5571ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5585792b7feSMike Smith     struct mlxd_softc	*mlxd;
5595792b7feSMike Smith     int			i, s, error;
5601ac4b82bSMike Smith 
561da8bb3a3SMike Smith     debug_called(1);
5621ac4b82bSMike Smith 
5635792b7feSMike Smith     error = EBUSY;
5645792b7feSMike Smith     s = splbio();
5651ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
5665792b7feSMike Smith 	goto out;
5671ac4b82bSMike Smith 
5685792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5695792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5705792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
5715792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
5725792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
5735792b7feSMike Smith 		goto out;
5745792b7feSMike Smith 	    }
5755792b7feSMike Smith 	}
5765792b7feSMike Smith     }
5771ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
5785792b7feSMike Smith 	goto out;
5791ac4b82bSMike Smith 
5801ac4b82bSMike Smith     mlx_free(sc);
5811ac4b82bSMike Smith 
5825792b7feSMike Smith     error = 0;
5835792b7feSMike Smith  out:
5845792b7feSMike Smith     splx(s);
5855792b7feSMike Smith     return(error);
5861ac4b82bSMike Smith }
5871ac4b82bSMike Smith 
5881ac4b82bSMike Smith /********************************************************************************
5891ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
5901ac4b82bSMike Smith  *
5911ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
5921ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
5931ac4b82bSMike Smith  * resume normal operation.)
5941ac4b82bSMike Smith  *
5958177437dSPoul-Henning Kamp  * Note that we can assume that the bioq on the controller is empty, as we won't
5961ac4b82bSMike Smith  * allow shutdown if any device is open.
5971ac4b82bSMike Smith  */
5981ac4b82bSMike Smith int
5991ac4b82bSMike Smith mlx_shutdown(device_t dev)
6001ac4b82bSMike Smith {
6011ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6021ac4b82bSMike Smith     int			i, s, error;
6031ac4b82bSMike Smith 
604da8bb3a3SMike Smith     debug_called(1);
6051ac4b82bSMike Smith 
6061ac4b82bSMike Smith     s = splbio();
6071ac4b82bSMike Smith     error = 0;
6081ac4b82bSMike Smith 
6091ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
6105792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6111ac4b82bSMike Smith 
6121ac4b82bSMike Smith     /* flush controller */
6131ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6141ac4b82bSMike Smith     if (mlx_flush(sc)) {
6151ac4b82bSMike Smith 	printf("failed\n");
6161ac4b82bSMike Smith     } else {
6171ac4b82bSMike Smith 	printf("done\n");
6181ac4b82bSMike Smith     }
6191ac4b82bSMike Smith 
6201ac4b82bSMike Smith     /* delete all our child devices */
6211ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6221ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6231ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
6241ac4b82bSMike Smith 		goto out;
6251ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
6261ac4b82bSMike Smith 	}
6271ac4b82bSMike Smith     }
6281ac4b82bSMike Smith 
6291ac4b82bSMike Smith  out:
6301ac4b82bSMike Smith     splx(s);
6311ac4b82bSMike Smith     return(error);
6321ac4b82bSMike Smith }
6331ac4b82bSMike Smith 
6341ac4b82bSMike Smith /********************************************************************************
6351ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
6361ac4b82bSMike Smith  */
6371ac4b82bSMike Smith int
6381ac4b82bSMike Smith mlx_suspend(device_t dev)
6391ac4b82bSMike Smith {
6401ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6411ac4b82bSMike Smith     int			s;
6421ac4b82bSMike Smith 
643da8bb3a3SMike Smith     debug_called(1);
6441ac4b82bSMike Smith 
6451ac4b82bSMike Smith     s = splbio();
6461ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
6471ac4b82bSMike Smith 
6481ac4b82bSMike Smith     /* flush controller */
6491ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6501ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6511ac4b82bSMike Smith 
6521ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6531ac4b82bSMike Smith     splx(s);
6541ac4b82bSMike Smith 
6551ac4b82bSMike Smith     return(0);
6561ac4b82bSMike Smith }
6571ac4b82bSMike Smith 
6581ac4b82bSMike Smith /********************************************************************************
6591ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
6601ac4b82bSMike Smith  */
6611ac4b82bSMike Smith int
6621ac4b82bSMike Smith mlx_resume(device_t dev)
6631ac4b82bSMike Smith {
6641ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6651ac4b82bSMike Smith 
666da8bb3a3SMike Smith     debug_called(1);
6671ac4b82bSMike Smith 
6681ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
6691ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
6701ac4b82bSMike Smith 
6711ac4b82bSMike Smith     return(0);
6721ac4b82bSMike Smith }
6731ac4b82bSMike Smith 
6741ac4b82bSMike Smith /*******************************************************************************
6751ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
6761ac4b82bSMike Smith  * status.
6771ac4b82bSMike Smith  */
6781ac4b82bSMike Smith void
6791ac4b82bSMike Smith mlx_intr(void *arg)
6801ac4b82bSMike Smith {
6811ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
6821ac4b82bSMike Smith 
683da8bb3a3SMike Smith     debug_called(1);
6841ac4b82bSMike Smith 
6855792b7feSMike Smith     /* collect finished commands, queue anything waiting */
6865792b7feSMike Smith     mlx_done(sc);
6871ac4b82bSMike Smith };
6881ac4b82bSMike Smith 
6891ac4b82bSMike Smith /*******************************************************************************
6901ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
6911ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
6921ac4b82bSMike Smith  */
6931ac4b82bSMike Smith int
69415fd5d22SMike Smith mlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp)
6951ac4b82bSMike Smith {
6964b006d7bSMike Smith     int		s;
6974b006d7bSMike Smith 
698da8bb3a3SMike Smith     debug_called(1);
6991ac4b82bSMike Smith 
7004b006d7bSMike Smith     s = splbio();
70115fd5d22SMike Smith     MLX_BIO_QINSERT(sc->mlx_bioq, bp);
7021ac4b82bSMike Smith     sc->mlx_waitbufs++;
7034b006d7bSMike Smith     splx(s);
7041ac4b82bSMike Smith     mlx_startio(sc);
7051ac4b82bSMike Smith     return(0);
7061ac4b82bSMike Smith }
7071ac4b82bSMike Smith 
7081ac4b82bSMike Smith /********************************************************************************
7091ac4b82bSMike Smith  * Accept an open operation on the control device.
7101ac4b82bSMike Smith  */
7111ac4b82bSMike Smith int
71289c9c53dSPoul-Henning Kamp mlx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
7131ac4b82bSMike Smith {
7141ac4b82bSMike Smith     int			unit = minor(dev);
7151ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7161ac4b82bSMike Smith 
7171ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
7181ac4b82bSMike Smith     return(0);
7191ac4b82bSMike Smith }
7201ac4b82bSMike Smith 
7211ac4b82bSMike Smith /********************************************************************************
7221ac4b82bSMike Smith  * Accept the last close on the control device.
7231ac4b82bSMike Smith  */
7241ac4b82bSMike Smith int
72589c9c53dSPoul-Henning Kamp mlx_close(struct cdev *dev, int flags, int fmt, struct thread *td)
7261ac4b82bSMike Smith {
7271ac4b82bSMike Smith     int			unit = minor(dev);
7281ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
7291ac4b82bSMike Smith 
7301ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
7311ac4b82bSMike Smith     return (0);
7321ac4b82bSMike Smith }
7331ac4b82bSMike Smith 
7341ac4b82bSMike Smith /********************************************************************************
7351ac4b82bSMike Smith  * Handle controller-specific control operations.
7361ac4b82bSMike Smith  */
7371ac4b82bSMike Smith int
73889c9c53dSPoul-Henning Kamp mlx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
7391ac4b82bSMike Smith {
7401ac4b82bSMike Smith     int				unit = minor(dev);
7411ac4b82bSMike Smith     struct mlx_softc		*sc = devclass_get_softc(mlx_devclass, unit);
742421f2f7dSMike Smith     struct mlx_rebuild_request	*rb = (struct mlx_rebuild_request *)addr;
743421f2f7dSMike Smith     struct mlx_rebuild_status	*rs = (struct mlx_rebuild_status *)addr;
7441ac4b82bSMike Smith     int				*arg = (int *)addr;
7451ac4b82bSMike Smith     struct mlx_pause		*mp;
7461ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
7471ac4b82bSMike Smith     struct mlxd_softc		*mlxd;
7481ac4b82bSMike Smith     int				i, error;
7491ac4b82bSMike Smith 
7501ac4b82bSMike Smith     switch(cmd) {
7511ac4b82bSMike Smith 	/*
7521ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
7531ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
7541ac4b82bSMike Smith 	 * a valid unit on this controller.
7551ac4b82bSMike Smith 	 */
7561ac4b82bSMike Smith     case MLX_NEXT_CHILD:
7571ac4b82bSMike Smith 	/* search system drives */
7581ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
7591ac4b82bSMike Smith 	    /* is this one attached? */
7601ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
7611ac4b82bSMike Smith 		/* looking for the next one we come across? */
7621ac4b82bSMike Smith 		if (*arg == -1) {
76329e6fa3aSStephane E. Potvin 		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
7641ac4b82bSMike Smith 		    return(0);
7651ac4b82bSMike Smith 		}
7661ac4b82bSMike Smith 		/* we want the one after this one */
7671ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
7681ac4b82bSMike Smith 		    *arg = -1;
7691ac4b82bSMike Smith 	    }
7701ac4b82bSMike Smith 	}
7711ac4b82bSMike Smith 	return(ENOENT);
7721ac4b82bSMike Smith 
7731ac4b82bSMike Smith 	/*
7741ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
7751ac4b82bSMike Smith 	 */
7761ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
7771ac4b82bSMike Smith 	mlx_startup(sc);
7781ac4b82bSMike Smith 	return(0);
7791ac4b82bSMike Smith 
7801ac4b82bSMike Smith 	/*
7811ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
7821ac4b82bSMike Smith 	 * away.
7831ac4b82bSMike Smith 	 */
7841ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
7851ac4b82bSMike Smith 
7861ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
7871ac4b82bSMike Smith 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
7881ac4b82bSMike Smith 	    return(ENOENT);
7891ac4b82bSMike Smith 
7901ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
7911ac4b82bSMike Smith 	error = 0;
7921ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
7931ac4b82bSMike Smith 	    error = EBUSY;
7941ac4b82bSMike Smith 	    goto detach_out;
7951ac4b82bSMike Smith 	}
7961ac4b82bSMike Smith 
7971ac4b82bSMike Smith 	/* flush controller */
7981ac4b82bSMike Smith 	if (mlx_flush(sc)) {
7991ac4b82bSMike Smith 	    error = EBUSY;
8001ac4b82bSMike Smith 	    goto detach_out;
8011ac4b82bSMike Smith 	}
8021ac4b82bSMike Smith 
8031ac4b82bSMike Smith 	/* nuke drive */
8041ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
8051ac4b82bSMike Smith 	    goto detach_out;
8061ac4b82bSMike Smith 	dr->ms_disk = 0;
8071ac4b82bSMike Smith 
8081ac4b82bSMike Smith     detach_out:
8091ac4b82bSMike Smith 	if (error) {
8101ac4b82bSMike Smith 	    printf("failed\n");
8111ac4b82bSMike Smith 	} else {
8121ac4b82bSMike Smith 	    printf("done\n");
8131ac4b82bSMike Smith 	}
8141ac4b82bSMike Smith 	return(error);
8151ac4b82bSMike Smith 
8161ac4b82bSMike Smith 	/*
8171ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
8181ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
8191ac4b82bSMike Smith 	 *
8201ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
8211ac4b82bSMike Smith 	 * to do this right.
8221ac4b82bSMike Smith 	 */
8231ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
8241ac4b82bSMike Smith 	/* Does this command work on this firmware? */
8251ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
8261ac4b82bSMike Smith 	    return(EOPNOTSUPP);
8271ac4b82bSMike Smith 
8281ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
8291ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
8301ac4b82bSMike Smith 	    /* cancel a pending pause operation */
8311ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
8321ac4b82bSMike Smith 	} else {
8331ac4b82bSMike Smith 	    /* fix for legal channels */
8349eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
8351ac4b82bSMike Smith 	    /* check time values */
8361ac4b82bSMike Smith 	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
8371ac4b82bSMike Smith 		return(EINVAL);
8381ac4b82bSMike Smith 	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
8391ac4b82bSMike Smith 		return(EINVAL);
8401ac4b82bSMike Smith 
8411ac4b82bSMike Smith 	    /* check for a pause currently running */
8421ac4b82bSMike Smith 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
8431ac4b82bSMike Smith 		return(EBUSY);
8441ac4b82bSMike Smith 
8451ac4b82bSMike Smith 	    /* looks ok, go with it */
8461ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
8471ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
8481ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
8491ac4b82bSMike Smith 	}
8501ac4b82bSMike Smith 	return(0);
8511ac4b82bSMike Smith 
8521ac4b82bSMike Smith 	/*
8531ac4b82bSMike Smith 	 * Accept a command passthrough-style.
8541ac4b82bSMike Smith 	 */
8551ac4b82bSMike Smith     case MLX_COMMAND:
8561ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
8571ac4b82bSMike Smith 
858421f2f7dSMike Smith 	/*
859421f2f7dSMike Smith 	 * Start a rebuild on a given SCSI disk
860421f2f7dSMike Smith 	 */
861421f2f7dSMike Smith     case MLX_REBUILDASYNC:
862421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
863421f2f7dSMike Smith 	    rb->rr_status = 0x0106;
864421f2f7dSMike Smith 	    return(EBUSY);
865421f2f7dSMike Smith 	}
866421f2f7dSMike Smith 	rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
867421f2f7dSMike Smith 	switch (rb->rr_status) {
868421f2f7dSMike Smith 	case 0:
869421f2f7dSMike Smith 	    error = 0;
870421f2f7dSMike Smith 	    break;
871421f2f7dSMike Smith 	case 0x10000:
872421f2f7dSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
873421f2f7dSMike Smith 	    break;
874421f2f7dSMike Smith 	case 0x0002:
875421f2f7dSMike Smith 	    error = EBUSY;
876421f2f7dSMike Smith 	    break;
877421f2f7dSMike Smith 	case 0x0104:
878421f2f7dSMike Smith 	    error = EIO;
879421f2f7dSMike Smith 	    break;
880421f2f7dSMike Smith 	case 0x0105:
881421f2f7dSMike Smith 	    error = ERANGE;
882421f2f7dSMike Smith 	    break;
883421f2f7dSMike Smith 	case 0x0106:
884421f2f7dSMike Smith 	    error = EBUSY;
885421f2f7dSMike Smith 	    break;
886421f2f7dSMike Smith 	default:
887421f2f7dSMike Smith 	    error = EINVAL;
888421f2f7dSMike Smith 	    break;
889421f2f7dSMike Smith 	}
890421f2f7dSMike Smith 	if (error == 0)
891421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_REBUILD;
892421f2f7dSMike Smith 	return(error);
893421f2f7dSMike Smith 
894421f2f7dSMike Smith 	/*
895421f2f7dSMike Smith 	 * Get the status of the current rebuild or consistency check.
896421f2f7dSMike Smith 	 */
897421f2f7dSMike Smith     case MLX_REBUILDSTAT:
898421f2f7dSMike Smith 	*rs = sc->mlx_rebuildstat;
899421f2f7dSMike Smith 	return(0);
900421f2f7dSMike Smith 
901421f2f7dSMike Smith 	/*
902421f2f7dSMike Smith 	 * Return the per-controller system drive number matching the
903421f2f7dSMike Smith 	 * disk device number in (arg), if it happens to belong to us.
904421f2f7dSMike Smith 	 */
905421f2f7dSMike Smith     case MLX_GET_SYSDRIVE:
906421f2f7dSMike Smith 	error = ENOENT;
907421f2f7dSMike Smith 	mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg);
908421f2f7dSMike Smith 	if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
909421f2f7dSMike Smith 	    (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
910421f2f7dSMike Smith 	    error = 0;
911421f2f7dSMike Smith 	    *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
912421f2f7dSMike Smith 	}
913421f2f7dSMike Smith 	return(error);
914421f2f7dSMike Smith 
9151ac4b82bSMike Smith     default:
9161ac4b82bSMike Smith 	return(ENOTTY);
9171ac4b82bSMike Smith     }
9181ac4b82bSMike Smith }
9191ac4b82bSMike Smith 
9201ac4b82bSMike Smith /********************************************************************************
9211ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
9221ac4b82bSMike Smith  */
9231ac4b82bSMike Smith int
9241ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
925b40ce416SJulian Elischer 		caddr_t addr, int32_t flag, struct thread *td)
9261ac4b82bSMike Smith {
9271ac4b82bSMike Smith     int				*arg = (int *)addr;
928421f2f7dSMike Smith     int				error, result;
9291ac4b82bSMike Smith 
9301ac4b82bSMike Smith     switch(cmd) {
9311ac4b82bSMike Smith 	/*
9321ac4b82bSMike Smith 	 * Return the current status of this drive.
9331ac4b82bSMike Smith 	 */
9341ac4b82bSMike Smith     case MLXD_STATUS:
9351ac4b82bSMike Smith 	*arg = drive->ms_state;
9361ac4b82bSMike Smith 	return(0);
9371ac4b82bSMike Smith 
9381ac4b82bSMike Smith 	/*
939421f2f7dSMike Smith 	 * Start a background consistency check on this drive.
9401ac4b82bSMike Smith 	 */
941421f2f7dSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
942421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
943421f2f7dSMike Smith 	    *arg = 0x0106;
9441ac4b82bSMike Smith 	    return(EBUSY);
945421f2f7dSMike Smith 	}
946421f2f7dSMike Smith 	result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
947421f2f7dSMike Smith 	switch (result) {
9481ac4b82bSMike Smith 	case 0:
9491ac4b82bSMike Smith 	    error = 0;
9501ac4b82bSMike Smith 	    break;
9511ac4b82bSMike Smith 	case 0x10000:
9521ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
9531ac4b82bSMike Smith 	    break;
9541ac4b82bSMike Smith 	case 0x0002:
9551ac4b82bSMike Smith 	    error = EIO;
9561ac4b82bSMike Smith 	    break;
9571ac4b82bSMike Smith 	case 0x0105:
9581ac4b82bSMike Smith 	    error = ERANGE;
9591ac4b82bSMike Smith 	    break;
960421f2f7dSMike Smith 	case 0x0106:
961421f2f7dSMike Smith 	    error = EBUSY;
962421f2f7dSMike Smith 	    break;
9631ac4b82bSMike Smith 	default:
9641ac4b82bSMike Smith 	    error = EINVAL;
9651ac4b82bSMike Smith 	    break;
9661ac4b82bSMike Smith 	}
967421f2f7dSMike Smith 	if (error == 0)
968421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_CHECK;
969421f2f7dSMike Smith 	*arg = result;
9701ac4b82bSMike Smith 	return(error);
9711ac4b82bSMike Smith 
9721ac4b82bSMike Smith     }
9731ac4b82bSMike Smith     return(ENOIOCTL);
9741ac4b82bSMike Smith }
9751ac4b82bSMike Smith 
9761ac4b82bSMike Smith 
9771ac4b82bSMike Smith /********************************************************************************
9781ac4b82bSMike Smith  ********************************************************************************
9791ac4b82bSMike Smith                                                                 Status Monitoring
9801ac4b82bSMike Smith  ********************************************************************************
9811ac4b82bSMike Smith  ********************************************************************************/
9821ac4b82bSMike Smith 
9831ac4b82bSMike Smith /********************************************************************************
9841ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
9851ac4b82bSMike Smith  */
9861ac4b82bSMike Smith static void
9871ac4b82bSMike Smith mlx_periodic(void *data)
9881ac4b82bSMike Smith {
9891ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
9901ac4b82bSMike Smith 
991da8bb3a3SMike Smith     debug_called(1);
9921ac4b82bSMike Smith 
9931ac4b82bSMike Smith     /*
9941ac4b82bSMike Smith      * Run a bus pause?
9951ac4b82bSMike Smith      */
9961ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
9971ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
9981ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
9991ac4b82bSMike Smith 
10001ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
10011ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
10021ac4b82bSMike Smith 	sysbeep(500, hz);
10031ac4b82bSMike Smith 
10041ac4b82bSMike Smith 	/*
10051ac4b82bSMike Smith 	 * Bus pause still running?
10061ac4b82bSMike Smith 	 */
10071ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
10081ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
10091ac4b82bSMike Smith 
10101ac4b82bSMike Smith 	/* time to stop bus pause? */
10111ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
10121ac4b82bSMike Smith 	    mlx_pause_action(sc);
10131ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
10141ac4b82bSMike Smith 	    sysbeep(500, hz);
10151ac4b82bSMike Smith 	} else {
10161ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
10171ac4b82bSMike Smith 	}
10181ac4b82bSMike Smith 
10191ac4b82bSMike Smith 	/*
10201ac4b82bSMike Smith 	 * Run normal periodic activities?
10211ac4b82bSMike Smith 	 */
10225792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
10231ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
10241ac4b82bSMike Smith 
10251ac4b82bSMike Smith 	/*
10261ac4b82bSMike Smith 	 * Check controller status.
10275792b7feSMike Smith 	 *
10285792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
10291ac4b82bSMike Smith 	 */
1030da8bb3a3SMike Smith 	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
1031da8bb3a3SMike Smith 		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
10321ac4b82bSMike Smith 
10331ac4b82bSMike Smith 	/*
10341ac4b82bSMike Smith 	 * Check system drive status.
10351ac4b82bSMike Smith 	 *
10361ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
10371ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
10381ac4b82bSMike Smith 	 *     online.
10391ac4b82bSMike Smith 	 */
10401ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
10411ac4b82bSMike Smith 			mlx_periodic_enquiry);
10421ac4b82bSMike Smith 
10431ac4b82bSMike Smith     }
10441ac4b82bSMike Smith 
1045421f2f7dSMike Smith     /* get drive rebuild/check status */
1046421f2f7dSMike Smith     /* XXX should check sc->mlx_background if this is only valid while in progress */
1047421f2f7dSMike Smith     mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
1048421f2f7dSMike Smith 
10495792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
10505792b7feSMike Smith     mlx_done(sc);
10511ac4b82bSMike Smith 
10521ac4b82bSMike Smith     /* reschedule another poll next second or so */
10531ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
10541ac4b82bSMike Smith }
10551ac4b82bSMike Smith 
10561ac4b82bSMike Smith /********************************************************************************
10571ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
10581ac4b82bSMike Smith  */
10591ac4b82bSMike Smith static void
10601ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
10611ac4b82bSMike Smith {
10621ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
10631ac4b82bSMike Smith 
1064da8bb3a3SMike Smith     debug_called(1);
10651ac4b82bSMike Smith 
10661ac4b82bSMike Smith     /* Command completed OK? */
10671ac4b82bSMike Smith     if (mc->mc_status != 0) {
1068da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
10691ac4b82bSMike Smith 	goto out;
10701ac4b82bSMike Smith     }
10711ac4b82bSMike Smith 
10721ac4b82bSMike Smith     /* respond to command */
10731ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
10741ac4b82bSMike Smith 	/*
1075da8bb3a3SMike Smith 	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
1076da8bb3a3SMike Smith 	 * pointer yet.
1077da8bb3a3SMike Smith 	 */
1078da8bb3a3SMike Smith     case MLX_CMD_ENQUIRY_OLD:
1079da8bb3a3SMike Smith     {
1080da8bb3a3SMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
1081da8bb3a3SMike Smith 	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
1082da8bb3a3SMike Smith 	int				i;
1083da8bb3a3SMike Smith 
1084da8bb3a3SMike Smith 	/* convert data in-place to new format */
1085da8bb3a3SMike Smith 	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
1086da8bb3a3SMike Smith 	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1087da8bb3a3SMike Smith 	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1088da8bb3a3SMike Smith 	}
1089da8bb3a3SMike Smith 	me->me_misc_flags        = 0;
1090da8bb3a3SMike Smith 	me->me_rebuild_count     = meo->me_rebuild_count;
1091da8bb3a3SMike Smith 	me->me_dead_count        = meo->me_dead_count;
1092da8bb3a3SMike Smith 	me->me_critical_sd_count = meo->me_critical_sd_count;
1093da8bb3a3SMike Smith 	me->me_event_log_seq_num = 0;
1094da8bb3a3SMike Smith 	me->me_offline_sd_count  = meo->me_offline_sd_count;
1095da8bb3a3SMike Smith 	me->me_max_commands      = meo->me_max_commands;
1096da8bb3a3SMike Smith 	me->me_rebuild_flag      = meo->me_rebuild_flag;
1097da8bb3a3SMike Smith 	me->me_fwmajor           = meo->me_fwmajor;
1098da8bb3a3SMike Smith 	me->me_fwminor           = meo->me_fwminor;
1099da8bb3a3SMike Smith 	me->me_status_flags      = meo->me_status_flags;
1100da8bb3a3SMike Smith 	me->me_flash_age         = meo->me_flash_age;
1101da8bb3a3SMike Smith 	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
1102da8bb3a3SMike Smith 	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
1103da8bb3a3SMike Smith 		me->me_drvsize[i] = 0;		/* drive beyond supported range */
1104da8bb3a3SMike Smith 	    } else {
1105da8bb3a3SMike Smith 		me->me_drvsize[i] = meo->me_drvsize[i];
1106da8bb3a3SMike Smith 	    }
1107da8bb3a3SMike Smith 	}
1108da8bb3a3SMike Smith 	me->me_num_sys_drvs = meo->me_num_sys_drvs;
1109da8bb3a3SMike Smith     }
1110da8bb3a3SMike Smith     /* FALLTHROUGH */
1111da8bb3a3SMike Smith 
1112da8bb3a3SMike Smith 	/*
11131ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
11141ac4b82bSMike Smith 	 * checking the event log.
11151ac4b82bSMike Smith 	 */
11161ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
11171ac4b82bSMike Smith     {
11181ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
11191ac4b82bSMike Smith 
1120421f2f7dSMike Smith 	if (sc->mlx_currevent == -1) {
11219eee27f1SMike Smith 	    /* initialise our view of the event log */
11229eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
11235d278f5cSMike Smith 	} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
11241ac4b82bSMike Smith 	    /* record where current events are up to */
11251ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
1126da8bb3a3SMike Smith 	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
11271ac4b82bSMike Smith 
11285d278f5cSMike Smith 	    /* mark the event log as busy */
11295d278f5cSMike Smith 	    atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
11305d278f5cSMike Smith 
11319eee27f1SMike Smith 	    /* drain new eventlog entries */
11321ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
11331ac4b82bSMike Smith 	}
11341ac4b82bSMike Smith 	break;
11351ac4b82bSMike Smith     }
11361ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
11371ac4b82bSMike Smith     {
11381ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
11391ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
11401ac4b82bSMike Smith 	int				i;
11411ac4b82bSMike Smith 
11421ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
11431ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
11441ac4b82bSMike Smith 	     i++) {
11451ac4b82bSMike Smith 
11461ac4b82bSMike Smith 	    /* has state been changed by controller? */
11471ac4b82bSMike Smith 	    if (dr->ms_state != mes[i].sd_state) {
11481ac4b82bSMike Smith 		switch(mes[i].sd_state) {
11491ac4b82bSMike Smith 		case MLX_SYSD_OFFLINE:
11501ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive offline\n");
11511ac4b82bSMike Smith 		    break;
11521ac4b82bSMike Smith 		case MLX_SYSD_ONLINE:
11531ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive online\n");
11541ac4b82bSMike Smith 		    break;
11551ac4b82bSMike Smith 		case MLX_SYSD_CRITICAL:
11561ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive critical\n");
11571ac4b82bSMike Smith 		    break;
11581ac4b82bSMike Smith 		}
11591ac4b82bSMike Smith 		/* save new state */
11601ac4b82bSMike Smith 		dr->ms_state = mes[i].sd_state;
11611ac4b82bSMike Smith 	    }
11621ac4b82bSMike Smith 	}
11631ac4b82bSMike Smith 	break;
11641ac4b82bSMike Smith     }
11651ac4b82bSMike Smith     default:
11666e551fb6SDavid E. O'Brien 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]);
11671ac4b82bSMike Smith 	break;
11681ac4b82bSMike Smith     }
11691ac4b82bSMike Smith 
11701ac4b82bSMike Smith  out:
11711ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
11721ac4b82bSMike Smith     mlx_releasecmd(mc);
11731ac4b82bSMike Smith }
11741ac4b82bSMike Smith 
11751b4404f9SScott Long static void
11761b4404f9SScott Long mlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
11771b4404f9SScott Long {
11781b4404f9SScott Long     struct mlx_command *mc;
11791b4404f9SScott Long 
11801b4404f9SScott Long     mc = (struct mlx_command *)arg;
11811b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
11821b4404f9SScott Long 
11831b4404f9SScott Long     /* build the command to get one entry */
11841b4404f9SScott Long     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1,
11851b4404f9SScott Long 		   mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
11861b4404f9SScott Long     mc->mc_complete = mlx_periodic_eventlog_respond;
11871b4404f9SScott Long     mc->mc_private = mc;
11881b4404f9SScott Long 
11891b4404f9SScott Long     /* start the command */
11901b4404f9SScott Long     if (mlx_start(mc) != 0) {
11911b4404f9SScott Long 	mlx_releasecmd(mc);
11921b4404f9SScott Long 	free(mc->mc_data, M_DEVBUF);
11931b4404f9SScott Long 	mc->mc_data = NULL;
11941b4404f9SScott Long     }
11951b4404f9SScott Long 
11961b4404f9SScott Long }
11971b4404f9SScott Long 
11981ac4b82bSMike Smith /********************************************************************************
11991ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
12001ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
12011ac4b82bSMike Smith  */
12021ac4b82bSMike Smith static void
12031ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
12041ac4b82bSMike Smith {
12051ac4b82bSMike Smith     struct mlx_command	*mc;
12061ac4b82bSMike Smith     void		*result = NULL;
12071b4404f9SScott Long     int			error = 0;
12081ac4b82bSMike Smith 
1209da8bb3a3SMike Smith     debug_called(1);
12101ac4b82bSMike Smith 
12111ac4b82bSMike Smith     /* get ourselves a command buffer */
12121ac4b82bSMike Smith     error = 1;
12131ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
12141ac4b82bSMike Smith 	goto out;
12151b4404f9SScott Long 
12161ac4b82bSMike Smith     /* allocate the response structure */
12171b4404f9SScott Long     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF,
12181b4404f9SScott Long 			 M_NOWAIT)) == NULL)
12191ac4b82bSMike Smith 	goto out;
12201b4404f9SScott Long 
12211ac4b82bSMike Smith     /* get a command slot */
12221ac4b82bSMike Smith     if (mlx_getslot(mc))
12231ac4b82bSMike Smith 	goto out;
12241ac4b82bSMike Smith 
12251ac4b82bSMike Smith     /* map the command so the controller can see it */
12261ac4b82bSMike Smith     mc->mc_data = result;
122733c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
12281b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
12291b4404f9SScott Long 			    mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT);
12301ac4b82bSMike Smith 
12311ac4b82bSMike Smith  out:
123233c8cb18SMike Smith     if (error != 0) {
12331ac4b82bSMike Smith 	if (mc != NULL)
12341ac4b82bSMike Smith 	    mlx_releasecmd(mc);
12351b4404f9SScott Long 	if ((result != NULL) && (mc->mc_data != NULL))
12361ac4b82bSMike Smith 	    free(result, M_DEVBUF);
12371ac4b82bSMike Smith     }
123833c8cb18SMike Smith }
12391ac4b82bSMike Smith 
12401ac4b82bSMike Smith /********************************************************************************
12411ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
12421ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
12431ac4b82bSMike Smith  */
12441ac4b82bSMike Smith static char *mlx_sense_messages[] = {
12451ac4b82bSMike Smith     "because write recovery failed",
12461ac4b82bSMike Smith     "because of SCSI bus reset failure",
12471ac4b82bSMike Smith     "because of double check condition",
12481ac4b82bSMike Smith     "because it was removed",
12491ac4b82bSMike Smith     "because of gross error on SCSI chip",
12501ac4b82bSMike Smith     "because of bad tag returned from drive",
12511ac4b82bSMike Smith     "because of timeout on SCSI command",
12521ac4b82bSMike Smith     "because of reset SCSI command issued from system",
12531ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
12541ac4b82bSMike Smith     "because of 'kill drive' command from system",
12551ac4b82bSMike Smith     "because of selection timeout",
12561ac4b82bSMike Smith     "due to SCSI phase sequence error",
12571ac4b82bSMike Smith     "due to unknown status"
12581ac4b82bSMike Smith };
12591ac4b82bSMike Smith 
12601ac4b82bSMike Smith static void
12611ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
12621ac4b82bSMike Smith {
12631ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
12641ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
12651ac4b82bSMike Smith     char			*reason;
12661ac4b82bSMike Smith 
1267da8bb3a3SMike Smith     debug_called(1);
12681ac4b82bSMike Smith 
12695792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
12701ac4b82bSMike Smith     if (mc->mc_status == 0) {
12711ac4b82bSMike Smith 
12721ac4b82bSMike Smith 	/* handle event log message */
12731ac4b82bSMike Smith 	switch(el->el_type) {
12741ac4b82bSMike Smith 	    /*
12751ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
12761ac4b82bSMike Smith 	     * The tests here are probably incomplete.
12771ac4b82bSMike Smith 	     */
12781ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
12791ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
12801ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
12811ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
12821ac4b82bSMike Smith 		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
12831ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
12841ac4b82bSMike Smith 		} else {
12851ac4b82bSMike Smith 		    reason = "for unknown reason";
12861ac4b82bSMike Smith 		}
12871ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
12881ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
12891ac4b82bSMike Smith 	    }
12901ac4b82bSMike Smith 	    /* SCSI drive was reset? */
12911ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
12921ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
12931ac4b82bSMike Smith 			      el->el_channel, el->el_target);
12941ac4b82bSMike Smith 	    }
12951ac4b82bSMike Smith 	    /* SCSI drive error? */
12961ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
12971ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
12981ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
12991ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
13001ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
13011ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
13021ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
13031ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
13041ac4b82bSMike Smith 	    }
13051ac4b82bSMike Smith 	    break;
13061ac4b82bSMike Smith 
13071ac4b82bSMike Smith 	default:
13081ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
13091ac4b82bSMike Smith 	    break;
13101ac4b82bSMike Smith 	}
13111ac4b82bSMike Smith     } else {
13121ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
13135d278f5cSMike Smith 	/* give up on all the outstanding messages, as we may have come unsynched */
13145d278f5cSMike Smith 	sc->mlx_lastevent = sc->mlx_currevent;
13151ac4b82bSMike Smith     }
13161ac4b82bSMike Smith 
13171ac4b82bSMike Smith     /* dispose of command and data */
13181ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
13191ac4b82bSMike Smith     mlx_releasecmd(mc);
13201ac4b82bSMike Smith 
13211ac4b82bSMike Smith     /* is there another message to obtain? */
13225d278f5cSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent) {
13231ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
13245d278f5cSMike Smith     } else {
13255d278f5cSMike Smith 	/* clear log-busy status */
13265d278f5cSMike Smith 	atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
13275d278f5cSMike Smith     }
13281ac4b82bSMike Smith }
13291ac4b82bSMike Smith 
13301ac4b82bSMike Smith /********************************************************************************
1331421f2f7dSMike Smith  * Handle check/rebuild operations in progress.
13321ac4b82bSMike Smith  */
13331ac4b82bSMike Smith static void
13341ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
13351ac4b82bSMike Smith {
13361ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
1337421f2f7dSMike Smith     struct mlx_rebuild_status	*mr = (struct mlx_rebuild_status *)mc->mc_data;
13381ac4b82bSMike Smith 
13391ac4b82bSMike Smith     switch(mc->mc_status) {
1340421f2f7dSMike Smith     case 0:				/* operation running, update stats */
1341421f2f7dSMike Smith 	sc->mlx_rebuildstat = *mr;
1342421f2f7dSMike Smith 
1343421f2f7dSMike Smith 	/* spontaneous rebuild/check? */
1344421f2f7dSMike Smith 	if (sc->mlx_background == 0) {
1345421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
1346421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
1347421f2f7dSMike Smith 	}
13481ac4b82bSMike Smith 	break;
13491ac4b82bSMike Smith 
1350421f2f7dSMike Smith     case 0x0105:			/* nothing running, finalise stats and report */
1351421f2f7dSMike Smith 	switch(sc->mlx_background) {
1352421f2f7dSMike Smith 	case MLX_BACKGROUND_CHECK:
1353421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");	/* XXX print drive? */
1354421f2f7dSMike Smith 	    break;
1355421f2f7dSMike Smith 	case MLX_BACKGROUND_REBUILD:
1356421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "drive rebuild completed\n");	/* XXX print channel/target? */
1357421f2f7dSMike Smith 	    break;
1358421f2f7dSMike Smith 	case MLX_BACKGROUND_SPONTANEOUS:
1359421f2f7dSMike Smith 	default:
1360421f2f7dSMike Smith 	    /* if we have previously been non-idle, report the transition */
1361421f2f7dSMike Smith 	    if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
1362421f2f7dSMike Smith 		device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
13631ac4b82bSMike Smith 	    }
1364421f2f7dSMike Smith 	}
1365421f2f7dSMike Smith 	sc->mlx_background = 0;
1366421f2f7dSMike Smith 	sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
13671ac4b82bSMike Smith 	break;
13681ac4b82bSMike Smith     }
13691ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
13701ac4b82bSMike Smith     mlx_releasecmd(mc);
13711ac4b82bSMike Smith }
13721ac4b82bSMike Smith 
13731ac4b82bSMike Smith /********************************************************************************
13741ac4b82bSMike Smith  ********************************************************************************
13751ac4b82bSMike Smith                                                                     Channel Pause
13761ac4b82bSMike Smith  ********************************************************************************
13771ac4b82bSMike Smith  ********************************************************************************/
13781ac4b82bSMike Smith 
13791ac4b82bSMike Smith /********************************************************************************
13801ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
13811ac4b82bSMike Smith  * the pause.
13821ac4b82bSMike Smith  */
13831ac4b82bSMike Smith static void
13841ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
13851ac4b82bSMike Smith {
13861ac4b82bSMike Smith     struct mlx_command	*mc;
13871ac4b82bSMike Smith     int			failsafe, i, command;
13881ac4b82bSMike Smith 
13891ac4b82bSMike Smith     /* What are we doing here? */
13901ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
13911ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
13921ac4b82bSMike Smith 	failsafe = 0;
13931ac4b82bSMike Smith 
13941ac4b82bSMike Smith     } else {
13951ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
13961ac4b82bSMike Smith 
13971ac4b82bSMike Smith 	/*
13981ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
13991ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
14001ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
14011ac4b82bSMike Smith 	 */
14021ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
14031ac4b82bSMike Smith 	if (failsafe > 0xf) {
14041ac4b82bSMike Smith 	    failsafe = 0xf;
14051ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
14061ac4b82bSMike Smith 	}
14071ac4b82bSMike Smith     }
14081ac4b82bSMike Smith 
14091ac4b82bSMike Smith     /* build commands for every channel requested */
14109eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
14111ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
14121ac4b82bSMike Smith 
14131ac4b82bSMike Smith 	    /* get ourselves a command buffer */
14141ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
14151ac4b82bSMike Smith 		goto fail;
14161ac4b82bSMike Smith 	    /* get a command slot */
14171ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
14181ac4b82bSMike Smith 	    if (mlx_getslot(mc))
14191ac4b82bSMike Smith 		goto fail;
14201ac4b82bSMike Smith 
14211ac4b82bSMike Smith 	    /* build the command */
14221ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
14231ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
14241ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
14251ac4b82bSMike Smith 	    if (mlx_start(mc))
14261ac4b82bSMike Smith 		goto fail;
14271ac4b82bSMike Smith 	    /* command submitted OK */
14281ac4b82bSMike Smith 	    return;
14291ac4b82bSMike Smith 
14301ac4b82bSMike Smith 	fail:
14311ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
14321ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
14331ac4b82bSMike Smith 	    if (mc != NULL)
14341ac4b82bSMike Smith 		mlx_releasecmd(mc);
14351ac4b82bSMike Smith 	}
14361ac4b82bSMike Smith     }
14371ac4b82bSMike Smith }
14381ac4b82bSMike Smith 
14391ac4b82bSMike Smith static void
14401ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
14411ac4b82bSMike Smith {
14421ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
14431ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
14441ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
14451ac4b82bSMike Smith 
14461ac4b82bSMike Smith     if (mc->mc_status != 0) {
14471ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
14481ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
14491ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
14501ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
145172c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
14521ac4b82bSMike Smith     } else {
14531ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
14541ac4b82bSMike Smith     }
14551ac4b82bSMike Smith     mlx_releasecmd(mc);
14561ac4b82bSMike Smith }
14571ac4b82bSMike Smith 
14581ac4b82bSMike Smith /********************************************************************************
14591ac4b82bSMike Smith  ********************************************************************************
14601ac4b82bSMike Smith                                                                Command Submission
14611ac4b82bSMike Smith  ********************************************************************************
14621ac4b82bSMike Smith  ********************************************************************************/
14631ac4b82bSMike Smith 
14641b4404f9SScott Long static void
14651b4404f9SScott Long mlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
14661b4404f9SScott Long {
14671b4404f9SScott Long     struct mlx_softc *sc;
14681b4404f9SScott Long     struct mlx_command *mc;
14691b4404f9SScott Long 
14701b4404f9SScott Long     mc = (struct mlx_command *)arg;
14711b4404f9SScott Long     if (error)
14721b4404f9SScott Long 	return;
14731b4404f9SScott Long 
14741b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
14751b4404f9SScott Long 
14761b4404f9SScott Long     /* build an enquiry command */
14771b4404f9SScott Long     sc = mc->mc_sc;
14781b4404f9SScott Long     mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
14791b4404f9SScott Long 
14801b4404f9SScott Long     /* do we want a completion callback? */
14811b4404f9SScott Long     if (mc->mc_complete != NULL) {
14821b4404f9SScott Long 	if ((error = mlx_start(mc)) != 0)
14831b4404f9SScott Long 	    return;
14841b4404f9SScott Long     } else {
14851b4404f9SScott Long 	/* run the command in either polled or wait mode */
14861b4404f9SScott Long 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) :
14871b4404f9SScott Long 						mlx_poll_command(mc))
14881b4404f9SScott Long 	    return;
14891b4404f9SScott Long 
14901b4404f9SScott Long 	/* command completed OK? */
14911b4404f9SScott Long 	if (mc->mc_status != 0) {
14921b4404f9SScott Long 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n",
14931b4404f9SScott Long 			  mlx_diagnose_command(mc));
14941b4404f9SScott Long 	    return;
14951b4404f9SScott Long 	}
14961b4404f9SScott Long     }
14971b4404f9SScott Long }
14981b4404f9SScott Long 
14991ac4b82bSMike Smith /********************************************************************************
15001ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
15011ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
15021ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
15031ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
15041ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
15051ac4b82bSMike Smith  */
15061ac4b82bSMike Smith static void *
15071ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
15081ac4b82bSMike Smith {
15091ac4b82bSMike Smith     struct mlx_command	*mc;
15101ac4b82bSMike Smith     void		*result;
15111ac4b82bSMike Smith     int			error;
15121ac4b82bSMike Smith 
1513da8bb3a3SMike Smith     debug_called(1);
15141ac4b82bSMike Smith 
15151ac4b82bSMike Smith     /* get ourselves a command buffer */
15161ac4b82bSMike Smith     error = 1;
15171ac4b82bSMike Smith     result = NULL;
15181ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
15191ac4b82bSMike Smith 	goto out;
15201ac4b82bSMike Smith     /* allocate the response structure */
15211ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
15221ac4b82bSMike Smith 	goto out;
15231ac4b82bSMike Smith     /* get a command slot */
15241ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
15251ac4b82bSMike Smith     if (mlx_getslot(mc))
15261ac4b82bSMike Smith 	goto out;
15271ac4b82bSMike Smith 
15281ac4b82bSMike Smith     /* map the command so the controller can see it */
15291ac4b82bSMike Smith     mc->mc_data = result;
15301ac4b82bSMike Smith     mc->mc_length = bufsize;
15311b4404f9SScott Long     mc->mc_command = command;
15321ac4b82bSMike Smith 
15331ac4b82bSMike Smith     if (complete != NULL) {
15341ac4b82bSMike Smith 	mc->mc_complete = complete;
15351ac4b82bSMike Smith 	mc->mc_private = mc;
15361b4404f9SScott Long     }
15371ac4b82bSMike Smith 
15381b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
15391b4404f9SScott Long 			    mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT);
15401b4404f9SScott Long 
15411ac4b82bSMike Smith  out:
15421ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
154323691262SSam Leffler     if ((mc != NULL) && (mc->mc_complete == NULL))
15441ac4b82bSMike Smith 	mlx_releasecmd(mc);
154533c8cb18SMike Smith     /* we got an error, and we allocated a result */
1546d4881905SScott Long     if ((error != 0) && (result != NULL)) {
1547d4881905SScott Long 	free(result, M_DEVBUF);
1548a7700303SScott Long 	result = NULL;
15491ac4b82bSMike Smith     }
15501ac4b82bSMike Smith     return(result);
15511ac4b82bSMike Smith }
15521ac4b82bSMike Smith 
15531ac4b82bSMike Smith 
15541ac4b82bSMike Smith /********************************************************************************
15551ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
15561ac4b82bSMike Smith  *
15571ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
15581ac4b82bSMike Smith  * the flush operation completes or fails.
15591ac4b82bSMike Smith  */
15601ac4b82bSMike Smith static int
15611ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
15621ac4b82bSMike Smith {
15631ac4b82bSMike Smith     struct mlx_command	*mc;
15641ac4b82bSMike Smith     int			error;
15651ac4b82bSMike Smith 
1566da8bb3a3SMike Smith     debug_called(1);
15671ac4b82bSMike Smith 
15681ac4b82bSMike Smith     /* get ourselves a command buffer */
15691ac4b82bSMike Smith     error = 1;
15701ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
15711ac4b82bSMike Smith 	goto out;
15721ac4b82bSMike Smith     /* get a command slot */
15731ac4b82bSMike Smith     if (mlx_getslot(mc))
15741ac4b82bSMike Smith 	goto out;
15751ac4b82bSMike Smith 
15761ac4b82bSMike Smith     /* build a flush command */
15771ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
15781ac4b82bSMike Smith 
15795792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
15805792b7feSMike Smith     if (mlx_poll_command(mc))
15811ac4b82bSMike Smith 	goto out;
15821ac4b82bSMike Smith 
15831ac4b82bSMike Smith     /* command completed OK? */
15841ac4b82bSMike Smith     if (mc->mc_status != 0) {
15851ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
15861ac4b82bSMike Smith 	goto out;
15871ac4b82bSMike Smith     }
15881ac4b82bSMike Smith 
15891ac4b82bSMike Smith     error = 0;			/* success */
15901ac4b82bSMike Smith  out:
15911ac4b82bSMike Smith     if (mc != NULL)
15921ac4b82bSMike Smith 	mlx_releasecmd(mc);
15931ac4b82bSMike Smith     return(error);
15941ac4b82bSMike Smith }
15951ac4b82bSMike Smith 
15961ac4b82bSMike Smith /********************************************************************************
1597421f2f7dSMike Smith  * Start a background consistency check on (drive).
1598421f2f7dSMike Smith  *
1599421f2f7dSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
1600421f2f7dSMike Smith  * operation has started or been refused.
1601421f2f7dSMike Smith  */
1602421f2f7dSMike Smith static int
1603421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive)
1604421f2f7dSMike Smith {
1605421f2f7dSMike Smith     struct mlx_command	*mc;
1606421f2f7dSMike Smith     int			error;
1607421f2f7dSMike Smith 
1608421f2f7dSMike Smith     debug_called(1);
1609421f2f7dSMike Smith 
1610421f2f7dSMike Smith     /* get ourselves a command buffer */
1611421f2f7dSMike Smith     error = 0x10000;
1612421f2f7dSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
1613421f2f7dSMike Smith 	goto out;
1614421f2f7dSMike Smith     /* get a command slot */
1615421f2f7dSMike Smith     if (mlx_getslot(mc))
1616421f2f7dSMike Smith 	goto out;
1617421f2f7dSMike Smith 
1618421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
1619421f2f7dSMike Smith     mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
1620421f2f7dSMike Smith 
1621421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1622421f2f7dSMike Smith     if (mlx_wait_command(mc))
1623421f2f7dSMike Smith 	goto out;
1624421f2f7dSMike Smith 
1625421f2f7dSMike Smith     /* command completed OK? */
1626421f2f7dSMike Smith     if (mc->mc_status != 0) {
1627421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
1628421f2f7dSMike Smith     } else {
1629421f2f7dSMike Smith 	device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
1630421f2f7dSMike Smith     }
1631421f2f7dSMike Smith     error = mc->mc_status;
1632421f2f7dSMike Smith 
1633421f2f7dSMike Smith  out:
1634421f2f7dSMike Smith     if (mc != NULL)
1635421f2f7dSMike Smith 	mlx_releasecmd(mc);
1636421f2f7dSMike Smith     return(error);
1637421f2f7dSMike Smith }
1638421f2f7dSMike Smith 
1639421f2f7dSMike Smith /********************************************************************************
1640421f2f7dSMike Smith  * Start a background rebuild of the physical drive at (channel),(target).
16411ac4b82bSMike Smith  *
16421ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
16431ac4b82bSMike Smith  * operation has started or been refused.
16441ac4b82bSMike Smith  */
16451ac4b82bSMike Smith static int
16461ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
16471ac4b82bSMike Smith {
16481ac4b82bSMike Smith     struct mlx_command	*mc;
16491ac4b82bSMike Smith     int			error;
16501ac4b82bSMike Smith 
1651da8bb3a3SMike Smith     debug_called(1);
16521ac4b82bSMike Smith 
16531ac4b82bSMike Smith     /* get ourselves a command buffer */
16541ac4b82bSMike Smith     error = 0x10000;
16551ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16561ac4b82bSMike Smith 	goto out;
16571ac4b82bSMike Smith     /* get a command slot */
16581ac4b82bSMike Smith     if (mlx_getslot(mc))
16591ac4b82bSMike Smith 	goto out;
16601ac4b82bSMike Smith 
1661421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
16621ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
16631ac4b82bSMike Smith 
1664421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1665421f2f7dSMike Smith     if (mlx_wait_command(mc))
16661ac4b82bSMike Smith 	goto out;
16671ac4b82bSMike Smith 
16681ac4b82bSMike Smith     /* command completed OK? */
16691ac4b82bSMike Smith     if (mc->mc_status != 0) {
16701ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
16711ac4b82bSMike Smith     } else {
1672421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
16731ac4b82bSMike Smith     }
16741ac4b82bSMike Smith     error = mc->mc_status;
16751ac4b82bSMike Smith 
16761ac4b82bSMike Smith  out:
16771ac4b82bSMike Smith     if (mc != NULL)
16781ac4b82bSMike Smith 	mlx_releasecmd(mc);
16791ac4b82bSMike Smith     return(error);
16801ac4b82bSMike Smith }
16811ac4b82bSMike Smith 
16821ac4b82bSMike Smith /********************************************************************************
16831ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
16841ac4b82bSMike Smith  *
16851ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
16861ac4b82bSMike Smith  */
16871ac4b82bSMike Smith static int
16881ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
16891ac4b82bSMike Smith {
16901ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16911ac4b82bSMike Smith     int			error, count;
16921ac4b82bSMike Smith 
1693da8bb3a3SMike Smith     debug_called(1);
16941ac4b82bSMike Smith 
16951ac4b82bSMike Smith     mc->mc_complete = NULL;
16961ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
16971ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
16981ac4b82bSMike Smith 	return(error);
16991ac4b82bSMike Smith 
17001ac4b82bSMike Smith     count = 0;
17011ac4b82bSMike Smith     /* XXX better timeout? */
17021ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
17031ac4b82bSMike Smith 	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
17041ac4b82bSMike Smith     }
17051ac4b82bSMike Smith 
17061ac4b82bSMike Smith     if (mc->mc_status != 0) {
1707da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
17081ac4b82bSMike Smith 	return(EIO);
17091ac4b82bSMike Smith     }
17101ac4b82bSMike Smith     return(0);
17111ac4b82bSMike Smith }
17121ac4b82bSMike Smith 
17131ac4b82bSMike Smith 
17141ac4b82bSMike Smith /********************************************************************************
17151ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
17161ac4b82bSMike Smith  *
1717da8bb3a3SMike Smith  * Should only be used when interrupts can't be relied upon. Returns 0 on
17181ac4b82bSMike Smith  * success, nonzero on error.
17191ac4b82bSMike Smith  * Successfully completed commands are dequeued.
17201ac4b82bSMike Smith  */
17211ac4b82bSMike Smith static int
17221ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
17231ac4b82bSMike Smith {
17241ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17251ac4b82bSMike Smith     int			error, count, s;
17261ac4b82bSMike Smith 
1727da8bb3a3SMike Smith     debug_called(1);
17281ac4b82bSMike Smith 
17291ac4b82bSMike Smith     mc->mc_complete = NULL;
17301ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
17311ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
17321ac4b82bSMike Smith 	return(error);
17331ac4b82bSMike Smith 
17341ac4b82bSMike Smith     count = 0;
17351ac4b82bSMike Smith     do {
17361ac4b82bSMike Smith 	/* poll for completion */
17371ac4b82bSMike Smith 	mlx_done(mc->mc_sc);
1738da8bb3a3SMike Smith 
1739da8bb3a3SMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
17401ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
17411ac4b82bSMike Smith 	s = splbio();
17424b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
17431ac4b82bSMike Smith 	splx(s);
17441ac4b82bSMike Smith 	return(0);
17451ac4b82bSMike Smith     }
1746421f2f7dSMike Smith     device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
17471ac4b82bSMike Smith     return(EIO);
17481ac4b82bSMike Smith }
17491ac4b82bSMike Smith 
17501b4404f9SScott Long void
17511b4404f9SScott Long mlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
17521b4404f9SScott Long {
17531b4404f9SScott Long     struct mlx_command	*mc;
17541b4404f9SScott Long     struct mlxd_softc	*mlxd;
17551b4404f9SScott Long     struct mlx_softc	*sc;
17561b4404f9SScott Long     mlx_bio		*bp;
17571b4404f9SScott Long     int			blkcount;
17581b4404f9SScott Long     int			driveno;
17591b4404f9SScott Long     int			cmd;
17601b4404f9SScott Long 
17611b4404f9SScott Long     mc = (struct mlx_command *)arg;
17621b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
17631b4404f9SScott Long 
17641b4404f9SScott Long     sc = mc->mc_sc;
17651b4404f9SScott Long     bp = mc->mc_private;
17661b4404f9SScott Long 
17671b4404f9SScott Long     if (MLX_BIO_IS_READ(bp)) {
17681b4404f9SScott Long 	mc->mc_flags |= MLX_CMD_DATAIN;
17691b4404f9SScott Long 	cmd = MLX_CMD_READSG;
17701b4404f9SScott Long     } else {
17711b4404f9SScott Long 	mc->mc_flags |= MLX_CMD_DATAOUT;
17721b4404f9SScott Long 	cmd = MLX_CMD_WRITESG;
17731b4404f9SScott Long     }
17741b4404f9SScott Long 
17751b4404f9SScott Long     /* build a suitable I/O command (assumes 512-byte rounded transfers) */
17761b4404f9SScott Long     mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp);
17771b4404f9SScott Long     driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
17781b4404f9SScott Long     blkcount = (MLX_BIO_LENGTH(bp) + MLX_BLKSIZE - 1) / MLX_BLKSIZE;
17791b4404f9SScott Long 
17801b4404f9SScott Long     if ((MLX_BIO_LBA(bp) + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
17811b4404f9SScott Long 	device_printf(sc->mlx_dev,
17821b4404f9SScott Long 		      "I/O beyond end of unit (%lld,%d > %lu)\n",
17831b4404f9SScott Long 		      (long long)MLX_BIO_LBA(bp), blkcount,
17841b4404f9SScott Long 		      (u_long)sc->mlx_sysdrive[driveno].ms_size);
17851b4404f9SScott Long 
17861b4404f9SScott Long     /*
17871b4404f9SScott Long      * Build the I/O command.  Note that the SG list type bits are set to zero,
17881b4404f9SScott Long      * denoting the format of SG list that we are using.
17891b4404f9SScott Long      */
17901b4404f9SScott Long     if (sc->mlx_iftype == MLX_IFTYPE_2) {
17911b4404f9SScott Long 	mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD :
17921b4404f9SScott Long 						      MLX_CMD_READSG_OLD,
17931b4404f9SScott Long 		       blkcount & 0xff, 	/* xfer length low byte */
17941b4404f9SScott Long 		       MLX_BIO_LBA(bp),		/* physical block number */
17951b4404f9SScott Long 		       driveno,			/* target drive number */
17961b4404f9SScott Long 		       mc->mc_sgphys,		/* location of SG list */
17971b4404f9SScott Long 		       mc->mc_nsgent & 0x3f);	/* size of SG list */
17981b4404f9SScott Long 	} else {
17991b4404f9SScott Long 	mlx_make_type5(mc, cmd,
18001b4404f9SScott Long 		       blkcount & 0xff, 	/* xfer length low byte */
18011b4404f9SScott Long 		       (driveno << 3) | ((blkcount >> 8) & 0x07),
18021b4404f9SScott Long 						/* target+length high 3 bits */
18031b4404f9SScott Long 		       MLX_BIO_LBA(bp),		/* physical block number */
18041b4404f9SScott Long 		       mc->mc_sgphys,		/* location of SG list */
18051b4404f9SScott Long 		       mc->mc_nsgent & 0x3f);	/* size of SG list */
18061b4404f9SScott Long     }
18071b4404f9SScott Long 
18081b4404f9SScott Long     /* try to give command to controller */
18091b4404f9SScott Long     if (mlx_start(mc) != 0) {
18101b4404f9SScott Long 	/* fail the command */
18111b4404f9SScott Long 	mc->mc_status = MLX_STATUS_WEDGED;
18121b4404f9SScott Long 	mlx_completeio(mc);
18131b4404f9SScott Long     }
18141b4404f9SScott Long }
18151b4404f9SScott Long 
18161ac4b82bSMike Smith /********************************************************************************
18171ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
18181ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
18191ac4b82bSMike Smith  *
18201ac4b82bSMike Smith  * Must be called at splbio or in an equivalent fashion that prevents
18218177437dSPoul-Henning Kamp  * reentry or activity on the bioq.
18221ac4b82bSMike Smith  */
18231ac4b82bSMike Smith static void
18241ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
18251ac4b82bSMike Smith {
18261ac4b82bSMike Smith     struct mlx_command	*mc;
182715fd5d22SMike Smith     mlx_bio		*bp;
18284b006d7bSMike Smith     int			s;
18291b4404f9SScott Long     int			error;
18301ac4b82bSMike Smith 
18315792b7feSMike Smith     /* avoid reentrancy */
18325792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_STARTING))
18335792b7feSMike Smith 	return;
18345792b7feSMike Smith 
18351ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
18364b006d7bSMike Smith     s = splbio();
18371ac4b82bSMike Smith     for (;;) {
18381ac4b82bSMike Smith 
18391ac4b82bSMike Smith 	/* see if there's work to be done */
184015fd5d22SMike Smith 	if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL)
18411ac4b82bSMike Smith 	    break;
18421ac4b82bSMike Smith 	/* get a command */
18431ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
18441ac4b82bSMike Smith 	    break;
18451ac4b82bSMike Smith 	/* get a slot for the command */
18461ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
18471ac4b82bSMike Smith 	    mlx_releasecmd(mc);
18481ac4b82bSMike Smith 	    break;
18491ac4b82bSMike Smith 	}
18501ac4b82bSMike Smith 	/* get the buf containing our work */
185115fd5d22SMike Smith 	MLX_BIO_QREMOVE(sc->mlx_bioq, bp);
18521ac4b82bSMike Smith 	sc->mlx_waitbufs--;
18534b006d7bSMike Smith 	splx(s);
18541ac4b82bSMike Smith 
18551ac4b82bSMike Smith 	/* connect the buf to the command */
18561ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
18571ac4b82bSMike Smith 	mc->mc_private = bp;
185815fd5d22SMike Smith 	mc->mc_data = MLX_BIO_DATA(bp);
185915fd5d22SMike Smith 	mc->mc_length = MLX_BIO_LENGTH(bp);
18601ac4b82bSMike Smith 
18611ac4b82bSMike Smith 	/* map the command so the controller can work with it */
18621b4404f9SScott Long 	error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
18631b4404f9SScott Long 				mc->mc_length, mlx_startio_cb, mc, 0);
18641b4404f9SScott Long 	if (error == EINPROGRESS) {
18651b4404f9SScott Long 		break;
1866da8bb3a3SMike Smith 	}
18671ac4b82bSMike Smith 
18684b006d7bSMike Smith 	s = splbio();
18691ac4b82bSMike Smith     }
18704b006d7bSMike Smith     splx(s);
18715792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_STARTING);
18721ac4b82bSMike Smith }
18731ac4b82bSMike Smith 
18741ac4b82bSMike Smith /********************************************************************************
18751ac4b82bSMike Smith  * Handle completion of an I/O command.
18761ac4b82bSMike Smith  */
18771ac4b82bSMike Smith static void
18781ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
18791ac4b82bSMike Smith {
18801ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
188115fd5d22SMike Smith     mlx_bio		*bp = (mlx_bio *)mc->mc_private;
188215fd5d22SMike Smith     struct mlxd_softc	*mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp);
18831ac4b82bSMike Smith 
18841ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
188515fd5d22SMike Smith 	MLX_BIO_SET_ERROR(bp, EIO);
18861ac4b82bSMike Smith 
18871ac4b82bSMike Smith 	switch(mc->mc_status) {
18881ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
18891ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1890f6b84b08SMike Smith 	    /* should signal this with a return code */
18911ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
18921ac4b82bSMike Smith 	    break;
18931ac4b82bSMike Smith 
18941ac4b82bSMike Smith 	default:				/* other I/O error */
18951ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
18961ac4b82bSMike Smith #if 0
1897cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
189815fd5d22SMike Smith 			  MLX_BIO_LENGTH(bp), MLX_BIO_LENGTH(bp) / MLX_BLKSIZE, MLX_BIO_LBA(bp));
18991ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
19001ac4b82bSMike Smith #endif
19011ac4b82bSMike Smith 	    break;
19021ac4b82bSMike Smith 	}
19031ac4b82bSMike Smith     }
19041ac4b82bSMike Smith     mlx_releasecmd(mc);
19051ac4b82bSMike Smith     mlxd_intr(bp);
19061ac4b82bSMike Smith }
19071ac4b82bSMike Smith 
19081b4404f9SScott Long void
19091b4404f9SScott Long mlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
19101b4404f9SScott Long {
19111b4404f9SScott Long     struct mlx_usercommand *mu;
19121b4404f9SScott Long     struct mlx_command *mc;
19131b4404f9SScott Long     struct mlx_dcdb	*dcdb;
19141b4404f9SScott Long 
19151b4404f9SScott Long     mc = (struct mlx_command *)arg;
19161b4404f9SScott Long     if (error)
19171b4404f9SScott Long 	return;
19181b4404f9SScott Long 
19191b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
19201b4404f9SScott Long 
19211b4404f9SScott Long     mu = (struct mlx_usercommand *)mc->mc_private;
19221b4404f9SScott Long     dcdb = NULL;
19231b4404f9SScott Long 
19241b4404f9SScott Long     /*
19251b4404f9SScott Long      * If this is a passthrough SCSI command, the DCDB is packed at the
19261b4404f9SScott Long      * beginning of the data area.  Fix up the DCDB to point to the correct
19271b4404f9SScott Long      * physical address and override any bufptr supplied by the caller since
19281b4404f9SScott Long      * we know what it's meant to be.
19291b4404f9SScott Long      */
19301b4404f9SScott Long     if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
19311b4404f9SScott Long 	dcdb = (struct mlx_dcdb *)mc->mc_data;
19321b4404f9SScott Long 	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
19331b4404f9SScott Long 	mu->mu_bufptr = 8;
19341b4404f9SScott Long     }
19351b4404f9SScott Long 
19361b4404f9SScott Long     /*
19371b4404f9SScott Long      * If there's a data buffer, fix up the command's buffer pointer.
19381b4404f9SScott Long      */
19391b4404f9SScott Long     if (mu->mu_datasize > 0) {
19401b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
19411b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
19421b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
19431b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
19441b4404f9SScott Long     }
19451b4404f9SScott Long     debug(0, "command fixup");
19461b4404f9SScott Long 
19471b4404f9SScott Long     /* submit the command and wait */
19481b4404f9SScott Long     if (mlx_wait_command(mc) != 0)
19491b4404f9SScott Long 	return;
19501b4404f9SScott Long 
19511b4404f9SScott Long }
19521b4404f9SScott Long 
19531ac4b82bSMike Smith /********************************************************************************
19541ac4b82bSMike Smith  * Take a command from user-space and try to run it.
1955da8bb3a3SMike Smith  *
1956da8bb3a3SMike Smith  * XXX Note that this can't perform very much in the way of error checking, and
1957da8bb3a3SMike Smith  *     as such, applications _must_ be considered trustworthy.
1958da8bb3a3SMike Smith  * XXX Commands using S/G for data are not supported.
19591ac4b82bSMike Smith  */
19601ac4b82bSMike Smith static int
19611ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
19621ac4b82bSMike Smith {
19631ac4b82bSMike Smith     struct mlx_command	*mc;
19641ac4b82bSMike Smith     void		*kbuf;
19651ac4b82bSMike Smith     int			error;
19661ac4b82bSMike Smith 
1967da8bb3a3SMike Smith     debug_called(0);
1968da8bb3a3SMike Smith 
19691ac4b82bSMike Smith     kbuf = NULL;
19701ac4b82bSMike Smith     mc = NULL;
19711ac4b82bSMike Smith     error = ENOMEM;
1972da8bb3a3SMike Smith 
1973da8bb3a3SMike Smith     /* get ourselves a command and copy in from user space */
19741ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
1975001ea8fbSSam Leffler 	return(error);
19761ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
1977da8bb3a3SMike Smith     debug(0, "got command buffer");
1978da8bb3a3SMike Smith 
19791b4404f9SScott Long     /*
19801b4404f9SScott Long      * if we need a buffer for data transfer, allocate one and copy in its
19811b4404f9SScott Long      * initial contents
19821b4404f9SScott Long      */
1983da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
1984aa083c3dSSam Leffler 	if (mu->mu_datasize > MAXPHYS) {
1985aa083c3dSSam Leffler 	    error = EINVAL;
1986aa083c3dSSam Leffler 	    goto out;
1987aa083c3dSSam Leffler 	}
1988a163d034SWarner Losh 	if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
1989da8bb3a3SMike Smith 	    (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))
19901ac4b82bSMike Smith 	    goto out;
1991da8bb3a3SMike Smith 	debug(0, "got kernel buffer");
1992da8bb3a3SMike Smith     }
19931ac4b82bSMike Smith 
19941ac4b82bSMike Smith     /* get a command slot */
19951ac4b82bSMike Smith     if (mlx_getslot(mc))
19961ac4b82bSMike Smith 	goto out;
1997da8bb3a3SMike Smith     debug(0, "got a slot");
19981ac4b82bSMike Smith 
1999da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
2000da8bb3a3SMike Smith 
2001da8bb3a3SMike Smith 	/* range check the pointer to physical buffer address */
20021b4404f9SScott Long 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) -
20031b4404f9SScott Long 						     sizeof(u_int32_t)))) {
2004da8bb3a3SMike Smith 	    error = EINVAL;
2005da8bb3a3SMike Smith 	    goto out;
2006da8bb3a3SMike Smith 	}
2007da8bb3a3SMike Smith     }
2008da8bb3a3SMike Smith 
20091b4404f9SScott Long     /* map the command so the controller can see it */
20101b4404f9SScott Long     mc->mc_data = kbuf;
20111b4404f9SScott Long     mc->mc_length = mu->mu_datasize;
20121b4404f9SScott Long     mc->mc_private = mu;
20131b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
20141b4404f9SScott Long 			    mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT);
20151ac4b82bSMike Smith 
20161ac4b82bSMike Smith     /* copy out status and data */
20171ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
20181b4404f9SScott Long     if ((mu->mu_datasize > 0) &&
20191b4404f9SScott Long 	((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
20201ac4b82bSMike Smith 	goto out;
20211b4404f9SScott Long 
20221ac4b82bSMike Smith     error = 0;
20231ac4b82bSMike Smith 
20241ac4b82bSMike Smith  out:
20251ac4b82bSMike Smith     mlx_releasecmd(mc);
20261ac4b82bSMike Smith     if (kbuf != NULL)
20271ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
20281ac4b82bSMike Smith     return(error);
20291ac4b82bSMike Smith }
20301ac4b82bSMike Smith 
20311ac4b82bSMike Smith /********************************************************************************
20321ac4b82bSMike Smith  ********************************************************************************
20331ac4b82bSMike Smith                                                         Command I/O to Controller
20341ac4b82bSMike Smith  ********************************************************************************
20351ac4b82bSMike Smith  ********************************************************************************/
20361ac4b82bSMike Smith 
20371ac4b82bSMike Smith /********************************************************************************
20381ac4b82bSMike Smith  * Find a free command slot for (mc).
20391ac4b82bSMike Smith  *
20401ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
20411ac4b82bSMike Smith  * 4 slots free for priority commands.
20421ac4b82bSMike Smith  */
20431ac4b82bSMike Smith static int
20441ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
20451ac4b82bSMike Smith {
20461ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
2047baff09dbSMike Smith     int			s, slot, limit;
20481ac4b82bSMike Smith 
2049da8bb3a3SMike Smith     debug_called(1);
20501ac4b82bSMike Smith 
2051baff09dbSMike Smith     /*
2052baff09dbSMike Smith      * Enforce slot-usage limit, if we have the required information.
2053baff09dbSMike Smith      */
2054baff09dbSMike Smith     if (sc->mlx_enq2 != NULL) {
2055baff09dbSMike Smith 	limit = sc->mlx_enq2->me_max_commands;
2056baff09dbSMike Smith     } else {
2057baff09dbSMike Smith 	limit = 2;
2058baff09dbSMike Smith     }
2059baff09dbSMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4))
20601ac4b82bSMike Smith 	return(EBUSY);
20611ac4b82bSMike Smith 
20621ac4b82bSMike Smith     /*
20631ac4b82bSMike Smith      * Allocate an outstanding command slot
20641ac4b82bSMike Smith      *
20651ac4b82bSMike Smith      * XXX linear search is slow
20661ac4b82bSMike Smith      */
20671ac4b82bSMike Smith     s = splbio();
2068baff09dbSMike Smith     for (slot = 0; slot < limit; slot++) {
2069da8bb3a3SMike Smith 	debug(2, "try slot %d", slot);
20701ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
20711ac4b82bSMike Smith 	    break;
20721ac4b82bSMike Smith     }
2073baff09dbSMike Smith     if (slot < limit) {
20741ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
20751ac4b82bSMike Smith 	sc->mlx_busycmds++;
20761ac4b82bSMike Smith     }
20771ac4b82bSMike Smith     splx(s);
20781ac4b82bSMike Smith 
20791ac4b82bSMike Smith     /* out of slots? */
2080baff09dbSMike Smith     if (slot >= limit)
20811ac4b82bSMike Smith 	return(EBUSY);
20821ac4b82bSMike Smith 
2083da8bb3a3SMike Smith     debug(2, "got slot %d", slot);
20841ac4b82bSMike Smith     mc->mc_slot = slot;
20851ac4b82bSMike Smith     return(0);
20861ac4b82bSMike Smith }
20871ac4b82bSMike Smith 
20881ac4b82bSMike Smith /********************************************************************************
20891ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
20901ac4b82bSMike Smith  */
20911ac4b82bSMike Smith static void
20921b4404f9SScott Long mlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments,
20931b4404f9SScott Long 		 int error)
20941ac4b82bSMike Smith {
20951ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20961ac4b82bSMike Smith     struct mlx_sgentry	*sg;
20971ac4b82bSMike Smith     int			i;
20981ac4b82bSMike Smith 
2099da8bb3a3SMike Smith     debug_called(1);
21001ac4b82bSMike Smith 
2101baff09dbSMike Smith     /* XXX should be unnecessary */
2102baff09dbSMike Smith     if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
21031b4404f9SScott Long 	panic("MLX: too many s/g segments (%d, max %d)", nsegments,
21041b4404f9SScott Long 	      sc->mlx_enq2->me_max_sg);
2105baff09dbSMike Smith 
21061ac4b82bSMike Smith     /* get base address of s/g table */
2107baff09dbSMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
21081ac4b82bSMike Smith 
21091ac4b82bSMike Smith     /* save s/g table information in command */
21101ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
21111b4404f9SScott Long     mc->mc_sgphys = sc->mlx_sgbusaddr +
21121b4404f9SScott Long 		   (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
21131ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
21141ac4b82bSMike Smith 
21151ac4b82bSMike Smith     /* populate s/g table */
21161ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
21171ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
21181ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
21191ac4b82bSMike Smith     }
21201ac4b82bSMike Smith 
21211b4404f9SScott Long     /* Make sure the buffers are visible on the bus. */
21221ac4b82bSMike Smith     if (mc->mc_flags & MLX_CMD_DATAIN)
21231b4404f9SScott Long 	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
21241b4404f9SScott Long 			BUS_DMASYNC_PREREAD);
21251ac4b82bSMike Smith     if (mc->mc_flags & MLX_CMD_DATAOUT)
21261b4404f9SScott Long 	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
21271b4404f9SScott Long 			BUS_DMASYNC_PREWRITE);
21281ac4b82bSMike Smith }
21291ac4b82bSMike Smith 
21301ac4b82bSMike Smith static void
21311ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
21321ac4b82bSMike Smith {
21331ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21341ac4b82bSMike Smith 
2135da8bb3a3SMike Smith     debug_called(1);
21361ac4b82bSMike Smith 
21371ac4b82bSMike Smith     /* if the command involved data at all */
21381ac4b82bSMike Smith     if (mc->mc_data != NULL) {
21391ac4b82bSMike Smith 
21401ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
21411ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
21421ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
21431ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
21441ac4b82bSMike Smith 
21451ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
21461ac4b82bSMike Smith     }
21471ac4b82bSMike Smith }
21481ac4b82bSMike Smith 
21491ac4b82bSMike Smith /********************************************************************************
21505792b7feSMike Smith  * Try to deliver (mc) to the controller.
21511ac4b82bSMike Smith  *
21521ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
21531ac4b82bSMike Smith  */
21541ac4b82bSMike Smith static int
21551ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
21561ac4b82bSMike Smith {
21571ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21585792b7feSMike Smith     int			i, s, done;
21591ac4b82bSMike Smith 
2160da8bb3a3SMike Smith     debug_called(1);
21611ac4b82bSMike Smith 
21621ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
21631ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
21641ac4b82bSMike Smith 
21654b006d7bSMike Smith     /* mark the command as currently being processed */
21661ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
21671ac4b82bSMike Smith 
21685792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
21695792b7feSMike Smith     mc->mc_timeout = time_second + 60;
21701ac4b82bSMike Smith 
21711ac4b82bSMike Smith     /* spin waiting for the mailbox */
21721ac4b82bSMike Smith     for (i = 100000, done = 0; (i > 0) && !done; i--) {
21731ac4b82bSMike Smith 	s = splbio();
21744b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
21754b006d7bSMike Smith 	    done = 1;
21764b006d7bSMike Smith 	    /* move command to work queue */
21774b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
21784b006d7bSMike Smith 	}
21795792b7feSMike Smith 	splx(s);	/* drop spl to allow completion interrupts */
21801ac4b82bSMike Smith     }
21811ac4b82bSMike Smith 
21821ac4b82bSMike Smith     /* command is enqueued */
21831ac4b82bSMike Smith     if (done)
21841ac4b82bSMike Smith 	return(0);
21851ac4b82bSMike Smith 
21861ac4b82bSMike Smith     /*
21871ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
21881ac4b82bSMike Smith      * that the command was given and return it with a bad status.
21891ac4b82bSMike Smith      */
21901ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
21911ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
21921ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
21935792b7feSMike Smith     mlx_complete(sc);
21941ac4b82bSMike Smith     return(EIO);
21951ac4b82bSMike Smith }
21961ac4b82bSMike Smith 
21971ac4b82bSMike Smith /********************************************************************************
21985792b7feSMike Smith  * Poll the controller (sc) for completed commands.
21995792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
22005792b7feSMike Smith  * new commands may be posted.
22011ac4b82bSMike Smith  *
22025792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
22031ac4b82bSMike Smith  */
22041ac4b82bSMike Smith static int
22051ac4b82bSMike Smith mlx_done(struct mlx_softc *sc)
22061ac4b82bSMike Smith {
22071ac4b82bSMike Smith     struct mlx_command	*mc;
22085792b7feSMike Smith     int			s, result;
22091ac4b82bSMike Smith     u_int8_t		slot;
22101ac4b82bSMike Smith     u_int16_t		status;
22111ac4b82bSMike Smith 
2212da8bb3a3SMike Smith     debug_called(2);
22131ac4b82bSMike Smith 
22145792b7feSMike Smith     result = 0;
22151ac4b82bSMike Smith 
22165792b7feSMike Smith     /* loop collecting completed commands */
22174b006d7bSMike Smith     s = splbio();
22185792b7feSMike Smith     for (;;) {
22195792b7feSMike Smith 	/* poll for a completed command's identifier and status */
22201ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
22215792b7feSMike Smith 	    result = 1;
22221ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
22231ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
22241ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
22251ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
22261ac4b82bSMike Smith 
22271ac4b82bSMike Smith 		    /* free slot for reuse */
22281ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
22291ac4b82bSMike Smith 		    sc->mlx_busycmds--;
22301ac4b82bSMike Smith 		} else {
22311ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
22321ac4b82bSMike Smith 		}
22331ac4b82bSMike Smith 	    } else {
22341ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
22351ac4b82bSMike Smith 	    }
22365792b7feSMike Smith 	} else {
22375792b7feSMike Smith 	    break;
22381ac4b82bSMike Smith 	}
22395792b7feSMike Smith     }
2240baff09dbSMike Smith     splx(s);
22411ac4b82bSMike Smith 
22425792b7feSMike Smith     /* if we've completed any commands, try posting some more */
22435792b7feSMike Smith     if (result)
22445792b7feSMike Smith 	mlx_startio(sc);
22455792b7feSMike Smith 
22465792b7feSMike Smith     /* handle completion and timeouts */
22475792b7feSMike Smith     mlx_complete(sc);
22485792b7feSMike Smith 
22495792b7feSMike Smith     return(result);
22501ac4b82bSMike Smith }
22511ac4b82bSMike Smith 
22521ac4b82bSMike Smith /********************************************************************************
22535792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
22541ac4b82bSMike Smith  */
22551ac4b82bSMike Smith static void
22561ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
22571ac4b82bSMike Smith {
22581ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
22591ac4b82bSMike Smith     int			s, count;
22601ac4b82bSMike Smith 
2261da8bb3a3SMike Smith     debug_called(2);
22621ac4b82bSMike Smith 
22635792b7feSMike Smith     /* avoid reentrancy  XXX might want to signal and request a restart */
22645792b7feSMike Smith     if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
22655792b7feSMike Smith 	return;
22665792b7feSMike Smith 
22671ac4b82bSMike Smith     s = splbio();
22681ac4b82bSMike Smith     count = 0;
22691ac4b82bSMike Smith 
22705792b7feSMike Smith     /* scan the list of busy/done commands */
22714b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
22721ac4b82bSMike Smith     while (mc != NULL) {
22731ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
22741ac4b82bSMike Smith 
22755792b7feSMike Smith 	/* Command has been completed in some fashion */
22764b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
22774b006d7bSMike Smith 
22785792b7feSMike Smith 	    /* unmap the command's data buffer */
22795792b7feSMike Smith 	    mlx_unmapcmd(mc);
22801ac4b82bSMike Smith 	    /*
22811ac4b82bSMike Smith 	     * Does the command have a completion handler?
22821ac4b82bSMike Smith 	     */
22831ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
22841ac4b82bSMike Smith 		/* remove from list and give to handler */
22854b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
22861ac4b82bSMike Smith 		mc->mc_complete(mc);
22871ac4b82bSMike Smith 
22881ac4b82bSMike Smith 		/*
22891ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
22901ac4b82bSMike Smith 		 */
22911ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
22921ac4b82bSMike Smith 
22931ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
22944b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
22951ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
22961ac4b82bSMike Smith 
22971ac4b82bSMike Smith 		/*
22981ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
22991ac4b82bSMike Smith 		 */
23001ac4b82bSMike Smith 	    } else {
23011ac4b82bSMike Smith 	    }
23024b006d7bSMike Smith 	}
23031ac4b82bSMike Smith 	mc = nc;
23041ac4b82bSMike Smith     }
23051ac4b82bSMike Smith     splx(s);
23061ac4b82bSMike Smith 
23075792b7feSMike Smith     mlx_lock_clr(sc, MLX_LOCK_COMPLETING);
23081ac4b82bSMike Smith }
23091ac4b82bSMike Smith 
23101ac4b82bSMike Smith /********************************************************************************
23111ac4b82bSMike Smith  ********************************************************************************
23121ac4b82bSMike Smith                                                         Command Buffer Management
23131ac4b82bSMike Smith  ********************************************************************************
23141ac4b82bSMike Smith  ********************************************************************************/
23151ac4b82bSMike Smith 
23161ac4b82bSMike Smith /********************************************************************************
23171ac4b82bSMike Smith  * Get a new command buffer.
23181ac4b82bSMike Smith  *
23191ac4b82bSMike Smith  * This may return NULL in low-memory cases.
23201ac4b82bSMike Smith  *
23211ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
23221ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
23231ac4b82bSMike Smith  *
23241ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
23251ac4b82bSMike Smith  *
23261ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
23271ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
23281ac4b82bSMike Smith  *     using a buffer.
23291ac4b82bSMike Smith  */
23301ac4b82bSMike Smith static struct mlx_command *
23311ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
23321ac4b82bSMike Smith {
23331ac4b82bSMike Smith     struct mlx_command	*mc;
23341ac4b82bSMike Smith     int			error;
23351ac4b82bSMike Smith     int			s;
23361ac4b82bSMike Smith 
2337da8bb3a3SMike Smith     debug_called(1);
23381ac4b82bSMike Smith 
23391ac4b82bSMike Smith     s = splbio();
23401ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
23411ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
23421ac4b82bSMike Smith     splx(s);
23431ac4b82bSMike Smith 
23441ac4b82bSMike Smith     /* allocate a new command buffer? */
23451ac4b82bSMike Smith     if (mc == NULL) {
2346ca89ee27SDavid Malone 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO);
23471ac4b82bSMike Smith 	if (mc != NULL) {
23481ac4b82bSMike Smith 	    mc->mc_sc = sc;
23491ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
23501ac4b82bSMike Smith 	    if (error) {
23511ac4b82bSMike Smith 		free(mc, M_DEVBUF);
23521ac4b82bSMike Smith 		return(NULL);
23531ac4b82bSMike Smith 	    }
23541ac4b82bSMike Smith 	}
23551ac4b82bSMike Smith     }
23561ac4b82bSMike Smith     return(mc);
23571ac4b82bSMike Smith }
23581ac4b82bSMike Smith 
23591ac4b82bSMike Smith /********************************************************************************
23601ac4b82bSMike Smith  * Release a command buffer for recycling.
23611ac4b82bSMike Smith  *
23621ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
23631ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
23641ac4b82bSMike Smith  */
23651ac4b82bSMike Smith static void
23661ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
23671ac4b82bSMike Smith {
23681ac4b82bSMike Smith     int		s;
23691ac4b82bSMike Smith 
2370da8bb3a3SMike Smith     debug_called(1);
23711ac4b82bSMike Smith 
23721ac4b82bSMike Smith     s = splbio();
23731ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
23741ac4b82bSMike Smith     splx(s);
23751ac4b82bSMike Smith }
23761ac4b82bSMike Smith 
23771ac4b82bSMike Smith /********************************************************************************
23781ac4b82bSMike Smith  * Permanently discard a command buffer.
23791ac4b82bSMike Smith  */
23801ac4b82bSMike Smith static void
23811ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
23821ac4b82bSMike Smith {
23831ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
23841ac4b82bSMike Smith 
2385da8bb3a3SMike Smith     debug_called(1);
23861ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
23871ac4b82bSMike Smith     free(mc, M_DEVBUF);
23881ac4b82bSMike Smith }
23891ac4b82bSMike Smith 
23901ac4b82bSMike Smith 
23911ac4b82bSMike Smith /********************************************************************************
23921ac4b82bSMike Smith  ********************************************************************************
23931ac4b82bSMike Smith                                                 Type 3 interface accessor methods
23941ac4b82bSMike Smith  ********************************************************************************
23951ac4b82bSMike Smith  ********************************************************************************/
23961ac4b82bSMike Smith 
23971ac4b82bSMike Smith /********************************************************************************
23981ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
23991ac4b82bSMike Smith  * (the controller is not ready to take a command).
24001ac4b82bSMike Smith  *
24011ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
24021ac4b82bSMike Smith  */
24031ac4b82bSMike Smith static int
24041ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
24051ac4b82bSMike Smith {
24061ac4b82bSMike Smith     int		i;
24071ac4b82bSMike Smith 
2408da8bb3a3SMike Smith     debug_called(2);
24091ac4b82bSMike Smith 
24101ac4b82bSMike Smith     /* ready for our command? */
24111ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
24121ac4b82bSMike Smith 	/* copy mailbox data to window */
24131ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
24141ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
24151ac4b82bSMike Smith 
24161ac4b82bSMike Smith 	/* post command */
2417f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
24181ac4b82bSMike Smith 	return(1);
24191ac4b82bSMike Smith     }
24201ac4b82bSMike Smith     return(0);
24211ac4b82bSMike Smith }
24221ac4b82bSMike Smith 
24231ac4b82bSMike Smith /********************************************************************************
24241ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
24251ac4b82bSMike Smith  * and recover the slot number and status code.
24261ac4b82bSMike Smith  *
24271ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
24281ac4b82bSMike Smith  */
24291ac4b82bSMike Smith static int
24301ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
24311ac4b82bSMike Smith {
24321ac4b82bSMike Smith 
2433da8bb3a3SMike Smith     debug_called(2);
24341ac4b82bSMike Smith 
24351ac4b82bSMike Smith     /* status available? */
24361ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
24371ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
24381ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
24391ac4b82bSMike Smith 
24401ac4b82bSMike Smith 	/* acknowledge completion */
2441f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2442f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
24431ac4b82bSMike Smith 	return(1);
24441ac4b82bSMike Smith     }
24451ac4b82bSMike Smith     return(0);
24461ac4b82bSMike Smith }
24471ac4b82bSMike Smith 
24481ac4b82bSMike Smith /********************************************************************************
24491ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
24501ac4b82bSMike Smith  *
24511ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
24521ac4b82bSMike Smith  */
24531ac4b82bSMike Smith static void
24541ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
24551ac4b82bSMike Smith {
2456da8bb3a3SMike Smith     debug_called(1);
24571ac4b82bSMike Smith 
24581ac4b82bSMike Smith     switch(action) {
24591ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
24601ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
24611ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
24621ac4b82bSMike Smith 	break;
24631ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
24641ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
24651ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
24661ac4b82bSMike Smith 	break;
24671ac4b82bSMike Smith     }
24681ac4b82bSMike Smith }
24691ac4b82bSMike Smith 
2470da8bb3a3SMike Smith /********************************************************************************
2471da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2472da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2473da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2474da8bb3a3SMike Smith  */
2475da8bb3a3SMike Smith static int
2476da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2477da8bb3a3SMike Smith {
2478da8bb3a3SMike Smith     u_int8_t	fwerror;
2479da8bb3a3SMike Smith     static int	initted = 0;
2480da8bb3a3SMike Smith 
2481da8bb3a3SMike Smith     debug_called(2);
2482da8bb3a3SMike Smith 
2483da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2484da8bb3a3SMike Smith     if (!initted) {
2485da8bb3a3SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
2486da8bb3a3SMike Smith 	DELAY(1000);
2487da8bb3a3SMike Smith 	initted = 1;
2488da8bb3a3SMike Smith     }
2489da8bb3a3SMike Smith 
2490da8bb3a3SMike Smith     /* init in progress? */
2491da8bb3a3SMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
2492da8bb3a3SMike Smith 	return(0);
2493da8bb3a3SMike Smith 
2494da8bb3a3SMike Smith     /* test error value */
2495da8bb3a3SMike Smith     fwerror = MLX_V3_GET_FWERROR(sc);
2496da8bb3a3SMike Smith     if (!(fwerror & MLX_V3_FWERROR_PEND))
2497da8bb3a3SMike Smith 	return(1);
2498da8bb3a3SMike Smith 
2499da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2500da8bb3a3SMike Smith     *error = fwerror & ~MLX_V3_FWERROR_PEND;
2501da8bb3a3SMike Smith     *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
2502da8bb3a3SMike Smith     *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
2503da8bb3a3SMike Smith 
2504da8bb3a3SMike Smith     /* acknowledge */
2505da8bb3a3SMike Smith     MLX_V3_PUT_FWERROR(sc, 0);
2506da8bb3a3SMike Smith 
2507da8bb3a3SMike Smith     return(2);
2508da8bb3a3SMike Smith }
25091ac4b82bSMike Smith 
25101ac4b82bSMike Smith /********************************************************************************
25111ac4b82bSMike Smith  ********************************************************************************
2512f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2513f6b84b08SMike Smith  ********************************************************************************
2514f6b84b08SMike Smith  ********************************************************************************/
2515f6b84b08SMike Smith 
2516f6b84b08SMike Smith /********************************************************************************
2517f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2518f6b84b08SMike Smith  * (the controller is not ready to take a command).
2519f6b84b08SMike Smith  *
2520f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2521f6b84b08SMike Smith  */
2522f6b84b08SMike Smith static int
2523f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2524f6b84b08SMike Smith {
2525f6b84b08SMike Smith     int		i;
2526f6b84b08SMike Smith 
2527da8bb3a3SMike Smith     debug_called(2);
2528f6b84b08SMike Smith 
2529f6b84b08SMike Smith     /* ready for our command? */
2530f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2531f6b84b08SMike Smith 	/* copy mailbox data to window */
2532f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2533f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2534f6b84b08SMike Smith 
2535da8bb3a3SMike Smith 	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
2536da8bb3a3SMike Smith 	bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
2537da8bb3a3SMike Smith 			  BUS_SPACE_BARRIER_WRITE);
2538da8bb3a3SMike Smith 
2539f6b84b08SMike Smith 	/* post command */
2540f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2541f6b84b08SMike Smith 	return(1);
2542f6b84b08SMike Smith     }
2543f6b84b08SMike Smith     return(0);
2544f6b84b08SMike Smith }
2545f6b84b08SMike Smith 
2546f6b84b08SMike Smith /********************************************************************************
2547f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2548f6b84b08SMike Smith  * and recover the slot number and status code.
2549f6b84b08SMike Smith  *
2550f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2551f6b84b08SMike Smith  */
2552f6b84b08SMike Smith static int
2553f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2554f6b84b08SMike Smith {
2555f6b84b08SMike Smith 
2556da8bb3a3SMike Smith     debug_called(2);
2557f6b84b08SMike Smith 
2558f6b84b08SMike Smith     /* status available? */
2559f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2560f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2561f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2562f6b84b08SMike Smith 
2563f6b84b08SMike Smith 	/* acknowledge completion */
2564f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2565f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2566f6b84b08SMike Smith 	return(1);
2567f6b84b08SMike Smith     }
2568f6b84b08SMike Smith     return(0);
2569f6b84b08SMike Smith }
2570f6b84b08SMike Smith 
2571f6b84b08SMike Smith /********************************************************************************
2572f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2573f6b84b08SMike Smith  *
2574f6b84b08SMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
2575f6b84b08SMike Smith  */
2576f6b84b08SMike Smith static void
2577f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2578f6b84b08SMike Smith {
2579da8bb3a3SMike Smith     debug_called(1);
2580f6b84b08SMike Smith 
2581f6b84b08SMike Smith     switch(action) {
2582f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2583f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2584f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2585f6b84b08SMike Smith 	break;
2586f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2587f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2588f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2589f6b84b08SMike Smith 	break;
2590f6b84b08SMike Smith     }
2591f6b84b08SMike Smith }
2592f6b84b08SMike Smith 
2593da8bb3a3SMike Smith /********************************************************************************
2594da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2595da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2596da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2597da8bb3a3SMike Smith  */
2598da8bb3a3SMike Smith static int
2599da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2600da8bb3a3SMike Smith {
2601da8bb3a3SMike Smith     u_int8_t	fwerror;
2602da8bb3a3SMike Smith     static int	initted = 0;
2603da8bb3a3SMike Smith 
2604da8bb3a3SMike Smith     debug_called(2);
2605da8bb3a3SMike Smith 
2606da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2607da8bb3a3SMike Smith     if (!initted) {
2608da8bb3a3SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2609da8bb3a3SMike Smith 	DELAY(1000);
2610da8bb3a3SMike Smith 	initted = 1;
2611da8bb3a3SMike Smith     }
2612da8bb3a3SMike Smith 
2613da8bb3a3SMike Smith     /* init in progress? */
2614da8bb3a3SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
2615da8bb3a3SMike Smith 	return(0);
2616da8bb3a3SMike Smith 
2617da8bb3a3SMike Smith     /* test error value */
2618da8bb3a3SMike Smith     fwerror = MLX_V4_GET_FWERROR(sc);
2619da8bb3a3SMike Smith     if (!(fwerror & MLX_V4_FWERROR_PEND))
2620da8bb3a3SMike Smith 	return(1);
2621da8bb3a3SMike Smith 
2622da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2623da8bb3a3SMike Smith     *error = fwerror & ~MLX_V4_FWERROR_PEND;
2624da8bb3a3SMike Smith     *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
2625da8bb3a3SMike Smith     *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
2626da8bb3a3SMike Smith 
2627da8bb3a3SMike Smith     /* acknowledge */
2628da8bb3a3SMike Smith     MLX_V4_PUT_FWERROR(sc, 0);
2629da8bb3a3SMike Smith 
2630da8bb3a3SMike Smith     return(2);
2631da8bb3a3SMike Smith }
2632f6b84b08SMike Smith 
2633f6b84b08SMike Smith /********************************************************************************
2634f6b84b08SMike Smith  ********************************************************************************
26355792b7feSMike Smith                                                 Type 5 interface accessor methods
26365792b7feSMike Smith  ********************************************************************************
26375792b7feSMike Smith  ********************************************************************************/
26385792b7feSMike Smith 
26395792b7feSMike Smith /********************************************************************************
26405792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
26415792b7feSMike Smith  * (the controller is not ready to take a command).
26425792b7feSMike Smith  *
26435792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
26445792b7feSMike Smith  */
26455792b7feSMike Smith static int
26465792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
26475792b7feSMike Smith {
26485792b7feSMike Smith     int		i;
26495792b7feSMike Smith 
2650da8bb3a3SMike Smith     debug_called(2);
26515792b7feSMike Smith 
26525792b7feSMike Smith     /* ready for our command? */
26535792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
26545792b7feSMike Smith 	/* copy mailbox data to window */
26555792b7feSMike Smith 	for (i = 0; i < 13; i++)
26565792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
26575792b7feSMike Smith 
26585792b7feSMike Smith 	/* post command */
26595792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
26605792b7feSMike Smith 	return(1);
26615792b7feSMike Smith     }
26625792b7feSMike Smith     return(0);
26635792b7feSMike Smith }
26645792b7feSMike Smith 
26655792b7feSMike Smith /********************************************************************************
26665792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
26675792b7feSMike Smith  * and recover the slot number and status code.
26685792b7feSMike Smith  *
26695792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
26705792b7feSMike Smith  */
26715792b7feSMike Smith static int
26725792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
26735792b7feSMike Smith {
26745792b7feSMike Smith 
2675da8bb3a3SMike Smith     debug_called(2);
26765792b7feSMike Smith 
26775792b7feSMike Smith     /* status available? */
26785792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
26795792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
26805792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
26815792b7feSMike Smith 
26825792b7feSMike Smith 	/* acknowledge completion */
26835792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
26845792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
26855792b7feSMike Smith 	return(1);
26865792b7feSMike Smith     }
26875792b7feSMike Smith     return(0);
26885792b7feSMike Smith }
26895792b7feSMike Smith 
26905792b7feSMike Smith /********************************************************************************
26915792b7feSMike Smith  * Enable/disable interrupts as requested.
26925792b7feSMike Smith  *
26935792b7feSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
26945792b7feSMike Smith  */
26955792b7feSMike Smith static void
26965792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
26975792b7feSMike Smith {
2698da8bb3a3SMike Smith     debug_called(1);
26995792b7feSMike Smith 
27005792b7feSMike Smith     switch(action) {
27015792b7feSMike Smith     case MLX_INTACTION_DISABLE:
2702da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
27035792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
27045792b7feSMike Smith 	break;
27055792b7feSMike Smith     case MLX_INTACTION_ENABLE:
2706da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
27075792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
27085792b7feSMike Smith 	break;
27095792b7feSMike Smith     }
27105792b7feSMike Smith }
27115792b7feSMike Smith 
2712da8bb3a3SMike Smith /********************************************************************************
2713da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2714da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2715da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2716da8bb3a3SMike Smith  */
2717da8bb3a3SMike Smith static int
2718da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
2719da8bb3a3SMike Smith {
2720da8bb3a3SMike Smith     u_int8_t	fwerror;
2721da8bb3a3SMike Smith     static int	initted = 0;
2722da8bb3a3SMike Smith 
2723da8bb3a3SMike Smith     debug_called(2);
2724da8bb3a3SMike Smith 
2725da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
2726da8bb3a3SMike Smith     if (!initted) {
2727da8bb3a3SMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
2728da8bb3a3SMike Smith 	DELAY(1000);
2729da8bb3a3SMike Smith 	initted = 1;
2730da8bb3a3SMike Smith     }
2731da8bb3a3SMike Smith 
2732da8bb3a3SMike Smith     /* init in progress? */
2733da8bb3a3SMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
2734da8bb3a3SMike Smith 	return(0);
2735da8bb3a3SMike Smith 
2736da8bb3a3SMike Smith     /* test for error value */
2737da8bb3a3SMike Smith     fwerror = MLX_V5_GET_FWERROR(sc);
2738da8bb3a3SMike Smith     if (!(fwerror & MLX_V5_FWERROR_PEND))
2739da8bb3a3SMike Smith 	return(1);
2740da8bb3a3SMike Smith 
2741da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2742da8bb3a3SMike Smith     *error = fwerror & ~MLX_V5_FWERROR_PEND;
2743da8bb3a3SMike Smith     *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
2744da8bb3a3SMike Smith     *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
2745da8bb3a3SMike Smith 
2746da8bb3a3SMike Smith     /* acknowledge */
2747da8bb3a3SMike Smith     MLX_V5_PUT_FWERROR(sc, 0xff);
2748da8bb3a3SMike Smith 
2749da8bb3a3SMike Smith     return(2);
2750da8bb3a3SMike Smith }
27515792b7feSMike Smith 
27525792b7feSMike Smith /********************************************************************************
27535792b7feSMike Smith  ********************************************************************************
27541ac4b82bSMike Smith                                                                         Debugging
27551ac4b82bSMike Smith  ********************************************************************************
27561ac4b82bSMike Smith  ********************************************************************************/
27571ac4b82bSMike Smith 
27581ac4b82bSMike Smith /********************************************************************************
27591ac4b82bSMike Smith  * Return a status message describing (mc)
27601ac4b82bSMike Smith  */
27611ac4b82bSMike Smith static char *mlx_status_messages[] = {
27621ac4b82bSMike Smith     "normal completion",			/* 00 */
27631ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
27641ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
27651ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
27661ac4b82bSMike Smith     "bad data encountered",			/* 04 */
27671ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
27681ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
27691ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
27701ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
27711ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
27721ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
27731ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
27741ac4b82bSMike Smith     "channel is busy",				/* 12 */
27751ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
2776da8bb3a3SMike Smith     "rebuild successfully terminated",		/* 14 */
2777da8bb3a3SMike Smith     "unsupported command",			/* 15 */
2778da8bb3a3SMike Smith     "check condition received",			/* 16 */
2779da8bb3a3SMike Smith     "device is busy",				/* 17 */
2780da8bb3a3SMike Smith     "selection or command timeout",		/* 18 */
2781da8bb3a3SMike Smith     "command terminated abnormally",		/* 19 */
2782da8bb3a3SMike Smith     ""
27831ac4b82bSMike Smith };
27841ac4b82bSMike Smith 
27851ac4b82bSMike Smith static struct
27861ac4b82bSMike Smith {
27871ac4b82bSMike Smith     int		command;
27881ac4b82bSMike Smith     u_int16_t	status;
27891ac4b82bSMike Smith     int		msg;
27901ac4b82bSMike Smith } mlx_messages[] = {
2791da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0001,	 1},
2792da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0002,	 1},
2793da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0105,	 3},
2794da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x010c,	 4},
2795da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0001,	 1},
2796da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0002,	 1},
2797da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0105,	 3},
2798da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0001,	 1},
2799da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0002,	 1},
2800da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0105,	 3},
2801da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
2802da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
2803da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
28041ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
28051ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
28061ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
28071ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
28081ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
2809da8bb3a3SMike Smith     {MLX_CMD_REBUILDASYNC,	0x0107, 14},
28101ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
28111ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
28121ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
28131ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
28141ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
28151ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
28161ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
2817da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0002, 16},
2818da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0008, 17},
2819da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000e, 18},
2820da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000f, 19},
2821da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0105,  8},
2822da8bb3a3SMike Smith 
2823da8bb3a3SMike Smith     {0,				0x0104, 14},
28241ac4b82bSMike Smith     {-1, 0, 0}
28251ac4b82bSMike Smith };
28261ac4b82bSMike Smith 
28271ac4b82bSMike Smith static char *
28281ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
28291ac4b82bSMike Smith {
28301ac4b82bSMike Smith     static char	unkmsg[80];
28311ac4b82bSMike Smith     int		i;
28321ac4b82bSMike Smith 
28331ac4b82bSMike Smith     /* look up message in table */
28341ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
2835da8bb3a3SMike Smith 	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
2836466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
28371ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
28381ac4b82bSMike Smith 
28391ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
28401ac4b82bSMike Smith     return(unkmsg);
28411ac4b82bSMike Smith }
28421ac4b82bSMike Smith 
28431ac4b82bSMike Smith /*******************************************************************************
2844da8bb3a3SMike Smith  * Print a string describing the controller (sc)
28451ac4b82bSMike Smith  */
28465792b7feSMike Smith static struct
28475792b7feSMike Smith {
28485792b7feSMike Smith     int		hwid;
28495792b7feSMike Smith     char	*name;
28505792b7feSMike Smith } mlx_controller_names[] = {
28515792b7feSMike Smith     {0x01,	"960P/PD"},
28525792b7feSMike Smith     {0x02,	"960PL"},
28535792b7feSMike Smith     {0x10,	"960PG"},
28545792b7feSMike Smith     {0x11,	"960PJ"},
28559eee27f1SMike Smith     {0x12,	"960PR"},
28569eee27f1SMike Smith     {0x13,	"960PT"},
28579eee27f1SMike Smith     {0x14,	"960PTL0"},
28589eee27f1SMike Smith     {0x15,	"960PRL"},
28599eee27f1SMike Smith     {0x16,	"960PTL1"},
28609eee27f1SMike Smith     {0x20,	"1164PVX"},
28615792b7feSMike Smith     {-1, NULL}
28625792b7feSMike Smith };
28635792b7feSMike Smith 
28649eee27f1SMike Smith static void
28659eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
28661ac4b82bSMike Smith {
28671ac4b82bSMike Smith     static char		buf[80];
28685792b7feSMike Smith     char		*model;
28699eee27f1SMike Smith     int			i;
28701ac4b82bSMike Smith 
28715792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
28729eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
28735792b7feSMike Smith 	    model = mlx_controller_names[i].name;
28741ac4b82bSMike Smith 	    break;
28751ac4b82bSMike Smith 	}
28765792b7feSMike Smith     }
28775792b7feSMike Smith     if (model == NULL) {
28789eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
28795792b7feSMike Smith 	model = buf;
28805792b7feSMike Smith     }
2881da8bb3a3SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
28829eee27f1SMike Smith 		  model,
28839eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
28849eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
28859eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
28869eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
28879eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2888b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
28899eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
28909eee27f1SMike Smith 
28919eee27f1SMike Smith     if (bootverbose) {
28929eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
28939eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
28949eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
28959eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
28969eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
28979eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
28989eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
28999eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
29009eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
29019eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
29029eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
29039eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
29049eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
29059eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
29069eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
29079eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
29089eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
29099eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
29109eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
29119eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
29129eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
29139eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
29149eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
29159eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
29169eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
29179eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
29189eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
29199eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
29209eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
29219eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
29229eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
29239eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
29249eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
29259eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
29269eee27f1SMike Smith 
29279eee27f1SMike Smith     }
29281ac4b82bSMike Smith }
29291ac4b82bSMike Smith 
2930da8bb3a3SMike Smith /*******************************************************************************
2931da8bb3a3SMike Smith  * Emit a string describing the firmware handshake status code, and return a flag
2932da8bb3a3SMike Smith  * indicating whether the code represents a fatal error.
2933da8bb3a3SMike Smith  *
2934da8bb3a3SMike Smith  * Error code interpretations are from the Linux driver, and don't directly match
2935da8bb3a3SMike Smith  * the messages printed by Mylex's BIOS.  This may change if documentation on the
2936da8bb3a3SMike Smith  * codes is forthcoming.
2937da8bb3a3SMike Smith  */
2938da8bb3a3SMike Smith static int
2939da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
2940da8bb3a3SMike Smith {
2941da8bb3a3SMike Smith     switch(error) {
2942da8bb3a3SMike Smith     case 0x00:
2943da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
2944da8bb3a3SMike Smith 	break;
2945da8bb3a3SMike Smith     case 0x08:
2946da8bb3a3SMike Smith 	/* we could be neater about this and give some indication when we receive more of them */
2947da8bb3a3SMike Smith 	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
2948da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "spinning up drives...\n");
2949da8bb3a3SMike Smith 	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
2950da8bb3a3SMike Smith 	}
2951da8bb3a3SMike Smith 	break;
2952da8bb3a3SMike Smith     case 0x30:
2953da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "configuration checksum error\n");
2954da8bb3a3SMike Smith 	break;
2955da8bb3a3SMike Smith     case 0x60:
2956da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
2957da8bb3a3SMike Smith 	break;
2958da8bb3a3SMike Smith     case 0x70:
2959da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
2960da8bb3a3SMike Smith 	break;
2961da8bb3a3SMike Smith     case 0x90:
2962da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
2963da8bb3a3SMike Smith 	break;
2964da8bb3a3SMike Smith     case 0xa0:
2965da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
2966da8bb3a3SMike Smith 	break;
2967da8bb3a3SMike Smith     case 0xb0:
2968da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
2969da8bb3a3SMike Smith 	break;
2970da8bb3a3SMike Smith     case 0xd0:
2971da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "new controller configuration found\n");
2972da8bb3a3SMike Smith 	break;
2973da8bb3a3SMike Smith     case 0xf0:
2974da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
2975da8bb3a3SMike Smith 	return(1);
2976da8bb3a3SMike Smith     default:
2977da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
2978da8bb3a3SMike Smith 	break;
2979da8bb3a3SMike Smith     }
2980da8bb3a3SMike Smith     return(0);
2981da8bb3a3SMike Smith }
2982da8bb3a3SMike Smith 
29831ac4b82bSMike Smith /********************************************************************************
29841ac4b82bSMike Smith  ********************************************************************************
29851ac4b82bSMike Smith                                                                 Utility Functions
29861ac4b82bSMike Smith  ********************************************************************************
29871ac4b82bSMike Smith  ********************************************************************************/
29881ac4b82bSMike Smith 
29891ac4b82bSMike Smith /********************************************************************************
29901ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
29911ac4b82bSMike Smith  */
29921ac4b82bSMike Smith static struct mlx_sysdrive *
29931ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
29941ac4b82bSMike Smith {
29951ac4b82bSMike Smith     int		i;
29961ac4b82bSMike Smith 
29971ac4b82bSMike Smith     /* search system drives */
29981ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
29991ac4b82bSMike Smith 	/* is this one attached? */
30001ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
30011ac4b82bSMike Smith 	    /* is this the one? */
30021ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
30031ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
30041ac4b82bSMike Smith 	}
30051ac4b82bSMike Smith     }
30061ac4b82bSMike Smith     return(NULL);
30071ac4b82bSMike Smith }
3008