xref: /freebsd/sys/dev/mlx/mlx.c (revision 11a9117871e6037ae7b8011b243939322efce569)
11ac4b82bSMike Smith /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
41ac4b82bSMike Smith  * Copyright (c) 1999 Michael Smith
51ac4b82bSMike Smith  * All rights reserved.
61ac4b82bSMike Smith  *
71ac4b82bSMike Smith  * Redistribution and use in source and binary forms, with or without
81ac4b82bSMike Smith  * modification, are permitted provided that the following conditions
91ac4b82bSMike Smith  * are met:
101ac4b82bSMike Smith  * 1. Redistributions of source code must retain the above copyright
111ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer.
121ac4b82bSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
131ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer in the
141ac4b82bSMike Smith  *    documentation and/or other materials provided with the distribution.
151ac4b82bSMike Smith  *
161ac4b82bSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171ac4b82bSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181ac4b82bSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191ac4b82bSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201ac4b82bSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211ac4b82bSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221ac4b82bSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231ac4b82bSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241ac4b82bSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251ac4b82bSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261ac4b82bSMike Smith  * SUCH DAMAGE.
271ac4b82bSMike Smith  */
281ac4b82bSMike Smith 
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>
35b04e4c12SJohn Baldwin #include <sys/bio.h>
360fca6f8bSJohn Baldwin #include <sys/lock.h>
371ac4b82bSMike Smith #include <sys/malloc.h>
380fca6f8bSJohn Baldwin #include <sys/mutex.h>
391ac4b82bSMike Smith #include <sys/kernel.h>
400fca6f8bSJohn Baldwin #include <sys/sx.h>
411ac4b82bSMike Smith 
421ac4b82bSMike Smith #include <sys/bus.h>
431ac4b82bSMike Smith #include <sys/conf.h>
44da8bb3a3SMike Smith #include <sys/stat.h>
451ac4b82bSMike Smith 
461ac4b82bSMike Smith #include <machine/resource.h>
471ac4b82bSMike Smith #include <machine/bus.h>
481ac4b82bSMike Smith #include <machine/clock.h>
491ac4b82bSMike Smith #include <sys/rman.h>
501ac4b82bSMike Smith 
51891619a6SPoul-Henning Kamp #include <geom/geom_disk.h>
52891619a6SPoul-Henning Kamp 
531ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
541ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
551ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
561ac4b82bSMike Smith 
571ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
58dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
597ac40f5fSPoul-Henning Kamp 	.d_open =	mlx_open,
607ac40f5fSPoul-Henning Kamp 	.d_close =	mlx_close,
617ac40f5fSPoul-Henning Kamp 	.d_ioctl =	mlx_ioctl,
627ac40f5fSPoul-Henning Kamp 	.d_name =	"mlx",
631ac4b82bSMike Smith };
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);
710fca6f8bSJohn Baldwin static int			mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
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);
760fca6f8bSJohn Baldwin static int			mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
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);
810fca6f8bSJohn Baldwin static int			mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
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);
1330fca6f8bSJohn Baldwin static int			mlx_shutdown_locked(struct mlx_softc *sc);
1341ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1350fca6f8bSJohn Baldwin static int			mlx_done(struct mlx_softc *sc, int startio);
1361ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1371ac4b82bSMike Smith 
1381ac4b82bSMike Smith /*
1391ac4b82bSMike Smith  * Debugging.
1401ac4b82bSMike Smith  */
1411ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1429eee27f1SMike Smith static void			mlx_describe_controller(struct mlx_softc *sc);
143da8bb3a3SMike Smith static int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
1441ac4b82bSMike Smith 
1451ac4b82bSMike Smith /*
1461ac4b82bSMike Smith  * Utility functions.
1471ac4b82bSMike Smith  */
1481ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1491ac4b82bSMike Smith 
1501ac4b82bSMike Smith /********************************************************************************
1511ac4b82bSMike Smith  ********************************************************************************
1521ac4b82bSMike Smith                                                                 Public Interfaces
1531ac4b82bSMike Smith  ********************************************************************************
1541ac4b82bSMike Smith  ********************************************************************************/
1551ac4b82bSMike Smith 
1561ac4b82bSMike Smith /********************************************************************************
1571ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1581ac4b82bSMike Smith  *
1591ac4b82bSMike Smith  * Should not be called if the controller is active.
1601ac4b82bSMike Smith  */
1611ac4b82bSMike Smith void
mlx_free(struct mlx_softc * sc)1621ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1631ac4b82bSMike Smith {
1641ac4b82bSMike Smith     struct mlx_command	*mc;
1651ac4b82bSMike Smith 
166da8bb3a3SMike Smith     debug_called(1);
1671ac4b82bSMike Smith 
1680fca6f8bSJohn Baldwin     /* destroy control device */
1690fca6f8bSJohn Baldwin     if (sc->mlx_dev_t != NULL)
1700fca6f8bSJohn Baldwin 	destroy_dev(sc->mlx_dev_t);
1710fca6f8bSJohn Baldwin 
1720fca6f8bSJohn Baldwin     if (sc->mlx_intr)
1730fca6f8bSJohn Baldwin 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1740fca6f8bSJohn Baldwin 
1751ac4b82bSMike Smith     /* cancel status timeout */
1760fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
1770fca6f8bSJohn Baldwin     callout_stop(&sc->mlx_timeout);
1781ac4b82bSMike Smith 
1791ac4b82bSMike Smith     /* throw away any command buffers */
1801ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1811ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1821ac4b82bSMike Smith 	mlx_freecmd(mc);
1831ac4b82bSMike Smith     }
1840fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
1850fca6f8bSJohn Baldwin     callout_drain(&sc->mlx_timeout);
1861ac4b82bSMike Smith 
1871ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1881ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1891ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1901ac4b82bSMike Smith 
1911ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
192aced5239SJohn Baldwin     if (sc->mlx_sgbusaddr)
193aced5239SJohn Baldwin 	bus_dmamap_unload(sc->mlx_sg_dmat, sc->mlx_sg_dmamap);
1941ac4b82bSMike Smith     if (sc->mlx_sgtable)
1951ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1961ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1971ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1981ac4b82bSMike Smith 
1991ac4b82bSMike Smith     /* disconnect the interrupt handler */
2001ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
2011ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
2021ac4b82bSMike Smith 
2031ac4b82bSMike Smith     /* destroy the parent DMA tag */
2041ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
2051ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
2061ac4b82bSMike Smith 
2071ac4b82bSMike Smith     /* release the register window mapping */
2081ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
2099b11c7baSMatthew N. Dodd 	bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem);
2109eee27f1SMike Smith 
2119eee27f1SMike Smith     /* free controller enquiry data */
2129eee27f1SMike Smith     if (sc->mlx_enq2 != NULL)
2139eee27f1SMike Smith 	free(sc->mlx_enq2, M_DEVBUF);
214da8bb3a3SMike Smith 
2150fca6f8bSJohn Baldwin     sx_destroy(&sc->mlx_config_lock);
2160fca6f8bSJohn Baldwin     mtx_destroy(&sc->mlx_io_lock);
2171ac4b82bSMike Smith }
2181ac4b82bSMike Smith 
2191ac4b82bSMike Smith /********************************************************************************
2201ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2211ac4b82bSMike Smith  */
2221ac4b82bSMike Smith static void
mlx_dma_map_sg(void * arg,bus_dma_segment_t * segs,int nseg,int error)2231ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2241ac4b82bSMike Smith {
2251ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2261ac4b82bSMike Smith 
227da8bb3a3SMike Smith     debug_called(1);
2281ac4b82bSMike Smith 
2291ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2301ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2311ac4b82bSMike Smith }
2321ac4b82bSMike Smith 
2331ac4b82bSMike Smith static int
mlx_sglist_map(struct mlx_softc * sc)2341ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2351ac4b82bSMike Smith {
2361ac4b82bSMike Smith     size_t	segsize;
237baff09dbSMike Smith     int		error, ncmd;
2381ac4b82bSMike Smith 
239da8bb3a3SMike Smith     debug_called(1);
2401ac4b82bSMike Smith 
2411ac4b82bSMike Smith     /* destroy any existing mappings */
242aced5239SJohn Baldwin     if (sc->mlx_sgbusaddr)
243aced5239SJohn Baldwin 	bus_dmamap_unload(sc->mlx_sg_dmat, sc->mlx_sg_dmamap);
2441ac4b82bSMike Smith     if (sc->mlx_sgtable)
2451ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2461ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2471ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
248aced5239SJohn Baldwin     sc->mlx_sgbusaddr = 0;
249aced5239SJohn Baldwin     sc->mlx_sgtable = NULL;
250aced5239SJohn Baldwin     sc->mlx_sg_dmat = NULL;
2511ac4b82bSMike Smith 
2521ac4b82bSMike Smith     /*
2531ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
254baff09dbSMike Smith      * the s/g lists we will need.  If we're called early on, we don't know how
255baff09dbSMike Smith      * many commands we're going to be asked to support, so only allocate enough
256baff09dbSMike Smith      * for a couple.
2571ac4b82bSMike Smith      */
258baff09dbSMike Smith     if (sc->mlx_enq2 == NULL) {
259baff09dbSMike Smith 	ncmd = 2;
260baff09dbSMike Smith     } else {
261baff09dbSMike Smith 	ncmd = sc->mlx_enq2->me_max_commands;
262baff09dbSMike Smith     }
263baff09dbSMike Smith     segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd;
2641ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2651ac4b82bSMike Smith 			       1, 0, 			/* alignment,boundary */
2661ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2671ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2681ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2691ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2701ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2711ac4b82bSMike Smith 			       0,			/* flags */
272fc3e87b3SScott Long 			       NULL, NULL,		/* lockfunc, lockarg */
2731ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2741ac4b82bSMike Smith     if (error != 0) {
2751ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2761ac4b82bSMike Smith 	return(ENOMEM);
2771ac4b82bSMike Smith     }
2781ac4b82bSMike Smith 
2791ac4b82bSMike Smith     /*
2801ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2811ac4b82bSMike Smith      * controller-visible space.
2821ac4b82bSMike Smith      *
2831ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
284fc3e87b3SScott Long      * contiguous slab.  We may need to switch to a more complex arrangement
285fc3e87b3SScott Long      * where we allocate in smaller chunks and keep a lookup table from slot
286fc3e87b3SScott Long      * to bus address.
2871ac4b82bSMike Smith      */
288fc3e87b3SScott Long     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable,
289fc3e87b3SScott Long 			     BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2901ac4b82bSMike Smith     if (error) {
2911ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2921ac4b82bSMike Smith 	return(ENOMEM);
2931ac4b82bSMike Smith     }
2941b4404f9SScott Long     (void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable,
295fc3e87b3SScott Long 			  segsize, mlx_dma_map_sg, sc, 0);
2961ac4b82bSMike Smith     return(0);
2971ac4b82bSMike Smith }
2981ac4b82bSMike Smith 
2991ac4b82bSMike Smith /********************************************************************************
3001ac4b82bSMike Smith  * Initialise the controller and softc
3011ac4b82bSMike Smith  */
3021ac4b82bSMike Smith int
mlx_attach(struct mlx_softc * sc)3031ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
3041ac4b82bSMike Smith {
305da8bb3a3SMike Smith     struct mlx_enquiry_old	*meo;
306da8bb3a3SMike Smith     int				rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
3071ac4b82bSMike Smith 
308da8bb3a3SMike Smith     debug_called(1);
3091ac4b82bSMike Smith 
3101ac4b82bSMike Smith     /*
3111ac4b82bSMike Smith      * Initialise per-controller queues.
3121ac4b82bSMike Smith      */
3134b006d7bSMike Smith     TAILQ_INIT(&sc->mlx_work);
3141ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
315b04e4c12SJohn Baldwin     bioq_init(&sc->mlx_bioq);
3161ac4b82bSMike Smith 
3171ac4b82bSMike Smith     /*
3181ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
3191ac4b82bSMike Smith      */
3201ac4b82bSMike Smith     switch(sc->mlx_iftype) {
321da8bb3a3SMike Smith     case MLX_IFTYPE_2:
3221ac4b82bSMike Smith     case MLX_IFTYPE_3:
3231ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
3241ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
3251ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
326da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v3_fw_handshake;
3271ac4b82bSMike Smith 	break;
328f6b84b08SMike Smith     case MLX_IFTYPE_4:
329f6b84b08SMike Smith 	sc->mlx_tryqueue	= mlx_v4_tryqueue;
330f6b84b08SMike Smith 	sc->mlx_findcomplete	= mlx_v4_findcomplete;
331f6b84b08SMike Smith 	sc->mlx_intaction	= mlx_v4_intaction;
332da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
333f6b84b08SMike Smith 	break;
3345792b7feSMike Smith     case MLX_IFTYPE_5:
3355792b7feSMike Smith 	sc->mlx_tryqueue	= mlx_v5_tryqueue;
3365792b7feSMike Smith 	sc->mlx_findcomplete	= mlx_v5_findcomplete;
3375792b7feSMike Smith 	sc->mlx_intaction	= mlx_v5_intaction;
338da8bb3a3SMike Smith 	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
3395792b7feSMike Smith 	break;
3401ac4b82bSMike Smith     default:
3411ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3421ac4b82bSMike Smith     }
3431ac4b82bSMike Smith 
3441ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
3450fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
3461ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
3470fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
3481ac4b82bSMike Smith 
3491ac4b82bSMike Smith     /*
350da8bb3a3SMike Smith      * Wait for the controller to come ready, handshake with the firmware if required.
351da8bb3a3SMike Smith      * This is typically only necessary on platforms where the controller BIOS does not
352da8bb3a3SMike Smith      * run.
353da8bb3a3SMike Smith      */
354da8bb3a3SMike Smith     hsmsg = 0;
355da8bb3a3SMike Smith     DELAY(1000);
3560fca6f8bSJohn Baldwin     while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2,
3570fca6f8bSJohn Baldwin 	hsmsg == 0)) != 0) {
358da8bb3a3SMike Smith 	/* report first time around... */
359da8bb3a3SMike Smith 	if (hsmsg == 0) {
360da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
361da8bb3a3SMike Smith 	    hsmsg = 1;
362da8bb3a3SMike Smith 	}
363da8bb3a3SMike Smith 	/* did we get a real message? */
364da8bb3a3SMike Smith 	if (hscode == 2) {
365da8bb3a3SMike Smith 	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
366da8bb3a3SMike Smith 	    /* fatal initialisation error? */
367da8bb3a3SMike Smith 	    if (hscode != 0) {
368da8bb3a3SMike Smith 		return(ENXIO);
369da8bb3a3SMike Smith 	    }
370da8bb3a3SMike Smith 	}
371da8bb3a3SMike Smith     }
372da8bb3a3SMike Smith     if (hsmsg == 1)
373da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "initialisation complete.\n");
374da8bb3a3SMike Smith 
375da8bb3a3SMike Smith     /*
3761ac4b82bSMike Smith      * Allocate and connect our interrupt.
3771ac4b82bSMike Smith      */
3781ac4b82bSMike Smith     rid = 0;
3795f96beb9SNate Lawson     sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid,
3805f96beb9SNate Lawson         RF_SHAREABLE | RF_ACTIVE);
3811ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
382421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't allocate interrupt\n");
3831ac4b82bSMike Smith 	return(ENXIO);
3841ac4b82bSMike Smith     }
3850fca6f8bSJohn Baldwin     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO |
3860fca6f8bSJohn Baldwin 	INTR_ENTROPY | INTR_MPSAFE, NULL, mlx_intr, sc, &sc->mlx_intr);
3871ac4b82bSMike Smith     if (error) {
388421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't set up interrupt\n");
3891ac4b82bSMike Smith 	return(ENXIO);
3901ac4b82bSMike Smith     }
3911ac4b82bSMike Smith 
3921ac4b82bSMike Smith     /*
3931ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3941ac4b82bSMike Smith      */
3951ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
3961b4404f9SScott Long 			       1, 0, 			/* align, boundary */
3971ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
3981ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
3991ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
4006f954fb3SAlexander Motin 			       MLX_MAXPHYS,		/* maxsize */
4016f954fb3SAlexander Motin 			       MLX_NSEG,		/* nsegments */
4021ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
4031ac4b82bSMike Smith 			       0,			/* flags */
404f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
4050fca6f8bSJohn Baldwin 			       &sc->mlx_io_lock,	/* lockarg */
4061ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
4071ac4b82bSMike Smith     if (error != 0) {
4081ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
4091ac4b82bSMike Smith 	return(ENOMEM);
4101ac4b82bSMike Smith     }
4111ac4b82bSMike Smith 
4121ac4b82bSMike Smith     /*
4131b4404f9SScott Long      * Create some initial scatter/gather mappings so we can run the probe
4141b4404f9SScott Long      * commands.
4151ac4b82bSMike Smith      */
4161ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4171ac4b82bSMike Smith     if (error != 0) {
418421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
4191ac4b82bSMike Smith 	return(error);
4201ac4b82bSMike Smith     }
4211ac4b82bSMike Smith 
422baff09dbSMike Smith     /*
423baff09dbSMike Smith      * We don't (yet) know where the event log is up to.
424baff09dbSMike Smith      */
425baff09dbSMike Smith     sc->mlx_currevent = -1;
426baff09dbSMike Smith 
427baff09dbSMike Smith     /*
428baff09dbSMike Smith      * Obtain controller feature information
429baff09dbSMike Smith      */
4300fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
4319eee27f1SMike Smith     if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
4320fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
4331ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
4341ac4b82bSMike Smith 	return(ENXIO);
4351ac4b82bSMike Smith     }
4361ac4b82bSMike Smith 
4379eee27f1SMike Smith     /*
4381ac4b82bSMike Smith      * Do quirk/feature related things.
4391ac4b82bSMike Smith      */
4409eee27f1SMike Smith     fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
4411ac4b82bSMike Smith     switch(sc->mlx_iftype) {
442da8bb3a3SMike Smith     case MLX_IFTYPE_2:
443da8bb3a3SMike Smith 	/* These controllers don't report the firmware version in the ENQUIRY2 response */
444da8bb3a3SMike Smith 	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
4450fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
446da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
447da8bb3a3SMike Smith 	    return(ENXIO);
448da8bb3a3SMike Smith 	}
449da8bb3a3SMike Smith 	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
450da8bb3a3SMike Smith 
451572f2440SWarner Losh 	/* XXX require 2.42 or better (PCI) */
452da8bb3a3SMike Smith 	if (meo->me_fwminor < 42) {
453da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
454da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
455da8bb3a3SMike Smith 	}
456caa32ef5SColin Percival 	free(meo, M_DEVBUF);
457da8bb3a3SMike Smith 	break;
4581ac4b82bSMike Smith     case MLX_IFTYPE_3:
459f6b84b08SMike Smith 	/* XXX certify 3.52? */
4609eee27f1SMike Smith 	if (fwminor < 51) {
4614b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4624b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
4631ac4b82bSMike Smith 	}
4641ac4b82bSMike Smith 	break;
465f6b84b08SMike Smith     case MLX_IFTYPE_4:
466f6b84b08SMike Smith 	/* XXX certify firmware versions? */
4679eee27f1SMike Smith 	if (fwminor < 6) {
4684b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4694b006d7bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
470f6b84b08SMike Smith 	}
471f6b84b08SMike Smith 	break;
4725792b7feSMike Smith     case MLX_IFTYPE_5:
4739eee27f1SMike Smith 	if (fwminor < 7) {
4745792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
4755792b7feSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
4765792b7feSMike Smith 	}
4775792b7feSMike Smith 	break;
4781ac4b82bSMike Smith     default:
4790fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
4801ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
4811ac4b82bSMike Smith     }
4820fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
4831ac4b82bSMike Smith 
4841ac4b82bSMike Smith     /*
485baff09dbSMike Smith      * Create the final scatter/gather mappings now that we have characterised the controller.
4861ac4b82bSMike Smith      */
4871ac4b82bSMike Smith     error = mlx_sglist_map(sc);
4881ac4b82bSMike Smith     if (error != 0) {
489baff09dbSMike Smith 	device_printf(sc->mlx_dev, "can't make final s/g list mapping\n");
4901ac4b82bSMike Smith 	return(error);
4911ac4b82bSMike Smith     }
4921ac4b82bSMike Smith 
4931ac4b82bSMike Smith     /*
494421f2f7dSMike Smith      * No user-requested background operation is in progress.
4951ac4b82bSMike Smith      */
496421f2f7dSMike Smith     sc->mlx_background = 0;
497421f2f7dSMike Smith     sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
4981ac4b82bSMike Smith 
4991ac4b82bSMike Smith     /*
500da8bb3a3SMike Smith      * Create the control device.
5011ac4b82bSMike Smith      */
502e78a5a3fSEd Schouten     sc->mlx_dev_t = make_dev(&mlx_cdevsw, 0, UID_ROOT, GID_OPERATOR,
503da8bb3a3SMike Smith 			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
504e78a5a3fSEd Schouten     sc->mlx_dev_t->si_drv1 = sc;
5051ac4b82bSMike Smith 
5061ac4b82bSMike Smith     /*
5071ac4b82bSMike Smith      * Start the timeout routine.
5081ac4b82bSMike Smith      */
5090fca6f8bSJohn Baldwin     callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc);
5101ac4b82bSMike Smith 
511da8bb3a3SMike Smith     /* print a little information about the controller */
512da8bb3a3SMike Smith     mlx_describe_controller(sc);
513da8bb3a3SMike Smith 
5141ac4b82bSMike Smith     return(0);
5151ac4b82bSMike Smith }
5161ac4b82bSMike Smith 
5171ac4b82bSMike Smith /********************************************************************************
5181ac4b82bSMike Smith  * Locate disk resources and attach children to them.
5191ac4b82bSMike Smith  */
5201ac4b82bSMike Smith void
mlx_startup(struct mlx_softc * sc)5211ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
5221ac4b82bSMike Smith {
5231ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
5241ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
52518250ec6SJohn Baldwin     int				i;
5261ac4b82bSMike Smith 
527da8bb3a3SMike Smith     debug_called(1);
5281ac4b82bSMike Smith 
5291ac4b82bSMike Smith     /*
5301ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
5311ac4b82bSMike Smith      * don't currently have them.
5321ac4b82bSMike Smith      */
5330fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
5341ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
5350fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
5361ac4b82bSMike Smith     if (mes == NULL) {
5379eee27f1SMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status\n");
5381ac4b82bSMike Smith 	return;
5391ac4b82bSMike Smith     }
5401ac4b82bSMike Smith 
5411ac4b82bSMike Smith     /* iterate over drives returned */
5420fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
5431ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
5441ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
5451ac4b82bSMike Smith 	 i++, dr++) {
5461ac4b82bSMike Smith 	/* are we already attached to this drive? */
5471ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
5481ac4b82bSMike Smith 	    /* pick up drive information */
5491ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
550f6b84b08SMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
5511ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
5521ac4b82bSMike Smith 
5531ac4b82bSMike Smith 	    /* generate geometry information */
5541ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
5551ac4b82bSMike Smith 		dr->ms_heads = 128;
5561ac4b82bSMike Smith 		dr->ms_sectors = 32;
5571ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
5581ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
5591ac4b82bSMike Smith 		dr->ms_heads = 255;
5601ac4b82bSMike Smith 		dr->ms_sectors = 63;
5611ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
5621ac4b82bSMike Smith 	    }
5635b56413dSWarner Losh 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, DEVICE_UNIT_ANY);
5641ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
5651ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
566fe0d4089SMatthew N. Dodd 	    device_set_ivars(dr->ms_disk, dr);
5671ac4b82bSMike Smith 	}
5681ac4b82bSMike Smith     }
5691ac4b82bSMike Smith     free(mes, M_DEVBUF);
57018250ec6SJohn Baldwin     bus_attach_children(sc->mlx_dev);
5711ac4b82bSMike Smith 
5721ac4b82bSMike Smith     /* mark controller back up */
5730fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
5741ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5751ac4b82bSMike Smith 
5761ac4b82bSMike Smith     /* enable interrupts */
5771ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
5780fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
5790fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
5801ac4b82bSMike Smith }
5811ac4b82bSMike Smith 
5821ac4b82bSMike Smith /********************************************************************************
5831ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
5841ac4b82bSMike Smith  */
5851ac4b82bSMike Smith int
mlx_detach(device_t dev)5861ac4b82bSMike Smith mlx_detach(device_t dev)
5871ac4b82bSMike Smith {
5881ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5895792b7feSMike Smith     struct mlxd_softc	*mlxd;
5900fca6f8bSJohn Baldwin     int			i, error;
5911ac4b82bSMike Smith 
592da8bb3a3SMike Smith     debug_called(1);
5931ac4b82bSMike Smith 
5945792b7feSMike Smith     error = EBUSY;
5950fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
5961ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
5975792b7feSMike Smith 	goto out;
5981ac4b82bSMike Smith 
5995792b7feSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
6005792b7feSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
6015792b7feSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
6025792b7feSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
6035792b7feSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
6045792b7feSMike Smith 		goto out;
6055792b7feSMike Smith 	    }
6065792b7feSMike Smith 	}
6075792b7feSMike Smith     }
6081ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
6095792b7feSMike Smith 	goto out;
6100fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
6111ac4b82bSMike Smith 
6121ac4b82bSMike Smith     mlx_free(sc);
6131ac4b82bSMike Smith 
6140fca6f8bSJohn Baldwin     return (0);
6155792b7feSMike Smith  out:
6160fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
6175792b7feSMike Smith     return(error);
6181ac4b82bSMike Smith }
6191ac4b82bSMike Smith 
6201ac4b82bSMike Smith /********************************************************************************
6211ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
6221ac4b82bSMike Smith  *
6231ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
6241ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
6251ac4b82bSMike Smith  * resume normal operation.)
6261ac4b82bSMike Smith  *
6278177437dSPoul-Henning Kamp  * Note that we can assume that the bioq on the controller is empty, as we won't
6281ac4b82bSMike Smith  * allow shutdown if any device is open.
6291ac4b82bSMike Smith  */
6301ac4b82bSMike Smith int
mlx_shutdown(device_t dev)6311ac4b82bSMike Smith mlx_shutdown(device_t dev)
6321ac4b82bSMike Smith {
6331ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6340fca6f8bSJohn Baldwin     int			error;
6350fca6f8bSJohn Baldwin 
6360fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
6370fca6f8bSJohn Baldwin     error = mlx_shutdown_locked(sc);
6380fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
6390fca6f8bSJohn Baldwin     return (error);
6400fca6f8bSJohn Baldwin }
6410fca6f8bSJohn Baldwin 
6420fca6f8bSJohn Baldwin static int
mlx_shutdown_locked(struct mlx_softc * sc)6430fca6f8bSJohn Baldwin mlx_shutdown_locked(struct mlx_softc *sc)
6440fca6f8bSJohn Baldwin {
645*11a91178SJohn Baldwin     int			error;
6461ac4b82bSMike Smith 
647da8bb3a3SMike Smith     debug_called(1);
6481ac4b82bSMike Smith 
6490fca6f8bSJohn Baldwin     MLX_CONFIG_ASSERT_LOCKED(sc);
6501ac4b82bSMike Smith 
6510fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
6521ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
6535792b7feSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6541ac4b82bSMike Smith 
6551ac4b82bSMike Smith     /* flush controller */
6561ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6571ac4b82bSMike Smith     if (mlx_flush(sc)) {
6581ac4b82bSMike Smith 	printf("failed\n");
6591ac4b82bSMike Smith     } else {
6601ac4b82bSMike Smith 	printf("done\n");
6611ac4b82bSMike Smith     }
6620fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
6631ac4b82bSMike Smith 
6641ac4b82bSMike Smith     /* delete all our child devices */
665*11a91178SJohn Baldwin     error = bus_generic_detach(sc->mlx_dev);
6661ac4b82bSMike Smith 
667*11a91178SJohn Baldwin     return (error);
6681ac4b82bSMike Smith }
6691ac4b82bSMike Smith 
6701ac4b82bSMike Smith /********************************************************************************
6711ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
6721ac4b82bSMike Smith  */
6731ac4b82bSMike Smith int
mlx_suspend(device_t dev)6741ac4b82bSMike Smith mlx_suspend(device_t dev)
6751ac4b82bSMike Smith {
6761ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6771ac4b82bSMike Smith 
678da8bb3a3SMike Smith     debug_called(1);
6791ac4b82bSMike Smith 
6800fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
6811ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
6821ac4b82bSMike Smith 
6831ac4b82bSMike Smith     /* flush controller */
6841ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
6851ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
6861ac4b82bSMike Smith 
6871ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
6880fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
6891ac4b82bSMike Smith 
6901ac4b82bSMike Smith     return(0);
6911ac4b82bSMike Smith }
6921ac4b82bSMike Smith 
6931ac4b82bSMike Smith /********************************************************************************
6941ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
6951ac4b82bSMike Smith  */
6961ac4b82bSMike Smith int
mlx_resume(device_t dev)6971ac4b82bSMike Smith mlx_resume(device_t dev)
6981ac4b82bSMike Smith {
6991ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
7001ac4b82bSMike Smith 
701da8bb3a3SMike Smith     debug_called(1);
7021ac4b82bSMike Smith 
7030fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7041ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
7051ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
7060fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7071ac4b82bSMike Smith 
7081ac4b82bSMike Smith     return(0);
7091ac4b82bSMike Smith }
7101ac4b82bSMike Smith 
7111ac4b82bSMike Smith /*******************************************************************************
7121ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
7131ac4b82bSMike Smith  * status.
7141ac4b82bSMike Smith  */
7151ac4b82bSMike Smith void
mlx_intr(void * arg)7161ac4b82bSMike Smith mlx_intr(void *arg)
7171ac4b82bSMike Smith {
7181ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
7191ac4b82bSMike Smith 
720da8bb3a3SMike Smith     debug_called(1);
7211ac4b82bSMike Smith 
7225792b7feSMike Smith     /* collect finished commands, queue anything waiting */
7230fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7240fca6f8bSJohn Baldwin     mlx_done(sc, 1);
7250fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7261ac4b82bSMike Smith };
7271ac4b82bSMike Smith 
7281ac4b82bSMike Smith /*******************************************************************************
7291ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
7301ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
7311ac4b82bSMike Smith  */
7321ac4b82bSMike Smith int
mlx_submit_buf(struct mlx_softc * sc,struct bio * bp)733b04e4c12SJohn Baldwin mlx_submit_buf(struct mlx_softc *sc, struct bio *bp)
7341ac4b82bSMike Smith {
7354b006d7bSMike Smith 
736da8bb3a3SMike Smith     debug_called(1);
7371ac4b82bSMike Smith 
7380fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
739b04e4c12SJohn Baldwin     bioq_insert_tail(&sc->mlx_bioq, bp);
7401ac4b82bSMike Smith     sc->mlx_waitbufs++;
7411ac4b82bSMike Smith     mlx_startio(sc);
7421ac4b82bSMike Smith     return(0);
7431ac4b82bSMike Smith }
7441ac4b82bSMike Smith 
7451ac4b82bSMike Smith /********************************************************************************
7461ac4b82bSMike Smith  * Accept an open operation on the control device.
7471ac4b82bSMike Smith  */
7481ac4b82bSMike Smith int
mlx_open(struct cdev * dev,int flags,int fmt,struct thread * td)74989c9c53dSPoul-Henning Kamp mlx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
7501ac4b82bSMike Smith {
751e78a5a3fSEd Schouten     struct mlx_softc	*sc = dev->si_drv1;
7521ac4b82bSMike Smith 
7530fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
7540fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7551ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
7560fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7570fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
7581ac4b82bSMike Smith     return(0);
7591ac4b82bSMike Smith }
7601ac4b82bSMike Smith 
7611ac4b82bSMike Smith /********************************************************************************
7621ac4b82bSMike Smith  * Accept the last close on the control device.
7631ac4b82bSMike Smith  */
7641ac4b82bSMike Smith int
mlx_close(struct cdev * dev,int flags,int fmt,struct thread * td)76589c9c53dSPoul-Henning Kamp mlx_close(struct cdev *dev, int flags, int fmt, struct thread *td)
7661ac4b82bSMike Smith {
767e78a5a3fSEd Schouten     struct mlx_softc	*sc = dev->si_drv1;
7681ac4b82bSMike Smith 
7690fca6f8bSJohn Baldwin     MLX_CONFIG_LOCK(sc);
7700fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
7711ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
7720fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
7730fca6f8bSJohn Baldwin     MLX_CONFIG_UNLOCK(sc);
7741ac4b82bSMike Smith     return (0);
7751ac4b82bSMike Smith }
7761ac4b82bSMike Smith 
7771ac4b82bSMike Smith /********************************************************************************
7781ac4b82bSMike Smith  * Handle controller-specific control operations.
7791ac4b82bSMike Smith  */
7801ac4b82bSMike Smith int
mlx_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int32_t flag,struct thread * td)78189c9c53dSPoul-Henning Kamp mlx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
7821ac4b82bSMike Smith {
783e78a5a3fSEd Schouten     struct mlx_softc		*sc = dev->si_drv1;
784421f2f7dSMike Smith     struct mlx_rebuild_request	*rb = (struct mlx_rebuild_request *)addr;
785421f2f7dSMike Smith     struct mlx_rebuild_status	*rs = (struct mlx_rebuild_status *)addr;
7861ac4b82bSMike Smith     int				*arg = (int *)addr;
7871ac4b82bSMike Smith     struct mlx_pause		*mp;
7881ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
7891ac4b82bSMike Smith     struct mlxd_softc		*mlxd;
7901ac4b82bSMike Smith     int				i, error;
7911ac4b82bSMike Smith 
7921ac4b82bSMike Smith     switch(cmd) {
7931ac4b82bSMike Smith 	/*
7941ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
7951ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
7961ac4b82bSMike Smith 	 * a valid unit on this controller.
7971ac4b82bSMike Smith 	 */
7981ac4b82bSMike Smith     case MLX_NEXT_CHILD:
7991ac4b82bSMike Smith 	/* search system drives */
8000fca6f8bSJohn Baldwin 	MLX_CONFIG_LOCK(sc);
8011ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
8021ac4b82bSMike Smith 	    /* is this one attached? */
8031ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
8041ac4b82bSMike Smith 		/* looking for the next one we come across? */
8051ac4b82bSMike Smith 		if (*arg == -1) {
80629e6fa3aSStephane E. Potvin 		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
8070fca6f8bSJohn Baldwin 		    MLX_CONFIG_UNLOCK(sc);
8081ac4b82bSMike Smith 		    return(0);
8091ac4b82bSMike Smith 		}
8101ac4b82bSMike Smith 		/* we want the one after this one */
8111ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
8121ac4b82bSMike Smith 		    *arg = -1;
8131ac4b82bSMike Smith 	    }
8141ac4b82bSMike Smith 	}
8150fca6f8bSJohn Baldwin 	MLX_CONFIG_UNLOCK(sc);
8161ac4b82bSMike Smith 	return(ENOENT);
8171ac4b82bSMike Smith 
8181ac4b82bSMike Smith 	/*
8191ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
8201ac4b82bSMike Smith 	 */
8211ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
822c6df6f53SWarner Losh 	bus_topo_lock();
8231ac4b82bSMike Smith 	mlx_startup(sc);
824c6df6f53SWarner Losh 	bus_topo_unlock();
8251ac4b82bSMike Smith 	return(0);
8261ac4b82bSMike Smith 
8271ac4b82bSMike Smith 	/*
8281ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
8291ac4b82bSMike Smith 	 * away.
8301ac4b82bSMike Smith 	 */
8311ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
8320fca6f8bSJohn Baldwin 	MLX_CONFIG_LOCK(sc);
8331ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
8340fca6f8bSJohn Baldwin 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) {
8350fca6f8bSJohn Baldwin 	    MLX_CONFIG_UNLOCK(sc);
8361ac4b82bSMike Smith 	    return(ENOENT);
8370fca6f8bSJohn Baldwin 	}
8381ac4b82bSMike Smith 
8391ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
8401ac4b82bSMike Smith 	error = 0;
8411ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
8421ac4b82bSMike Smith 	    error = EBUSY;
8431ac4b82bSMike Smith 	    goto detach_out;
8441ac4b82bSMike Smith 	}
8451ac4b82bSMike Smith 
8461ac4b82bSMike Smith 	/* flush controller */
8470fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
8481ac4b82bSMike Smith 	if (mlx_flush(sc)) {
8490fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
8501ac4b82bSMike Smith 	    error = EBUSY;
8511ac4b82bSMike Smith 	    goto detach_out;
8521ac4b82bSMike Smith 	}
8530fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
8541ac4b82bSMike Smith 
8551ac4b82bSMike Smith 	/* nuke drive */
8561ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
8571ac4b82bSMike Smith 	    goto detach_out;
8581ac4b82bSMike Smith 	dr->ms_disk = 0;
8591ac4b82bSMike Smith 
8601ac4b82bSMike Smith     detach_out:
8610fca6f8bSJohn Baldwin 	MLX_CONFIG_UNLOCK(sc);
8621ac4b82bSMike Smith 	if (error) {
8631ac4b82bSMike Smith 	    printf("failed\n");
8641ac4b82bSMike Smith 	} else {
8651ac4b82bSMike Smith 	    printf("done\n");
8661ac4b82bSMike Smith 	}
8671ac4b82bSMike Smith 	return(error);
8681ac4b82bSMike Smith 
8691ac4b82bSMike Smith 	/*
8701ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
8711ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
8721ac4b82bSMike Smith 	 *
8731ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
8741ac4b82bSMike Smith 	 * to do this right.
8751ac4b82bSMike Smith 	 */
8761ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
8771ac4b82bSMike Smith 	/* Does this command work on this firmware? */
8781ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
8791ac4b82bSMike Smith 	    return(EOPNOTSUPP);
8801ac4b82bSMike Smith 
8810fca6f8bSJohn Baldwin 	/* check time values */
8821ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
8830fca6f8bSJohn Baldwin 	if ((mp->mp_when < 0) || (mp->mp_when > 3600))
8840fca6f8bSJohn Baldwin 	    return(EINVAL);
8850fca6f8bSJohn Baldwin 	if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
8860fca6f8bSJohn Baldwin 	    return(EINVAL);
8870fca6f8bSJohn Baldwin 
8880fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
8891ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
8901ac4b82bSMike Smith 	    /* cancel a pending pause operation */
8911ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
8921ac4b82bSMike Smith 	} else {
8931ac4b82bSMike Smith 	    /* fix for legal channels */
8949eee27f1SMike Smith 	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
8951ac4b82bSMike Smith 
8961ac4b82bSMike Smith 	    /* check for a pause currently running */
8970fca6f8bSJohn Baldwin 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) {
8980fca6f8bSJohn Baldwin 		MLX_IO_UNLOCK(sc);
8991ac4b82bSMike Smith 		return(EBUSY);
9000fca6f8bSJohn Baldwin 	    }
9011ac4b82bSMike Smith 
9021ac4b82bSMike Smith 	    /* looks ok, go with it */
9031ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
9041ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
9051ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
9061ac4b82bSMike Smith 	}
9070fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
9081ac4b82bSMike Smith 	return(0);
9091ac4b82bSMike Smith 
9101ac4b82bSMike Smith 	/*
9111ac4b82bSMike Smith 	 * Accept a command passthrough-style.
9121ac4b82bSMike Smith 	 */
9131ac4b82bSMike Smith     case MLX_COMMAND:
9141ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
9151ac4b82bSMike Smith 
916421f2f7dSMike Smith 	/*
917421f2f7dSMike Smith 	 * Start a rebuild on a given SCSI disk
918421f2f7dSMike Smith 	 */
919421f2f7dSMike Smith     case MLX_REBUILDASYNC:
9200fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
921421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
9220fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
923421f2f7dSMike Smith 	    rb->rr_status = 0x0106;
924421f2f7dSMike Smith 	    return(EBUSY);
925421f2f7dSMike Smith 	}
926421f2f7dSMike Smith 	rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
927421f2f7dSMike Smith 	switch (rb->rr_status) {
928421f2f7dSMike Smith 	case 0:
929421f2f7dSMike Smith 	    error = 0;
930421f2f7dSMike Smith 	    break;
931421f2f7dSMike Smith 	case 0x10000:
932421f2f7dSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
933421f2f7dSMike Smith 	    break;
934421f2f7dSMike Smith 	case 0x0002:
935421f2f7dSMike Smith 	    error = EBUSY;
936421f2f7dSMike Smith 	    break;
937421f2f7dSMike Smith 	case 0x0104:
938421f2f7dSMike Smith 	    error = EIO;
939421f2f7dSMike Smith 	    break;
940421f2f7dSMike Smith 	case 0x0105:
941421f2f7dSMike Smith 	    error = ERANGE;
942421f2f7dSMike Smith 	    break;
943421f2f7dSMike Smith 	case 0x0106:
944421f2f7dSMike Smith 	    error = EBUSY;
945421f2f7dSMike Smith 	    break;
946421f2f7dSMike Smith 	default:
947421f2f7dSMike Smith 	    error = EINVAL;
948421f2f7dSMike Smith 	    break;
949421f2f7dSMike Smith 	}
950421f2f7dSMike Smith 	if (error == 0)
951421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_REBUILD;
9520fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
953421f2f7dSMike Smith 	return(error);
954421f2f7dSMike Smith 
955421f2f7dSMike Smith 	/*
956421f2f7dSMike Smith 	 * Get the status of the current rebuild or consistency check.
957421f2f7dSMike Smith 	 */
958421f2f7dSMike Smith     case MLX_REBUILDSTAT:
9590fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
960421f2f7dSMike Smith 	*rs = sc->mlx_rebuildstat;
9610fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
962421f2f7dSMike Smith 	return(0);
963421f2f7dSMike Smith 
964421f2f7dSMike Smith 	/*
965421f2f7dSMike Smith 	 * Return the per-controller system drive number matching the
966421f2f7dSMike Smith 	 * disk device number in (arg), if it happens to belong to us.
967421f2f7dSMike Smith 	 */
968421f2f7dSMike Smith     case MLX_GET_SYSDRIVE:
969421f2f7dSMike Smith 	error = ENOENT;
9700fca6f8bSJohn Baldwin 	MLX_CONFIG_LOCK(sc);
971c6df6f53SWarner Losh 	bus_topo_lock();
972967949e7SJohn Baldwin 	mlxd = devclass_get_softc(devclass_find("mlxd"), *arg);
973c6df6f53SWarner Losh 	bus_topo_unlock();
974421f2f7dSMike Smith 	if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
975421f2f7dSMike Smith 	    (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
976421f2f7dSMike Smith 	    error = 0;
977421f2f7dSMike Smith 	    *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
978421f2f7dSMike Smith 	}
9790fca6f8bSJohn Baldwin 	MLX_CONFIG_UNLOCK(sc);
980421f2f7dSMike Smith 	return(error);
981421f2f7dSMike Smith 
9821ac4b82bSMike Smith     default:
9831ac4b82bSMike Smith 	return(ENOTTY);
9841ac4b82bSMike Smith     }
9851ac4b82bSMike Smith }
9861ac4b82bSMike Smith 
9871ac4b82bSMike Smith /********************************************************************************
9881ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
9891ac4b82bSMike Smith  */
9901ac4b82bSMike Smith int
mlx_submit_ioctl(struct mlx_softc * sc,struct mlx_sysdrive * drive,u_long cmd,caddr_t addr,int32_t flag,struct thread * td)9911ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
992b40ce416SJulian Elischer 		caddr_t addr, int32_t flag, struct thread *td)
9931ac4b82bSMike Smith {
9941ac4b82bSMike Smith     int				*arg = (int *)addr;
995421f2f7dSMike Smith     int				error, result;
9961ac4b82bSMike Smith 
9971ac4b82bSMike Smith     switch(cmd) {
9981ac4b82bSMike Smith 	/*
9991ac4b82bSMike Smith 	 * Return the current status of this drive.
10001ac4b82bSMike Smith 	 */
10011ac4b82bSMike Smith     case MLXD_STATUS:
10020fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
10031ac4b82bSMike Smith 	*arg = drive->ms_state;
10040fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
10051ac4b82bSMike Smith 	return(0);
10061ac4b82bSMike Smith 
10071ac4b82bSMike Smith 	/*
1008421f2f7dSMike Smith 	 * Start a background consistency check on this drive.
10091ac4b82bSMike Smith 	 */
1010421f2f7dSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
10110fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
1012421f2f7dSMike Smith 	if (sc->mlx_background != 0) {
10130fca6f8bSJohn Baldwin 	    MLX_IO_UNLOCK(sc);
1014421f2f7dSMike Smith 	    *arg = 0x0106;
10151ac4b82bSMike Smith 	    return(EBUSY);
1016421f2f7dSMike Smith 	}
1017421f2f7dSMike Smith 	result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
1018421f2f7dSMike Smith 	switch (result) {
10191ac4b82bSMike Smith 	case 0:
10201ac4b82bSMike Smith 	    error = 0;
10211ac4b82bSMike Smith 	    break;
10221ac4b82bSMike Smith 	case 0x10000:
10231ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
10241ac4b82bSMike Smith 	    break;
10251ac4b82bSMike Smith 	case 0x0002:
10261ac4b82bSMike Smith 	    error = EIO;
10271ac4b82bSMike Smith 	    break;
10281ac4b82bSMike Smith 	case 0x0105:
10291ac4b82bSMike Smith 	    error = ERANGE;
10301ac4b82bSMike Smith 	    break;
1031421f2f7dSMike Smith 	case 0x0106:
1032421f2f7dSMike Smith 	    error = EBUSY;
1033421f2f7dSMike Smith 	    break;
10341ac4b82bSMike Smith 	default:
10351ac4b82bSMike Smith 	    error = EINVAL;
10361ac4b82bSMike Smith 	    break;
10371ac4b82bSMike Smith 	}
1038421f2f7dSMike Smith 	if (error == 0)
1039421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_CHECK;
10400fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
1041421f2f7dSMike Smith 	*arg = result;
10421ac4b82bSMike Smith 	return(error);
10431ac4b82bSMike Smith 
10441ac4b82bSMike Smith     }
10451ac4b82bSMike Smith     return(ENOIOCTL);
10461ac4b82bSMike Smith }
10471ac4b82bSMike Smith 
10481ac4b82bSMike Smith 
10491ac4b82bSMike Smith /********************************************************************************
10501ac4b82bSMike Smith  ********************************************************************************
10511ac4b82bSMike Smith                                                                 Status Monitoring
10521ac4b82bSMike Smith  ********************************************************************************
10531ac4b82bSMike Smith  ********************************************************************************/
10541ac4b82bSMike Smith 
10551ac4b82bSMike Smith /********************************************************************************
10561ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
10571ac4b82bSMike Smith  */
10581ac4b82bSMike Smith static void
mlx_periodic(void * data)10591ac4b82bSMike Smith mlx_periodic(void *data)
10601ac4b82bSMike Smith {
10611ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
10621ac4b82bSMike Smith 
1063da8bb3a3SMike Smith     debug_called(1);
10640fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
10651ac4b82bSMike Smith 
10661ac4b82bSMike Smith     /*
10671ac4b82bSMike Smith      * Run a bus pause?
10681ac4b82bSMike Smith      */
10691ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
10701ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
10711ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
10721ac4b82bSMike Smith 
10731ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
10741ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
1075072d5b98SWarner Losh 	sysbeep(500, SBT_1S);
10761ac4b82bSMike Smith 
10771ac4b82bSMike Smith 	/*
10781ac4b82bSMike Smith 	 * Bus pause still running?
10791ac4b82bSMike Smith 	 */
10801ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
10811ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
10821ac4b82bSMike Smith 
10831ac4b82bSMike Smith 	/* time to stop bus pause? */
10841ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
10851ac4b82bSMike Smith 	    mlx_pause_action(sc);
10861ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
1087072d5b98SWarner Losh 	    sysbeep(500, SBT_1S);
10881ac4b82bSMike Smith 	} else {
1089072d5b98SWarner Losh 	    sysbeep((time_second % 5) * 100 + 500, SBT_1S / 8);
10901ac4b82bSMike Smith 	}
10911ac4b82bSMike Smith 
10921ac4b82bSMike Smith 	/*
10931ac4b82bSMike Smith 	 * Run normal periodic activities?
10941ac4b82bSMike Smith 	 */
10955792b7feSMike Smith     } else if (time_second > (sc->mlx_lastpoll + 10)) {
10961ac4b82bSMike Smith 	sc->mlx_lastpoll = time_second;
10971ac4b82bSMike Smith 
10981ac4b82bSMike Smith 	/*
10991ac4b82bSMike Smith 	 * Check controller status.
11005792b7feSMike Smith 	 *
11015792b7feSMike Smith 	 * XXX Note that this may not actually launch a command in situations of high load.
11021ac4b82bSMike Smith 	 */
1103da8bb3a3SMike Smith 	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
1104da8bb3a3SMike Smith 		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
11051ac4b82bSMike Smith 
11061ac4b82bSMike Smith 	/*
11071ac4b82bSMike Smith 	 * Check system drive status.
11081ac4b82bSMike Smith 	 *
11091ac4b82bSMike Smith 	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
11101ac4b82bSMike Smith 	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
11111ac4b82bSMike Smith 	 *     online.
11121ac4b82bSMike Smith 	 */
11131ac4b82bSMike Smith 	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
11141ac4b82bSMike Smith 			mlx_periodic_enquiry);
11151ac4b82bSMike Smith 
11161ac4b82bSMike Smith     }
11171ac4b82bSMike Smith 
1118421f2f7dSMike Smith     /* get drive rebuild/check status */
1119421f2f7dSMike Smith     /* XXX should check sc->mlx_background if this is only valid while in progress */
1120421f2f7dSMike Smith     mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
1121421f2f7dSMike Smith 
11225792b7feSMike Smith     /* deal with possibly-missed interrupts and timed-out commands */
11230fca6f8bSJohn Baldwin     mlx_done(sc, 1);
11241ac4b82bSMike Smith 
11251ac4b82bSMike Smith     /* reschedule another poll next second or so */
11260fca6f8bSJohn Baldwin     callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc);
11271ac4b82bSMike Smith }
11281ac4b82bSMike Smith 
11291ac4b82bSMike Smith /********************************************************************************
11301ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
11311ac4b82bSMike Smith  */
11321ac4b82bSMike Smith static void
mlx_periodic_enquiry(struct mlx_command * mc)11331ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
11341ac4b82bSMike Smith {
11351ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
11361ac4b82bSMike Smith 
1137da8bb3a3SMike Smith     debug_called(1);
11380fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
11391ac4b82bSMike Smith 
11401ac4b82bSMike Smith     /* Command completed OK? */
11411ac4b82bSMike Smith     if (mc->mc_status != 0) {
1142da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
11431ac4b82bSMike Smith 	goto out;
11441ac4b82bSMike Smith     }
11451ac4b82bSMike Smith 
11461ac4b82bSMike Smith     /* respond to command */
11471ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
11481ac4b82bSMike Smith 	/*
1149da8bb3a3SMike Smith 	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
1150da8bb3a3SMike Smith 	 * pointer yet.
1151da8bb3a3SMike Smith 	 */
1152da8bb3a3SMike Smith     case MLX_CMD_ENQUIRY_OLD:
1153da8bb3a3SMike Smith     {
1154da8bb3a3SMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
1155da8bb3a3SMike Smith 	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
1156da8bb3a3SMike Smith 	int				i;
1157da8bb3a3SMike Smith 
1158da8bb3a3SMike Smith 	/* convert data in-place to new format */
1159da8bb3a3SMike Smith 	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
1160da8bb3a3SMike Smith 	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1161da8bb3a3SMike Smith 	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1162da8bb3a3SMike Smith 	}
1163da8bb3a3SMike Smith 	me->me_misc_flags        = 0;
1164da8bb3a3SMike Smith 	me->me_rebuild_count     = meo->me_rebuild_count;
1165da8bb3a3SMike Smith 	me->me_dead_count        = meo->me_dead_count;
1166da8bb3a3SMike Smith 	me->me_critical_sd_count = meo->me_critical_sd_count;
1167da8bb3a3SMike Smith 	me->me_event_log_seq_num = 0;
1168da8bb3a3SMike Smith 	me->me_offline_sd_count  = meo->me_offline_sd_count;
1169da8bb3a3SMike Smith 	me->me_max_commands      = meo->me_max_commands;
1170da8bb3a3SMike Smith 	me->me_rebuild_flag      = meo->me_rebuild_flag;
1171da8bb3a3SMike Smith 	me->me_fwmajor           = meo->me_fwmajor;
1172da8bb3a3SMike Smith 	me->me_fwminor           = meo->me_fwminor;
1173da8bb3a3SMike Smith 	me->me_status_flags      = meo->me_status_flags;
1174da8bb3a3SMike Smith 	me->me_flash_age         = meo->me_flash_age;
1175da8bb3a3SMike Smith 	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
1176da8bb3a3SMike Smith 	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
1177da8bb3a3SMike Smith 		me->me_drvsize[i] = 0;		/* drive beyond supported range */
1178da8bb3a3SMike Smith 	    } else {
1179da8bb3a3SMike Smith 		me->me_drvsize[i] = meo->me_drvsize[i];
1180da8bb3a3SMike Smith 	    }
1181da8bb3a3SMike Smith 	}
1182da8bb3a3SMike Smith 	me->me_num_sys_drvs = meo->me_num_sys_drvs;
1183da8bb3a3SMike Smith     }
1184da8bb3a3SMike Smith     /* FALLTHROUGH */
1185da8bb3a3SMike Smith 
1186da8bb3a3SMike Smith 	/*
11871ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
11881ac4b82bSMike Smith 	 * checking the event log.
11891ac4b82bSMike Smith 	 */
11901ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
11911ac4b82bSMike Smith     {
11921ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
11931ac4b82bSMike Smith 
1194421f2f7dSMike Smith 	if (sc->mlx_currevent == -1) {
11959eee27f1SMike Smith 	    /* initialise our view of the event log */
11969eee27f1SMike Smith 	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
11975d278f5cSMike Smith 	} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
11981ac4b82bSMike Smith 	    /* record where current events are up to */
11991ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
1200da8bb3a3SMike Smith 	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
12011ac4b82bSMike Smith 
12025d278f5cSMike Smith 	    /* mark the event log as busy */
12030fca6f8bSJohn Baldwin 	    sc->mlx_flags |= MLX_EVENTLOG_BUSY;
12045d278f5cSMike Smith 
12059eee27f1SMike Smith 	    /* drain new eventlog entries */
12061ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
12071ac4b82bSMike Smith 	}
12081ac4b82bSMike Smith 	break;
12091ac4b82bSMike Smith     }
12101ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
12111ac4b82bSMike Smith     {
12121ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
12131ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
12141ac4b82bSMike Smith 	int				i;
12151ac4b82bSMike Smith 
12161ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
12171ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
12181ac4b82bSMike Smith 	     i++) {
12191ac4b82bSMike Smith 
12201ac4b82bSMike Smith 	    /* has state been changed by controller? */
12211ac4b82bSMike Smith 	    if (dr->ms_state != mes[i].sd_state) {
12221ac4b82bSMike Smith 		switch(mes[i].sd_state) {
12231ac4b82bSMike Smith 		case MLX_SYSD_OFFLINE:
12241ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive offline\n");
12251ac4b82bSMike Smith 		    break;
12261ac4b82bSMike Smith 		case MLX_SYSD_ONLINE:
12271ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive online\n");
12281ac4b82bSMike Smith 		    break;
12291ac4b82bSMike Smith 		case MLX_SYSD_CRITICAL:
12301ac4b82bSMike Smith 		    device_printf(dr->ms_disk, "drive critical\n");
12311ac4b82bSMike Smith 		    break;
12321ac4b82bSMike Smith 		}
12331ac4b82bSMike Smith 		/* save new state */
12341ac4b82bSMike Smith 		dr->ms_state = mes[i].sd_state;
12351ac4b82bSMike Smith 	    }
12361ac4b82bSMike Smith 	}
12371ac4b82bSMike Smith 	break;
12381ac4b82bSMike Smith     }
12391ac4b82bSMike Smith     default:
12406e551fb6SDavid E. O'Brien 	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]);
12411ac4b82bSMike Smith 	break;
12421ac4b82bSMike Smith     }
12431ac4b82bSMike Smith 
12441ac4b82bSMike Smith  out:
12451ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12461ac4b82bSMike Smith     mlx_releasecmd(mc);
12471ac4b82bSMike Smith }
12481ac4b82bSMike Smith 
12491b4404f9SScott Long static void
mlx_eventlog_cb(void * arg,bus_dma_segment_t * segs,int nsegments,int error)12501b4404f9SScott Long mlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
12511b4404f9SScott Long {
12521b4404f9SScott Long     struct mlx_command *mc;
12531b4404f9SScott Long 
12541b4404f9SScott Long     mc = (struct mlx_command *)arg;
12551b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
12561b4404f9SScott Long 
12571b4404f9SScott Long     /* build the command to get one entry */
12581b4404f9SScott Long     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1,
12591b4404f9SScott Long 		   mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
12601b4404f9SScott Long     mc->mc_complete = mlx_periodic_eventlog_respond;
12611b4404f9SScott Long     mc->mc_private = mc;
12621b4404f9SScott Long 
12631b4404f9SScott Long     /* start the command */
12641b4404f9SScott Long     if (mlx_start(mc) != 0) {
12651b4404f9SScott Long 	mlx_releasecmd(mc);
12661b4404f9SScott Long 	free(mc->mc_data, M_DEVBUF);
12671b4404f9SScott Long 	mc->mc_data = NULL;
12681b4404f9SScott Long     }
12691b4404f9SScott Long 
12701b4404f9SScott Long }
12711b4404f9SScott Long 
12721ac4b82bSMike Smith /********************************************************************************
12731ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
12741ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
12751ac4b82bSMike Smith  */
12761ac4b82bSMike Smith static void
mlx_periodic_eventlog_poll(struct mlx_softc * sc)12771ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
12781ac4b82bSMike Smith {
12791ac4b82bSMike Smith     struct mlx_command	*mc;
12801ac4b82bSMike Smith     void		*result = NULL;
12811b4404f9SScott Long     int			error = 0;
12821ac4b82bSMike Smith 
1283da8bb3a3SMike Smith     debug_called(1);
12840fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
12851ac4b82bSMike Smith 
12861ac4b82bSMike Smith     /* get ourselves a command buffer */
12871ac4b82bSMike Smith     error = 1;
12881ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
12891ac4b82bSMike Smith 	goto out;
12901b4404f9SScott Long 
12911ac4b82bSMike Smith     /* allocate the response structure */
12921b4404f9SScott Long     if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF,
12931b4404f9SScott Long 			 M_NOWAIT)) == NULL)
12941ac4b82bSMike Smith 	goto out;
12951b4404f9SScott Long 
12961ac4b82bSMike Smith     /* get a command slot */
12971ac4b82bSMike Smith     if (mlx_getslot(mc))
12981ac4b82bSMike Smith 	goto out;
12991ac4b82bSMike Smith 
13001ac4b82bSMike Smith     /* map the command so the controller can see it */
13011ac4b82bSMike Smith     mc->mc_data = result;
130233c8cb18SMike Smith     mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
13031b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
13041b4404f9SScott Long 			    mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT);
13051ac4b82bSMike Smith 
13061ac4b82bSMike Smith  out:
130733c8cb18SMike Smith     if (error != 0) {
13081ac4b82bSMike Smith 	if (mc != NULL)
13091ac4b82bSMike Smith 	    mlx_releasecmd(mc);
13101b4404f9SScott Long 	if ((result != NULL) && (mc->mc_data != NULL))
13111ac4b82bSMike Smith 	    free(result, M_DEVBUF);
13121ac4b82bSMike Smith     }
131333c8cb18SMike Smith }
13141ac4b82bSMike Smith 
13151ac4b82bSMike Smith /********************************************************************************
13161ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
13171ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
13181ac4b82bSMike Smith  */
13191ac4b82bSMike Smith static char *mlx_sense_messages[] = {
13201ac4b82bSMike Smith     "because write recovery failed",
13211ac4b82bSMike Smith     "because of SCSI bus reset failure",
13221ac4b82bSMike Smith     "because of double check condition",
13231ac4b82bSMike Smith     "because it was removed",
13241ac4b82bSMike Smith     "because of gross error on SCSI chip",
13251ac4b82bSMike Smith     "because of bad tag returned from drive",
13261ac4b82bSMike Smith     "because of timeout on SCSI command",
13271ac4b82bSMike Smith     "because of reset SCSI command issued from system",
13281ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
13291ac4b82bSMike Smith     "because of 'kill drive' command from system",
13301ac4b82bSMike Smith     "because of selection timeout",
13311ac4b82bSMike Smith     "due to SCSI phase sequence error",
13321ac4b82bSMike Smith     "due to unknown status"
13331ac4b82bSMike Smith };
13341ac4b82bSMike Smith 
13351ac4b82bSMike Smith static void
mlx_periodic_eventlog_respond(struct mlx_command * mc)13361ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
13371ac4b82bSMike Smith {
13381ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
13391ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
13401ac4b82bSMike Smith     char			*reason;
13411ac4b82bSMike Smith 
1342da8bb3a3SMike Smith     debug_called(1);
13430fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
13441ac4b82bSMike Smith 
13455792b7feSMike Smith     sc->mlx_lastevent++;		/* next message... */
13461ac4b82bSMike Smith     if (mc->mc_status == 0) {
13471ac4b82bSMike Smith 
13481ac4b82bSMike Smith 	/* handle event log message */
13491ac4b82bSMike Smith 	switch(el->el_type) {
13501ac4b82bSMike Smith 	    /*
13511ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
13521ac4b82bSMike Smith 	     * The tests here are probably incomplete.
13531ac4b82bSMike Smith 	     */
13541ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
13551ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
13561ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
13571ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
135873a1170aSPedro F. Giffuni 		if (el->el_asq < nitems(mlx_sense_messages)) {
13591ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
13601ac4b82bSMike Smith 		} else {
13611ac4b82bSMike Smith 		    reason = "for unknown reason";
13621ac4b82bSMike Smith 		}
13631ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
13641ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
13651ac4b82bSMike Smith 	    }
13661ac4b82bSMike Smith 	    /* SCSI drive was reset? */
13671ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
13681ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
13691ac4b82bSMike Smith 			      el->el_channel, el->el_target);
13701ac4b82bSMike Smith 	    }
13711ac4b82bSMike Smith 	    /* SCSI drive error? */
13721ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
13731ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
13741ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
13751ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
13761ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
13771ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
13781ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
13791ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
13801ac4b82bSMike Smith 	    }
13811ac4b82bSMike Smith 	    break;
13821ac4b82bSMike Smith 
13831ac4b82bSMike Smith 	default:
13841ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
13851ac4b82bSMike Smith 	    break;
13861ac4b82bSMike Smith 	}
13871ac4b82bSMike Smith     } else {
13881ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
13895d278f5cSMike Smith 	/* give up on all the outstanding messages, as we may have come unsynched */
13905d278f5cSMike Smith 	sc->mlx_lastevent = sc->mlx_currevent;
13911ac4b82bSMike Smith     }
13921ac4b82bSMike Smith 
13931ac4b82bSMike Smith     /* dispose of command and data */
13941ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
13951ac4b82bSMike Smith     mlx_releasecmd(mc);
13961ac4b82bSMike Smith 
13971ac4b82bSMike Smith     /* is there another message to obtain? */
13985d278f5cSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent) {
13991ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
14005d278f5cSMike Smith     } else {
14015d278f5cSMike Smith 	/* clear log-busy status */
14020fca6f8bSJohn Baldwin 	sc->mlx_flags &= ~MLX_EVENTLOG_BUSY;
14035d278f5cSMike Smith     }
14041ac4b82bSMike Smith }
14051ac4b82bSMike Smith 
14061ac4b82bSMike Smith /********************************************************************************
1407421f2f7dSMike Smith  * Handle check/rebuild operations in progress.
14081ac4b82bSMike Smith  */
14091ac4b82bSMike Smith static void
mlx_periodic_rebuild(struct mlx_command * mc)14101ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
14111ac4b82bSMike Smith {
14121ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
1413421f2f7dSMike Smith     struct mlx_rebuild_status	*mr = (struct mlx_rebuild_status *)mc->mc_data;
14141ac4b82bSMike Smith 
14150fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
14161ac4b82bSMike Smith     switch(mc->mc_status) {
1417421f2f7dSMike Smith     case 0:				/* operation running, update stats */
1418421f2f7dSMike Smith 	sc->mlx_rebuildstat = *mr;
1419421f2f7dSMike Smith 
1420421f2f7dSMike Smith 	/* spontaneous rebuild/check? */
1421421f2f7dSMike Smith 	if (sc->mlx_background == 0) {
1422421f2f7dSMike Smith 	    sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
1423421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
1424421f2f7dSMike Smith 	}
14251ac4b82bSMike Smith 	break;
14261ac4b82bSMike Smith 
1427421f2f7dSMike Smith     case 0x0105:			/* nothing running, finalise stats and report */
1428421f2f7dSMike Smith 	switch(sc->mlx_background) {
1429421f2f7dSMike Smith 	case MLX_BACKGROUND_CHECK:
1430421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");	/* XXX print drive? */
1431421f2f7dSMike Smith 	    break;
1432421f2f7dSMike Smith 	case MLX_BACKGROUND_REBUILD:
1433421f2f7dSMike Smith 	    device_printf(sc->mlx_dev, "drive rebuild completed\n");	/* XXX print channel/target? */
1434421f2f7dSMike Smith 	    break;
1435421f2f7dSMike Smith 	case MLX_BACKGROUND_SPONTANEOUS:
1436421f2f7dSMike Smith 	default:
1437421f2f7dSMike Smith 	    /* if we have previously been non-idle, report the transition */
1438421f2f7dSMike Smith 	    if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
1439421f2f7dSMike Smith 		device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
14401ac4b82bSMike Smith 	    }
1441421f2f7dSMike Smith 	}
1442421f2f7dSMike Smith 	sc->mlx_background = 0;
1443421f2f7dSMike Smith 	sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
14441ac4b82bSMike Smith 	break;
14451ac4b82bSMike Smith     }
14461ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
14471ac4b82bSMike Smith     mlx_releasecmd(mc);
14481ac4b82bSMike Smith }
14491ac4b82bSMike Smith 
14501ac4b82bSMike Smith /********************************************************************************
14511ac4b82bSMike Smith  ********************************************************************************
14521ac4b82bSMike Smith                                                                     Channel Pause
14531ac4b82bSMike Smith  ********************************************************************************
14541ac4b82bSMike Smith  ********************************************************************************/
14551ac4b82bSMike Smith 
14561ac4b82bSMike Smith /********************************************************************************
14571ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
14581ac4b82bSMike Smith  * the pause.
14591ac4b82bSMike Smith  */
14601ac4b82bSMike Smith static void
mlx_pause_action(struct mlx_softc * sc)14611ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
14621ac4b82bSMike Smith {
14631ac4b82bSMike Smith     struct mlx_command	*mc;
14641ac4b82bSMike Smith     int			failsafe, i, command;
14651ac4b82bSMike Smith 
14660fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
14670fca6f8bSJohn Baldwin 
14681ac4b82bSMike Smith     /* What are we doing here? */
14691ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
14701ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
14711ac4b82bSMike Smith 	failsafe = 0;
14721ac4b82bSMike Smith 
14731ac4b82bSMike Smith     } else {
14741ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
14751ac4b82bSMike Smith 
14761ac4b82bSMike Smith 	/*
14771ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
14781ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
14791ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
14801ac4b82bSMike Smith 	 */
14811ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
14821ac4b82bSMike Smith 	if (failsafe > 0xf) {
14831ac4b82bSMike Smith 	    failsafe = 0xf;
14841ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
14851ac4b82bSMike Smith 	}
14861ac4b82bSMike Smith     }
14871ac4b82bSMike Smith 
14881ac4b82bSMike Smith     /* build commands for every channel requested */
14899eee27f1SMike Smith     for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
14901ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
14911ac4b82bSMike Smith 
14921ac4b82bSMike Smith 	    /* get ourselves a command buffer */
14931ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
14941ac4b82bSMike Smith 		goto fail;
14951ac4b82bSMike Smith 	    /* get a command slot */
14961ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
14971ac4b82bSMike Smith 	    if (mlx_getslot(mc))
14981ac4b82bSMike Smith 		goto fail;
14991ac4b82bSMike Smith 
15001ac4b82bSMike Smith 	    /* build the command */
15011ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
15021ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
15031ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
15041ac4b82bSMike Smith 	    if (mlx_start(mc))
15051ac4b82bSMike Smith 		goto fail;
15061ac4b82bSMike Smith 	    /* command submitted OK */
15071ac4b82bSMike Smith 	    return;
15081ac4b82bSMike Smith 
15091ac4b82bSMike Smith 	fail:
15101ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
15111ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
15121ac4b82bSMike Smith 	    if (mc != NULL)
15131ac4b82bSMike Smith 		mlx_releasecmd(mc);
15141ac4b82bSMike Smith 	}
15151ac4b82bSMike Smith     }
15161ac4b82bSMike Smith }
15171ac4b82bSMike Smith 
15181ac4b82bSMike Smith static void
mlx_pause_done(struct mlx_command * mc)15191ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
15201ac4b82bSMike Smith {
15211ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
15221ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
15231ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
15241ac4b82bSMike Smith 
15250fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
15261ac4b82bSMike Smith     if (mc->mc_status != 0) {
15271ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
15281ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
15291ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
15301ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
153172c10febSPeter Wemm 		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
15321ac4b82bSMike Smith     } else {
15331ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
15341ac4b82bSMike Smith     }
15351ac4b82bSMike Smith     mlx_releasecmd(mc);
15361ac4b82bSMike Smith }
15371ac4b82bSMike Smith 
15381ac4b82bSMike Smith /********************************************************************************
15391ac4b82bSMike Smith  ********************************************************************************
15401ac4b82bSMike Smith                                                                Command Submission
15411ac4b82bSMike Smith  ********************************************************************************
15421ac4b82bSMike Smith  ********************************************************************************/
15431ac4b82bSMike Smith 
15441b4404f9SScott Long static void
mlx_enquire_cb(void * arg,bus_dma_segment_t * segs,int nsegments,int error)15451b4404f9SScott Long mlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
15461b4404f9SScott Long {
15471b4404f9SScott Long     struct mlx_softc *sc;
15481b4404f9SScott Long     struct mlx_command *mc;
15491b4404f9SScott Long 
15501b4404f9SScott Long     mc = (struct mlx_command *)arg;
15511b4404f9SScott Long     if (error)
15521b4404f9SScott Long 	return;
15531b4404f9SScott Long 
15541b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
15551b4404f9SScott Long 
15561b4404f9SScott Long     /* build an enquiry command */
15571b4404f9SScott Long     sc = mc->mc_sc;
15581b4404f9SScott Long     mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
15591b4404f9SScott Long 
15601b4404f9SScott Long     /* do we want a completion callback? */
15611b4404f9SScott Long     if (mc->mc_complete != NULL) {
15621b4404f9SScott Long 	if ((error = mlx_start(mc)) != 0)
15631b4404f9SScott Long 	    return;
15641b4404f9SScott Long     } else {
15651b4404f9SScott Long 	/* run the command in either polled or wait mode */
15661b4404f9SScott Long 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) :
15671b4404f9SScott Long 						mlx_poll_command(mc))
15681b4404f9SScott Long 	    return;
15691b4404f9SScott Long 
15701b4404f9SScott Long 	/* command completed OK? */
15711b4404f9SScott Long 	if (mc->mc_status != 0) {
15721b4404f9SScott Long 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n",
15731b4404f9SScott Long 			  mlx_diagnose_command(mc));
15741b4404f9SScott Long 	    return;
15751b4404f9SScott Long 	}
15761b4404f9SScott Long     }
15771b4404f9SScott Long }
15781b4404f9SScott Long 
15791ac4b82bSMike Smith /********************************************************************************
15801ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
15811ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
15821ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
15831ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
15841ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
15851ac4b82bSMike Smith  */
15861ac4b82bSMike Smith static void *
mlx_enquire(struct mlx_softc * sc,int command,size_t bufsize,void (* complete)(struct mlx_command * mc))15871ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
15881ac4b82bSMike Smith {
15891ac4b82bSMike Smith     struct mlx_command	*mc;
15901ac4b82bSMike Smith     void		*result;
15911ac4b82bSMike Smith     int			error;
15921ac4b82bSMike Smith 
1593da8bb3a3SMike Smith     debug_called(1);
15940fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
15951ac4b82bSMike Smith 
15961ac4b82bSMike Smith     /* get ourselves a command buffer */
15971ac4b82bSMike Smith     error = 1;
15981ac4b82bSMike Smith     result = NULL;
15991ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16001ac4b82bSMike Smith 	goto out;
16011ac4b82bSMike Smith     /* allocate the response structure */
16021ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
16031ac4b82bSMike Smith 	goto out;
16041ac4b82bSMike Smith     /* get a command slot */
16051ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
16061ac4b82bSMike Smith     if (mlx_getslot(mc))
16071ac4b82bSMike Smith 	goto out;
16081ac4b82bSMike Smith 
16091ac4b82bSMike Smith     /* map the command so the controller can see it */
16101ac4b82bSMike Smith     mc->mc_data = result;
16111ac4b82bSMike Smith     mc->mc_length = bufsize;
16121b4404f9SScott Long     mc->mc_command = command;
16131ac4b82bSMike Smith 
16141ac4b82bSMike Smith     if (complete != NULL) {
16151ac4b82bSMike Smith 	mc->mc_complete = complete;
16161ac4b82bSMike Smith 	mc->mc_private = mc;
16171b4404f9SScott Long     }
16181ac4b82bSMike Smith 
16191b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
16201b4404f9SScott Long 			    mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT);
16211b4404f9SScott Long 
16221ac4b82bSMike Smith  out:
16231ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
162423691262SSam Leffler     if ((mc != NULL) && (mc->mc_complete == NULL))
16251ac4b82bSMike Smith 	mlx_releasecmd(mc);
162633c8cb18SMike Smith     /* we got an error, and we allocated a result */
1627d4881905SScott Long     if ((error != 0) && (result != NULL)) {
1628d4881905SScott Long 	free(result, M_DEVBUF);
1629a7700303SScott Long 	result = NULL;
16301ac4b82bSMike Smith     }
16311ac4b82bSMike Smith     return(result);
16321ac4b82bSMike Smith }
16331ac4b82bSMike Smith 
16341ac4b82bSMike Smith 
16351ac4b82bSMike Smith /********************************************************************************
16361ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
16371ac4b82bSMike Smith  *
16381ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
16391ac4b82bSMike Smith  * the flush operation completes or fails.
16401ac4b82bSMike Smith  */
16411ac4b82bSMike Smith static int
mlx_flush(struct mlx_softc * sc)16421ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
16431ac4b82bSMike Smith {
16441ac4b82bSMike Smith     struct mlx_command	*mc;
16451ac4b82bSMike Smith     int			error;
16461ac4b82bSMike Smith 
1647da8bb3a3SMike Smith     debug_called(1);
16480fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
16491ac4b82bSMike Smith 
16501ac4b82bSMike Smith     /* get ourselves a command buffer */
16511ac4b82bSMike Smith     error = 1;
16521ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16531ac4b82bSMike Smith 	goto out;
16541ac4b82bSMike Smith     /* get a command slot */
16551ac4b82bSMike Smith     if (mlx_getslot(mc))
16561ac4b82bSMike Smith 	goto out;
16571ac4b82bSMike Smith 
16581ac4b82bSMike Smith     /* build a flush command */
16591ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
16601ac4b82bSMike Smith 
16615792b7feSMike Smith     /* can't assume that interrupts are going to work here, so play it safe */
16625792b7feSMike Smith     if (mlx_poll_command(mc))
16631ac4b82bSMike Smith 	goto out;
16641ac4b82bSMike Smith 
16651ac4b82bSMike Smith     /* command completed OK? */
16661ac4b82bSMike Smith     if (mc->mc_status != 0) {
16671ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
16681ac4b82bSMike Smith 	goto out;
16691ac4b82bSMike Smith     }
16701ac4b82bSMike Smith 
16711ac4b82bSMike Smith     error = 0;			/* success */
16721ac4b82bSMike Smith  out:
16731ac4b82bSMike Smith     if (mc != NULL)
16741ac4b82bSMike Smith 	mlx_releasecmd(mc);
16751ac4b82bSMike Smith     return(error);
16761ac4b82bSMike Smith }
16771ac4b82bSMike Smith 
16781ac4b82bSMike Smith /********************************************************************************
1679421f2f7dSMike Smith  * Start a background consistency check on (drive).
1680421f2f7dSMike Smith  *
1681421f2f7dSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
1682421f2f7dSMike Smith  * operation has started or been refused.
1683421f2f7dSMike Smith  */
1684421f2f7dSMike Smith static int
mlx_check(struct mlx_softc * sc,int drive)1685421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive)
1686421f2f7dSMike Smith {
1687421f2f7dSMike Smith     struct mlx_command	*mc;
1688421f2f7dSMike Smith     int			error;
1689421f2f7dSMike Smith 
1690421f2f7dSMike Smith     debug_called(1);
16910fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
1692421f2f7dSMike Smith 
1693421f2f7dSMike Smith     /* get ourselves a command buffer */
1694421f2f7dSMike Smith     error = 0x10000;
1695421f2f7dSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
1696421f2f7dSMike Smith 	goto out;
1697421f2f7dSMike Smith     /* get a command slot */
1698421f2f7dSMike Smith     if (mlx_getslot(mc))
1699421f2f7dSMike Smith 	goto out;
1700421f2f7dSMike Smith 
1701421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
1702421f2f7dSMike Smith     mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
1703421f2f7dSMike Smith 
1704421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1705421f2f7dSMike Smith     if (mlx_wait_command(mc))
1706421f2f7dSMike Smith 	goto out;
1707421f2f7dSMike Smith 
1708421f2f7dSMike Smith     /* command completed OK? */
1709421f2f7dSMike Smith     if (mc->mc_status != 0) {
1710421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
1711421f2f7dSMike Smith     } else {
1712421f2f7dSMike Smith 	device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
1713421f2f7dSMike Smith     }
1714421f2f7dSMike Smith     error = mc->mc_status;
1715421f2f7dSMike Smith 
1716421f2f7dSMike Smith  out:
1717421f2f7dSMike Smith     if (mc != NULL)
1718421f2f7dSMike Smith 	mlx_releasecmd(mc);
1719421f2f7dSMike Smith     return(error);
1720421f2f7dSMike Smith }
1721421f2f7dSMike Smith 
1722421f2f7dSMike Smith /********************************************************************************
1723421f2f7dSMike Smith  * Start a background rebuild of the physical drive at (channel),(target).
17241ac4b82bSMike Smith  *
17251ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
17261ac4b82bSMike Smith  * operation has started or been refused.
17271ac4b82bSMike Smith  */
17281ac4b82bSMike Smith static int
mlx_rebuild(struct mlx_softc * sc,int channel,int target)17291ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
17301ac4b82bSMike Smith {
17311ac4b82bSMike Smith     struct mlx_command	*mc;
17321ac4b82bSMike Smith     int			error;
17331ac4b82bSMike Smith 
1734da8bb3a3SMike Smith     debug_called(1);
17350fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
17361ac4b82bSMike Smith 
17371ac4b82bSMike Smith     /* get ourselves a command buffer */
17381ac4b82bSMike Smith     error = 0x10000;
17391ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
17401ac4b82bSMike Smith 	goto out;
17411ac4b82bSMike Smith     /* get a command slot */
17421ac4b82bSMike Smith     if (mlx_getslot(mc))
17431ac4b82bSMike Smith 	goto out;
17441ac4b82bSMike Smith 
1745421f2f7dSMike Smith     /* build a checkasync command, set the "fix it" flag */
17461ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
17471ac4b82bSMike Smith 
1748421f2f7dSMike Smith     /* start the command and wait for it to be returned */
1749421f2f7dSMike Smith     if (mlx_wait_command(mc))
17501ac4b82bSMike Smith 	goto out;
17511ac4b82bSMike Smith 
17521ac4b82bSMike Smith     /* command completed OK? */
17531ac4b82bSMike Smith     if (mc->mc_status != 0) {
17541ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
17551ac4b82bSMike Smith     } else {
1756421f2f7dSMike Smith 	device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
17571ac4b82bSMike Smith     }
17581ac4b82bSMike Smith     error = mc->mc_status;
17591ac4b82bSMike Smith 
17601ac4b82bSMike Smith  out:
17611ac4b82bSMike Smith     if (mc != NULL)
17621ac4b82bSMike Smith 	mlx_releasecmd(mc);
17631ac4b82bSMike Smith     return(error);
17641ac4b82bSMike Smith }
17651ac4b82bSMike Smith 
17661ac4b82bSMike Smith /********************************************************************************
17671ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
17681ac4b82bSMike Smith  *
17691ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
17701ac4b82bSMike Smith  */
17711ac4b82bSMike Smith static int
mlx_wait_command(struct mlx_command * mc)17721ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
17731ac4b82bSMike Smith {
17741ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17751ac4b82bSMike Smith     int			error, count;
17761ac4b82bSMike Smith 
1777da8bb3a3SMike Smith     debug_called(1);
17780fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
17791ac4b82bSMike Smith 
17801ac4b82bSMike Smith     mc->mc_complete = NULL;
17811ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
17821ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
17831ac4b82bSMike Smith 	return(error);
17841ac4b82bSMike Smith 
17851ac4b82bSMike Smith     count = 0;
17861ac4b82bSMike Smith     /* XXX better timeout? */
17871ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
17880fca6f8bSJohn Baldwin 	mtx_sleep(mc->mc_private, &sc->mlx_io_lock, PRIBIO | PCATCH, "mlxwcmd", hz);
17891ac4b82bSMike Smith     }
17901ac4b82bSMike Smith 
17911ac4b82bSMike Smith     if (mc->mc_status != 0) {
1792da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
17931ac4b82bSMike Smith 	return(EIO);
17941ac4b82bSMike Smith     }
17951ac4b82bSMike Smith     return(0);
17961ac4b82bSMike Smith }
17971ac4b82bSMike Smith 
17981ac4b82bSMike Smith 
17991ac4b82bSMike Smith /********************************************************************************
18001ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
18011ac4b82bSMike Smith  *
1802da8bb3a3SMike Smith  * Should only be used when interrupts can't be relied upon. Returns 0 on
18031ac4b82bSMike Smith  * success, nonzero on error.
18041ac4b82bSMike Smith  * Successfully completed commands are dequeued.
18051ac4b82bSMike Smith  */
18061ac4b82bSMike Smith static int
mlx_poll_command(struct mlx_command * mc)18071ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
18081ac4b82bSMike Smith {
18091ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18100fca6f8bSJohn Baldwin     int			error, count;
18111ac4b82bSMike Smith 
1812da8bb3a3SMike Smith     debug_called(1);
18130fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
18141ac4b82bSMike Smith 
18151ac4b82bSMike Smith     mc->mc_complete = NULL;
18161ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
18171ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
18181ac4b82bSMike Smith 	return(error);
18191ac4b82bSMike Smith 
18201ac4b82bSMike Smith     count = 0;
18211ac4b82bSMike Smith     do {
18221ac4b82bSMike Smith 	/* poll for completion */
18230fca6f8bSJohn Baldwin 	mlx_done(mc->mc_sc, 1);
1824da8bb3a3SMike Smith 
1825da8bb3a3SMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
18261ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
18274b006d7bSMike Smith 	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
18281ac4b82bSMike Smith 	return(0);
18291ac4b82bSMike Smith     }
1830421f2f7dSMike Smith     device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
18311ac4b82bSMike Smith     return(EIO);
18321ac4b82bSMike Smith }
18331ac4b82bSMike Smith 
18341b4404f9SScott Long void
mlx_startio_cb(void * arg,bus_dma_segment_t * segs,int nsegments,int error)18351b4404f9SScott Long mlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
18361b4404f9SScott Long {
18371b4404f9SScott Long     struct mlx_command	*mc;
18381b4404f9SScott Long     struct mlxd_softc	*mlxd;
18391b4404f9SScott Long     struct mlx_softc	*sc;
1840b04e4c12SJohn Baldwin     struct bio		*bp;
18411b4404f9SScott Long     int			blkcount;
18421b4404f9SScott Long     int			driveno;
18431b4404f9SScott Long     int			cmd;
18441b4404f9SScott Long 
18451b4404f9SScott Long     mc = (struct mlx_command *)arg;
18461b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
18471b4404f9SScott Long 
18481b4404f9SScott Long     sc = mc->mc_sc;
18491b4404f9SScott Long     bp = mc->mc_private;
18501b4404f9SScott Long 
1851b04e4c12SJohn Baldwin     if (bp->bio_cmd == BIO_READ) {
18521b4404f9SScott Long 	mc->mc_flags |= MLX_CMD_DATAIN;
18531b4404f9SScott Long 	cmd = MLX_CMD_READSG;
18541b4404f9SScott Long     } else {
18551b4404f9SScott Long 	mc->mc_flags |= MLX_CMD_DATAOUT;
18561b4404f9SScott Long 	cmd = MLX_CMD_WRITESG;
18571b4404f9SScott Long     }
18581b4404f9SScott Long 
18591b4404f9SScott Long     /* build a suitable I/O command (assumes 512-byte rounded transfers) */
1860b04e4c12SJohn Baldwin     mlxd = bp->bio_disk->d_drv1;
18611b4404f9SScott Long     driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
1862057b4402SPedro F. Giffuni     blkcount = howmany(bp->bio_bcount, MLX_BLKSIZE);
18631b4404f9SScott Long 
1864b04e4c12SJohn Baldwin     if ((bp->bio_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
18651b4404f9SScott Long 	device_printf(sc->mlx_dev,
18661b4404f9SScott Long 		      "I/O beyond end of unit (%lld,%d > %lu)\n",
1867b04e4c12SJohn Baldwin 		      (long long)bp->bio_pblkno, blkcount,
18681b4404f9SScott Long 		      (u_long)sc->mlx_sysdrive[driveno].ms_size);
18691b4404f9SScott Long 
18701b4404f9SScott Long     /*
18711b4404f9SScott Long      * Build the I/O command.  Note that the SG list type bits are set to zero,
18721b4404f9SScott Long      * denoting the format of SG list that we are using.
18731b4404f9SScott Long      */
18741b4404f9SScott Long     if (sc->mlx_iftype == MLX_IFTYPE_2) {
18751b4404f9SScott Long 	mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD :
18761b4404f9SScott Long 						      MLX_CMD_READSG_OLD,
18771b4404f9SScott Long 		       blkcount & 0xff, 	/* xfer length low byte */
1878b04e4c12SJohn Baldwin 		       bp->bio_pblkno,		/* physical block number */
18791b4404f9SScott Long 		       driveno,			/* target drive number */
18801b4404f9SScott Long 		       mc->mc_sgphys,		/* location of SG list */
18811b4404f9SScott Long 		       mc->mc_nsgent & 0x3f);	/* size of SG list */
18821b4404f9SScott Long 	} else {
18831b4404f9SScott Long 	mlx_make_type5(mc, cmd,
18841b4404f9SScott Long 		       blkcount & 0xff, 	/* xfer length low byte */
18851b4404f9SScott Long 		       (driveno << 3) | ((blkcount >> 8) & 0x07),
18861b4404f9SScott Long 						/* target+length high 3 bits */
1887b04e4c12SJohn Baldwin 		       bp->bio_pblkno,		/* physical block number */
18881b4404f9SScott Long 		       mc->mc_sgphys,		/* location of SG list */
18891b4404f9SScott Long 		       mc->mc_nsgent & 0x3f);	/* size of SG list */
18901b4404f9SScott Long     }
18911b4404f9SScott Long 
18921b4404f9SScott Long     /* try to give command to controller */
18931b4404f9SScott Long     if (mlx_start(mc) != 0) {
18941b4404f9SScott Long 	/* fail the command */
18951b4404f9SScott Long 	mc->mc_status = MLX_STATUS_WEDGED;
18961b4404f9SScott Long 	mlx_completeio(mc);
18971b4404f9SScott Long     }
18980fca6f8bSJohn Baldwin 
18990fca6f8bSJohn Baldwin     sc->mlx_state &= ~MLX_STATE_QFROZEN;
19001b4404f9SScott Long }
19011b4404f9SScott Long 
19021ac4b82bSMike Smith /********************************************************************************
19031ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
19041ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
19051ac4b82bSMike Smith  */
19061ac4b82bSMike Smith static void
mlx_startio(struct mlx_softc * sc)19071ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
19081ac4b82bSMike Smith {
19091ac4b82bSMike Smith     struct mlx_command	*mc;
1910b04e4c12SJohn Baldwin     struct bio		*bp;
19111b4404f9SScott Long     int			error;
19121ac4b82bSMike Smith 
19130fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
19145792b7feSMike Smith 
19151ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
19161ac4b82bSMike Smith     for (;;) {
19170fca6f8bSJohn Baldwin 	if (sc->mlx_state & MLX_STATE_QFROZEN)
19180fca6f8bSJohn Baldwin 	    break;
19191ac4b82bSMike Smith 
19201ac4b82bSMike Smith 	/* see if there's work to be done */
1921b04e4c12SJohn Baldwin 	if ((bp = bioq_first(&sc->mlx_bioq)) == NULL)
19221ac4b82bSMike Smith 	    break;
19231ac4b82bSMike Smith 	/* get a command */
19241ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
19251ac4b82bSMike Smith 	    break;
19261ac4b82bSMike Smith 	/* get a slot for the command */
19271ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
19281ac4b82bSMike Smith 	    mlx_releasecmd(mc);
19291ac4b82bSMike Smith 	    break;
19301ac4b82bSMike Smith 	}
19311ac4b82bSMike Smith 	/* get the buf containing our work */
1932b04e4c12SJohn Baldwin 	bioq_remove(&sc->mlx_bioq, bp);
19331ac4b82bSMike Smith 	sc->mlx_waitbufs--;
19341ac4b82bSMike Smith 
19351ac4b82bSMike Smith 	/* connect the buf to the command */
19361ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
19371ac4b82bSMike Smith 	mc->mc_private = bp;
1938b04e4c12SJohn Baldwin 	mc->mc_data = bp->bio_data;
1939b04e4c12SJohn Baldwin 	mc->mc_length = bp->bio_bcount;
19401ac4b82bSMike Smith 
19411ac4b82bSMike Smith 	/* map the command so the controller can work with it */
19421b4404f9SScott Long 	error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
19431b4404f9SScott Long 				mc->mc_length, mlx_startio_cb, mc, 0);
19441b4404f9SScott Long 	if (error == EINPROGRESS) {
19450fca6f8bSJohn Baldwin 	    sc->mlx_state |= MLX_STATE_QFROZEN;
19461b4404f9SScott Long 	    break;
1947da8bb3a3SMike Smith 	}
19481ac4b82bSMike Smith     }
19491ac4b82bSMike Smith }
19501ac4b82bSMike Smith 
19511ac4b82bSMike Smith /********************************************************************************
19521ac4b82bSMike Smith  * Handle completion of an I/O command.
19531ac4b82bSMike Smith  */
19541ac4b82bSMike Smith static void
mlx_completeio(struct mlx_command * mc)19551ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
19561ac4b82bSMike Smith {
19571ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
1958b04e4c12SJohn Baldwin     struct bio		*bp = mc->mc_private;
1959b04e4c12SJohn Baldwin     struct mlxd_softc	*mlxd = bp->bio_disk->d_drv1;
19601ac4b82bSMike Smith 
19610fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
19621ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
1963b04e4c12SJohn Baldwin 	bp->bio_error = EIO;
1964b04e4c12SJohn Baldwin 	bp->bio_flags |= BIO_ERROR;
19651ac4b82bSMike Smith 
19661ac4b82bSMike Smith 	switch(mc->mc_status) {
19671ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
19681ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
1969f6b84b08SMike Smith 	    /* should signal this with a return code */
19701ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
19711ac4b82bSMike Smith 	    break;
19721ac4b82bSMike Smith 
19731ac4b82bSMike Smith 	default:				/* other I/O error */
19741ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
19751ac4b82bSMike Smith #if 0
1976cd4ace0cSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
1977b04e4c12SJohn Baldwin 			  bp->bio_bcount, bp->bio_bcount / MLX_BLKSIZE, bp->bio_pblkno);
19781ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
19791ac4b82bSMike Smith #endif
19801ac4b82bSMike Smith 	    break;
19811ac4b82bSMike Smith 	}
19821ac4b82bSMike Smith     }
19831ac4b82bSMike Smith     mlx_releasecmd(mc);
19841ac4b82bSMike Smith     mlxd_intr(bp);
19851ac4b82bSMike Smith }
19861ac4b82bSMike Smith 
19871b4404f9SScott Long void
mlx_user_cb(void * arg,bus_dma_segment_t * segs,int nsegments,int error)19881b4404f9SScott Long mlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
19891b4404f9SScott Long {
19901b4404f9SScott Long     struct mlx_usercommand *mu;
19911b4404f9SScott Long     struct mlx_command *mc;
19921b4404f9SScott Long     struct mlx_dcdb	*dcdb;
19931b4404f9SScott Long 
19941b4404f9SScott Long     mc = (struct mlx_command *)arg;
19951b4404f9SScott Long     if (error)
19961b4404f9SScott Long 	return;
19971b4404f9SScott Long 
19981b4404f9SScott Long     mlx_setup_dmamap(mc, segs, nsegments, error);
19991b4404f9SScott Long 
20001b4404f9SScott Long     mu = (struct mlx_usercommand *)mc->mc_private;
20011b4404f9SScott Long     dcdb = NULL;
20021b4404f9SScott Long 
20031b4404f9SScott Long     /*
20041b4404f9SScott Long      * If this is a passthrough SCSI command, the DCDB is packed at the
20051b4404f9SScott Long      * beginning of the data area.  Fix up the DCDB to point to the correct
20061b4404f9SScott Long      * physical address and override any bufptr supplied by the caller since
20071b4404f9SScott Long      * we know what it's meant to be.
20081b4404f9SScott Long      */
20091b4404f9SScott Long     if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
20101b4404f9SScott Long 	dcdb = (struct mlx_dcdb *)mc->mc_data;
20111b4404f9SScott Long 	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
20121b4404f9SScott Long 	mu->mu_bufptr = 8;
20131b4404f9SScott Long     }
20141b4404f9SScott Long 
20151b4404f9SScott Long     /*
20161b4404f9SScott Long      * If there's a data buffer, fix up the command's buffer pointer.
20171b4404f9SScott Long      */
20181b4404f9SScott Long     if (mu->mu_datasize > 0) {
20191b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
20201b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
20211b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
20221b4404f9SScott Long 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
20231b4404f9SScott Long     }
20241b4404f9SScott Long     debug(0, "command fixup");
20251b4404f9SScott Long 
20261b4404f9SScott Long     /* submit the command and wait */
20271b4404f9SScott Long     if (mlx_wait_command(mc) != 0)
20281b4404f9SScott Long 	return;
20291b4404f9SScott Long 
20301b4404f9SScott Long }
20311b4404f9SScott Long 
20321ac4b82bSMike Smith /********************************************************************************
20331ac4b82bSMike Smith  * Take a command from user-space and try to run it.
2034da8bb3a3SMike Smith  *
2035da8bb3a3SMike Smith  * XXX Note that this can't perform very much in the way of error checking, and
2036da8bb3a3SMike Smith  *     as such, applications _must_ be considered trustworthy.
2037da8bb3a3SMike Smith  * XXX Commands using S/G for data are not supported.
20381ac4b82bSMike Smith  */
20391ac4b82bSMike Smith static int
mlx_user_command(struct mlx_softc * sc,struct mlx_usercommand * mu)20401ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
20411ac4b82bSMike Smith {
20421ac4b82bSMike Smith     struct mlx_command	*mc;
20431ac4b82bSMike Smith     void		*kbuf;
20441ac4b82bSMike Smith     int			error;
20451ac4b82bSMike Smith 
2046da8bb3a3SMike Smith     debug_called(0);
2047da8bb3a3SMike Smith 
20481ac4b82bSMike Smith     kbuf = NULL;
20491ac4b82bSMike Smith     mc = NULL;
20501ac4b82bSMike Smith     error = ENOMEM;
2051da8bb3a3SMike Smith 
2052da8bb3a3SMike Smith     /* get ourselves a command and copy in from user space */
20530fca6f8bSJohn Baldwin     MLX_IO_LOCK(sc);
20540fca6f8bSJohn Baldwin     if ((mc = mlx_alloccmd(sc)) == NULL) {
20550fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
2056001ea8fbSSam Leffler 	return(error);
20570fca6f8bSJohn Baldwin     }
20581ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
2059da8bb3a3SMike Smith     debug(0, "got command buffer");
2060da8bb3a3SMike Smith 
20611b4404f9SScott Long     /*
20621b4404f9SScott Long      * if we need a buffer for data transfer, allocate one and copy in its
20631b4404f9SScott Long      * initial contents
20641b4404f9SScott Long      */
2065da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
206652c9ce25SScott Long 	if (mu->mu_datasize > MLX_MAXPHYS) {
2067aa083c3dSSam Leffler 	    error = EINVAL;
2068aa083c3dSSam Leffler 	    goto out;
2069aa083c3dSSam Leffler 	}
20700fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
2071556cd18fSZhenlei Huang 	kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK);
2072556cd18fSZhenlei Huang 	if ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) {
20730fca6f8bSJohn Baldwin 	    MLX_IO_LOCK(sc);
20741ac4b82bSMike Smith 	    goto out;
20750fca6f8bSJohn Baldwin 	}
20760fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
2077da8bb3a3SMike Smith 	debug(0, "got kernel buffer");
2078da8bb3a3SMike Smith     }
20791ac4b82bSMike Smith 
20801ac4b82bSMike Smith     /* get a command slot */
20811ac4b82bSMike Smith     if (mlx_getslot(mc))
20821ac4b82bSMike Smith 	goto out;
2083da8bb3a3SMike Smith     debug(0, "got a slot");
20841ac4b82bSMike Smith 
2085da8bb3a3SMike Smith     if (mu->mu_datasize > 0) {
2086da8bb3a3SMike Smith 
2087da8bb3a3SMike Smith 	/* range check the pointer to physical buffer address */
20881b4404f9SScott Long 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) -
20891b4404f9SScott Long 						     sizeof(u_int32_t)))) {
2090da8bb3a3SMike Smith 	    error = EINVAL;
2091da8bb3a3SMike Smith 	    goto out;
2092da8bb3a3SMike Smith 	}
2093da8bb3a3SMike Smith     }
2094da8bb3a3SMike Smith 
20951b4404f9SScott Long     /* map the command so the controller can see it */
20961b4404f9SScott Long     mc->mc_data = kbuf;
20971b4404f9SScott Long     mc->mc_length = mu->mu_datasize;
20981b4404f9SScott Long     mc->mc_private = mu;
20991b4404f9SScott Long     error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
21001b4404f9SScott Long 			    mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT);
21010fca6f8bSJohn Baldwin     if (error)
21020fca6f8bSJohn Baldwin 	goto out;
21031ac4b82bSMike Smith 
21041ac4b82bSMike Smith     /* copy out status and data */
21051ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
21060fca6f8bSJohn Baldwin     if (mu->mu_datasize > 0) {
21070fca6f8bSJohn Baldwin 	MLX_IO_UNLOCK(sc);
21080fca6f8bSJohn Baldwin 	error = copyout(kbuf, mu->mu_buf, mu->mu_datasize);
21090fca6f8bSJohn Baldwin 	MLX_IO_LOCK(sc);
21100fca6f8bSJohn Baldwin     }
21111ac4b82bSMike Smith 
21121ac4b82bSMike Smith  out:
21131ac4b82bSMike Smith     mlx_releasecmd(mc);
21140fca6f8bSJohn Baldwin     MLX_IO_UNLOCK(sc);
21151ac4b82bSMike Smith     if (kbuf != NULL)
21161ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
21171ac4b82bSMike Smith     return(error);
21181ac4b82bSMike Smith }
21191ac4b82bSMike Smith 
21201ac4b82bSMike Smith /********************************************************************************
21211ac4b82bSMike Smith  ********************************************************************************
21221ac4b82bSMike Smith                                                         Command I/O to Controller
21231ac4b82bSMike Smith  ********************************************************************************
21241ac4b82bSMike Smith  ********************************************************************************/
21251ac4b82bSMike Smith 
21261ac4b82bSMike Smith /********************************************************************************
21271ac4b82bSMike Smith  * Find a free command slot for (mc).
21281ac4b82bSMike Smith  *
21291ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
21301ac4b82bSMike Smith  * 4 slots free for priority commands.
21311ac4b82bSMike Smith  */
21321ac4b82bSMike Smith static int
mlx_getslot(struct mlx_command * mc)21331ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
21341ac4b82bSMike Smith {
21351ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21360fca6f8bSJohn Baldwin     int			slot, limit;
21371ac4b82bSMike Smith 
2138da8bb3a3SMike Smith     debug_called(1);
21391ac4b82bSMike Smith 
21400fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
21410fca6f8bSJohn Baldwin 
2142baff09dbSMike Smith     /*
2143baff09dbSMike Smith      * Enforce slot-usage limit, if we have the required information.
2144baff09dbSMike Smith      */
2145baff09dbSMike Smith     if (sc->mlx_enq2 != NULL) {
2146baff09dbSMike Smith 	limit = sc->mlx_enq2->me_max_commands;
2147baff09dbSMike Smith     } else {
2148baff09dbSMike Smith 	limit = 2;
2149baff09dbSMike Smith     }
2150baff09dbSMike Smith     if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4))
21511ac4b82bSMike Smith 	return(EBUSY);
21521ac4b82bSMike Smith 
21531ac4b82bSMike Smith     /*
21541ac4b82bSMike Smith      * Allocate an outstanding command slot
21551ac4b82bSMike Smith      *
21561ac4b82bSMike Smith      * XXX linear search is slow
21571ac4b82bSMike Smith      */
2158baff09dbSMike Smith     for (slot = 0; slot < limit; slot++) {
2159da8bb3a3SMike Smith 	debug(2, "try slot %d", slot);
21601ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
21611ac4b82bSMike Smith 	    break;
21621ac4b82bSMike Smith     }
2163baff09dbSMike Smith     if (slot < limit) {
21641ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
21651ac4b82bSMike Smith 	sc->mlx_busycmds++;
21661ac4b82bSMike Smith     }
21671ac4b82bSMike Smith 
21681ac4b82bSMike Smith     /* out of slots? */
2169baff09dbSMike Smith     if (slot >= limit)
21701ac4b82bSMike Smith 	return(EBUSY);
21711ac4b82bSMike Smith 
2172da8bb3a3SMike Smith     debug(2, "got slot %d", slot);
21731ac4b82bSMike Smith     mc->mc_slot = slot;
21741ac4b82bSMike Smith     return(0);
21751ac4b82bSMike Smith }
21761ac4b82bSMike Smith 
21771ac4b82bSMike Smith /********************************************************************************
21781ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
21791ac4b82bSMike Smith  */
21801ac4b82bSMike Smith static void
mlx_setup_dmamap(struct mlx_command * mc,bus_dma_segment_t * segs,int nsegments,int error)21811b4404f9SScott Long mlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments,
21821b4404f9SScott Long 		 int error)
21831ac4b82bSMike Smith {
21841ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
21851ac4b82bSMike Smith     struct mlx_sgentry	*sg;
21861ac4b82bSMike Smith     int			i;
21871ac4b82bSMike Smith 
2188da8bb3a3SMike Smith     debug_called(1);
21891ac4b82bSMike Smith 
2190baff09dbSMike Smith     /* XXX should be unnecessary */
2191baff09dbSMike Smith     if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
21921b4404f9SScott Long 	panic("MLX: too many s/g segments (%d, max %d)", nsegments,
21931b4404f9SScott Long 	      sc->mlx_enq2->me_max_sg);
2194baff09dbSMike Smith 
21951ac4b82bSMike Smith     /* get base address of s/g table */
2196baff09dbSMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
21971ac4b82bSMike Smith 
21981ac4b82bSMike Smith     /* save s/g table information in command */
21991ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
22001b4404f9SScott Long     mc->mc_sgphys = sc->mlx_sgbusaddr +
22011b4404f9SScott Long 		   (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
22021ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
22031ac4b82bSMike Smith 
22041ac4b82bSMike Smith     /* populate s/g table */
22051ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
22061ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
22071ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
22081ac4b82bSMike Smith     }
22091ac4b82bSMike Smith 
22101b4404f9SScott Long     /* Make sure the buffers are visible on the bus. */
22111ac4b82bSMike Smith     if (mc->mc_flags & MLX_CMD_DATAIN)
22121b4404f9SScott Long 	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
22131b4404f9SScott Long 			BUS_DMASYNC_PREREAD);
22141ac4b82bSMike Smith     if (mc->mc_flags & MLX_CMD_DATAOUT)
22151b4404f9SScott Long 	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
22161b4404f9SScott Long 			BUS_DMASYNC_PREWRITE);
22171ac4b82bSMike Smith }
22181ac4b82bSMike Smith 
22191ac4b82bSMike Smith static void
mlx_unmapcmd(struct mlx_command * mc)22201ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
22211ac4b82bSMike Smith {
22221ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
22231ac4b82bSMike Smith 
2224da8bb3a3SMike Smith     debug_called(1);
22251ac4b82bSMike Smith 
22261ac4b82bSMike Smith     /* if the command involved data at all */
22271ac4b82bSMike Smith     if (mc->mc_data != NULL) {
22281ac4b82bSMike Smith 
22291ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
22301ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
22311ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
22321ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
22331ac4b82bSMike Smith 
22341ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
22351ac4b82bSMike Smith     }
22361ac4b82bSMike Smith }
22371ac4b82bSMike Smith 
22381ac4b82bSMike Smith /********************************************************************************
22395792b7feSMike Smith  * Try to deliver (mc) to the controller.
22401ac4b82bSMike Smith  *
22411ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
22421ac4b82bSMike Smith  */
22431ac4b82bSMike Smith static int
mlx_start(struct mlx_command * mc)22441ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
22451ac4b82bSMike Smith {
22461ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
22470fca6f8bSJohn Baldwin     int			i;
22481ac4b82bSMike Smith 
2249da8bb3a3SMike Smith     debug_called(1);
22501ac4b82bSMike Smith 
22511ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
22521ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
22531ac4b82bSMike Smith 
22544b006d7bSMike Smith     /* mark the command as currently being processed */
22551ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
22561ac4b82bSMike Smith 
22575792b7feSMike Smith     /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
22585792b7feSMike Smith     mc->mc_timeout = time_second + 60;
22591ac4b82bSMike Smith 
22601ac4b82bSMike Smith     /* spin waiting for the mailbox */
22610fca6f8bSJohn Baldwin     for (i = 100000; i > 0; i--) {
22624b006d7bSMike Smith 	if (sc->mlx_tryqueue(sc, mc)) {
22634b006d7bSMike Smith 	    /* move command to work queue */
22644b006d7bSMike Smith 	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
22651ac4b82bSMike Smith 	    return (0);
22660fca6f8bSJohn Baldwin 	} else if (i > 1)
22670fca6f8bSJohn Baldwin 	    mlx_done(sc, 0);
22680fca6f8bSJohn Baldwin     }
22691ac4b82bSMike Smith 
22701ac4b82bSMike Smith     /*
22711ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
22721ac4b82bSMike Smith      * that the command was given and return it with a bad status.
22731ac4b82bSMike Smith      */
22741ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
22751ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
22761ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
22775792b7feSMike Smith     mlx_complete(sc);
22781ac4b82bSMike Smith     return(EIO);
22791ac4b82bSMike Smith }
22801ac4b82bSMike Smith 
22811ac4b82bSMike Smith /********************************************************************************
22825792b7feSMike Smith  * Poll the controller (sc) for completed commands.
22835792b7feSMike Smith  * Update command status and free slots for reuse.  If any slots were freed,
22845792b7feSMike Smith  * new commands may be posted.
22851ac4b82bSMike Smith  *
22865792b7feSMike Smith  * Returns nonzero if one or more commands were completed.
22871ac4b82bSMike Smith  */
22881ac4b82bSMike Smith static int
mlx_done(struct mlx_softc * sc,int startio)22890fca6f8bSJohn Baldwin mlx_done(struct mlx_softc *sc, int startio)
22901ac4b82bSMike Smith {
22911ac4b82bSMike Smith     struct mlx_command	*mc;
22920fca6f8bSJohn Baldwin     int			result;
22931ac4b82bSMike Smith     u_int8_t		slot;
22941ac4b82bSMike Smith     u_int16_t		status;
22951ac4b82bSMike Smith 
2296da8bb3a3SMike Smith     debug_called(2);
22970fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
22981ac4b82bSMike Smith 
22995792b7feSMike Smith     result = 0;
23001ac4b82bSMike Smith 
23015792b7feSMike Smith     /* loop collecting completed commands */
23025792b7feSMike Smith     for (;;) {
23035792b7feSMike Smith 	/* poll for a completed command's identifier and status */
23041ac4b82bSMike Smith 	if (sc->mlx_findcomplete(sc, &slot, &status)) {
23055792b7feSMike Smith 	    result = 1;
23061ac4b82bSMike Smith 	    mc = sc->mlx_busycmd[slot];			/* find command */
23071ac4b82bSMike Smith 	    if (mc != NULL) {				/* paranoia */
23081ac4b82bSMike Smith 		if (mc->mc_status == MLX_STATUS_BUSY) {
23091ac4b82bSMike Smith 		    mc->mc_status = status;		/* save status */
23101ac4b82bSMike Smith 
23111ac4b82bSMike Smith 		    /* free slot for reuse */
23121ac4b82bSMike Smith 		    sc->mlx_busycmd[slot] = NULL;
23131ac4b82bSMike Smith 		    sc->mlx_busycmds--;
23141ac4b82bSMike Smith 		} else {
23151ac4b82bSMike Smith 		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
23161ac4b82bSMike Smith 		}
23171ac4b82bSMike Smith 	    } else {
23181ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
23191ac4b82bSMike Smith 	    }
23205792b7feSMike Smith 	} else {
23215792b7feSMike Smith 	    break;
23221ac4b82bSMike Smith 	}
23235792b7feSMike Smith     }
23241ac4b82bSMike Smith 
23255792b7feSMike Smith     /* if we've completed any commands, try posting some more */
23260fca6f8bSJohn Baldwin     if (result && startio)
23275792b7feSMike Smith 	mlx_startio(sc);
23285792b7feSMike Smith 
23295792b7feSMike Smith     /* handle completion and timeouts */
23305792b7feSMike Smith     mlx_complete(sc);
23315792b7feSMike Smith 
23325792b7feSMike Smith     return(result);
23331ac4b82bSMike Smith }
23341ac4b82bSMike Smith 
23351ac4b82bSMike Smith /********************************************************************************
23365792b7feSMike Smith  * Perform post-completion processing for commands on (sc).
23371ac4b82bSMike Smith  */
23381ac4b82bSMike Smith static void
mlx_complete(struct mlx_softc * sc)23391ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
23401ac4b82bSMike Smith {
23411ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
23421ac4b82bSMike Smith 
2343da8bb3a3SMike Smith     debug_called(2);
23440fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
23451ac4b82bSMike Smith 
23465792b7feSMike Smith     /* scan the list of busy/done commands */
23474b006d7bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_work);
23481ac4b82bSMike Smith     while (mc != NULL) {
23491ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
23501ac4b82bSMike Smith 
23515792b7feSMike Smith 	/* Command has been completed in some fashion */
23524b006d7bSMike Smith 	if (mc->mc_status != MLX_STATUS_BUSY) {
23534b006d7bSMike Smith 
23545792b7feSMike Smith 	    /* unmap the command's data buffer */
23555792b7feSMike Smith 	    mlx_unmapcmd(mc);
23561ac4b82bSMike Smith 	    /*
23571ac4b82bSMike Smith 	     * Does the command have a completion handler?
23581ac4b82bSMike Smith 	     */
23591ac4b82bSMike Smith 	    if (mc->mc_complete != NULL) {
23601ac4b82bSMike Smith 		/* remove from list and give to handler */
23614b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
23621ac4b82bSMike Smith 		mc->mc_complete(mc);
23631ac4b82bSMike Smith 
23641ac4b82bSMike Smith 		/*
23651ac4b82bSMike Smith 		 * Is there a sleeper waiting on this command?
23661ac4b82bSMike Smith 		 */
23671ac4b82bSMike Smith 	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
23681ac4b82bSMike Smith 
23691ac4b82bSMike Smith 		/* remove from list and wake up sleeper */
23704b006d7bSMike Smith 		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
23711ac4b82bSMike Smith 		wakeup_one(mc->mc_private);
23721ac4b82bSMike Smith 
23731ac4b82bSMike Smith 		/*
23741ac4b82bSMike Smith 		 * Leave the command for a caller that's polling for it.
23751ac4b82bSMike Smith 		 */
23761ac4b82bSMike Smith 	    } else {
23771ac4b82bSMike Smith 	    }
23784b006d7bSMike Smith 	}
23791ac4b82bSMike Smith 	mc = nc;
23801ac4b82bSMike Smith     }
23811ac4b82bSMike Smith }
23821ac4b82bSMike Smith 
23831ac4b82bSMike Smith /********************************************************************************
23841ac4b82bSMike Smith  ********************************************************************************
23851ac4b82bSMike Smith                                                         Command Buffer Management
23861ac4b82bSMike Smith  ********************************************************************************
23871ac4b82bSMike Smith  ********************************************************************************/
23881ac4b82bSMike Smith 
23891ac4b82bSMike Smith /********************************************************************************
23901ac4b82bSMike Smith  * Get a new command buffer.
23911ac4b82bSMike Smith  *
23921ac4b82bSMike Smith  * This may return NULL in low-memory cases.
23931ac4b82bSMike Smith  *
23941ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
23951ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
23961ac4b82bSMike Smith  *
23971ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
23981ac4b82bSMike Smith  *
23991ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
24001ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
24011ac4b82bSMike Smith  *     using a buffer.
24021ac4b82bSMike Smith  */
24031ac4b82bSMike Smith static struct mlx_command *
mlx_alloccmd(struct mlx_softc * sc)24041ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
24051ac4b82bSMike Smith {
24061ac4b82bSMike Smith     struct mlx_command	*mc;
24071ac4b82bSMike Smith     int			error;
24081ac4b82bSMike Smith 
2409da8bb3a3SMike Smith     debug_called(1);
24101ac4b82bSMike Smith 
24110fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
24121ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
24131ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
24141ac4b82bSMike Smith 
24151ac4b82bSMike Smith     /* allocate a new command buffer? */
24161ac4b82bSMike Smith     if (mc == NULL) {
2417ca89ee27SDavid Malone 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO);
24181ac4b82bSMike Smith 	if (mc != NULL) {
24191ac4b82bSMike Smith 	    mc->mc_sc = sc;
24201ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
24211ac4b82bSMike Smith 	    if (error) {
24221ac4b82bSMike Smith 		free(mc, M_DEVBUF);
24231ac4b82bSMike Smith 		return(NULL);
24241ac4b82bSMike Smith 	    }
24251ac4b82bSMike Smith 	}
24261ac4b82bSMike Smith     }
24271ac4b82bSMike Smith     return(mc);
24281ac4b82bSMike Smith }
24291ac4b82bSMike Smith 
24301ac4b82bSMike Smith /********************************************************************************
24311ac4b82bSMike Smith  * Release a command buffer for recycling.
24321ac4b82bSMike Smith  *
24331ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
24341ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
24351ac4b82bSMike Smith  */
24361ac4b82bSMike Smith static void
mlx_releasecmd(struct mlx_command * mc)24371ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
24381ac4b82bSMike Smith {
24391ac4b82bSMike Smith 
2440da8bb3a3SMike Smith     debug_called(1);
24411ac4b82bSMike Smith 
24420fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(mc->mc_sc);
24431ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
24441ac4b82bSMike Smith }
24451ac4b82bSMike Smith 
24461ac4b82bSMike Smith /********************************************************************************
24471ac4b82bSMike Smith  * Permanently discard a command buffer.
24481ac4b82bSMike Smith  */
24491ac4b82bSMike Smith static void
mlx_freecmd(struct mlx_command * mc)24501ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
24511ac4b82bSMike Smith {
24521ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
24531ac4b82bSMike Smith 
2454da8bb3a3SMike Smith     debug_called(1);
24551ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
24561ac4b82bSMike Smith     free(mc, M_DEVBUF);
24571ac4b82bSMike Smith }
24581ac4b82bSMike Smith 
24591ac4b82bSMike Smith 
24601ac4b82bSMike Smith /********************************************************************************
24611ac4b82bSMike Smith  ********************************************************************************
24621ac4b82bSMike Smith                                                 Type 3 interface accessor methods
24631ac4b82bSMike Smith  ********************************************************************************
24641ac4b82bSMike Smith  ********************************************************************************/
24651ac4b82bSMike Smith 
24661ac4b82bSMike Smith /********************************************************************************
24671ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
24681ac4b82bSMike Smith  * (the controller is not ready to take a command).
24691ac4b82bSMike Smith  */
24701ac4b82bSMike Smith static int
mlx_v3_tryqueue(struct mlx_softc * sc,struct mlx_command * mc)24711ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
24721ac4b82bSMike Smith {
24731ac4b82bSMike Smith     int		i;
24741ac4b82bSMike Smith 
2475da8bb3a3SMike Smith     debug_called(2);
24760fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
24771ac4b82bSMike Smith 
24781ac4b82bSMike Smith     /* ready for our command? */
24791ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
24801ac4b82bSMike Smith 	/* copy mailbox data to window */
24811ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
24821ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
24831ac4b82bSMike Smith 
24841ac4b82bSMike Smith 	/* post command */
2485f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
24861ac4b82bSMike Smith 	return(1);
24871ac4b82bSMike Smith     }
24881ac4b82bSMike Smith     return(0);
24891ac4b82bSMike Smith }
24901ac4b82bSMike Smith 
24911ac4b82bSMike Smith /********************************************************************************
24921ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
24931ac4b82bSMike Smith  * and recover the slot number and status code.
24941ac4b82bSMike Smith  */
24951ac4b82bSMike Smith static int
mlx_v3_findcomplete(struct mlx_softc * sc,u_int8_t * slot,u_int16_t * status)24961ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
24971ac4b82bSMike Smith {
24981ac4b82bSMike Smith 
2499da8bb3a3SMike Smith     debug_called(2);
25000fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
25011ac4b82bSMike Smith 
25021ac4b82bSMike Smith     /* status available? */
25031ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
25041ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
25051ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
25061ac4b82bSMike Smith 
25071ac4b82bSMike Smith 	/* acknowledge completion */
2508f6b84b08SMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
2509f6b84b08SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
25101ac4b82bSMike Smith 	return(1);
25111ac4b82bSMike Smith     }
25121ac4b82bSMike Smith     return(0);
25131ac4b82bSMike Smith }
25141ac4b82bSMike Smith 
25151ac4b82bSMike Smith /********************************************************************************
25161ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
25171ac4b82bSMike Smith  */
25181ac4b82bSMike Smith static void
mlx_v3_intaction(struct mlx_softc * sc,int action)25191ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
25201ac4b82bSMike Smith {
2521da8bb3a3SMike Smith     debug_called(1);
25220fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
25231ac4b82bSMike Smith 
25241ac4b82bSMike Smith     switch(action) {
25251ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
25261ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
25271ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
25281ac4b82bSMike Smith 	break;
25291ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
25301ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
25311ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
25321ac4b82bSMike Smith 	break;
25331ac4b82bSMike Smith     }
25341ac4b82bSMike Smith }
25351ac4b82bSMike Smith 
2536da8bb3a3SMike Smith /********************************************************************************
2537da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2538da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2539da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2540da8bb3a3SMike Smith  */
2541da8bb3a3SMike Smith static int
mlx_v3_fw_handshake(struct mlx_softc * sc,int * error,int * param1,int * param2,int first)25420fca6f8bSJohn Baldwin mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
25430fca6f8bSJohn Baldwin     int first)
2544da8bb3a3SMike Smith {
2545da8bb3a3SMike Smith     u_int8_t	fwerror;
2546da8bb3a3SMike Smith 
2547da8bb3a3SMike Smith     debug_called(2);
2548da8bb3a3SMike Smith 
2549da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
25500fca6f8bSJohn Baldwin     if (first) {
2551da8bb3a3SMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
2552da8bb3a3SMike Smith 	DELAY(1000);
2553da8bb3a3SMike Smith     }
2554da8bb3a3SMike Smith 
2555da8bb3a3SMike Smith     /* init in progress? */
2556da8bb3a3SMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
2557da8bb3a3SMike Smith 	return(0);
2558da8bb3a3SMike Smith 
2559da8bb3a3SMike Smith     /* test error value */
2560da8bb3a3SMike Smith     fwerror = MLX_V3_GET_FWERROR(sc);
2561da8bb3a3SMike Smith     if (!(fwerror & MLX_V3_FWERROR_PEND))
2562da8bb3a3SMike Smith 	return(1);
2563da8bb3a3SMike Smith 
2564da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2565da8bb3a3SMike Smith     *error = fwerror & ~MLX_V3_FWERROR_PEND;
2566da8bb3a3SMike Smith     *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
2567da8bb3a3SMike Smith     *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
2568da8bb3a3SMike Smith 
2569da8bb3a3SMike Smith     /* acknowledge */
2570da8bb3a3SMike Smith     MLX_V3_PUT_FWERROR(sc, 0);
2571da8bb3a3SMike Smith 
2572da8bb3a3SMike Smith     return(2);
2573da8bb3a3SMike Smith }
25741ac4b82bSMike Smith 
25751ac4b82bSMike Smith /********************************************************************************
25761ac4b82bSMike Smith  ********************************************************************************
2577f6b84b08SMike Smith                                                 Type 4 interface accessor methods
2578f6b84b08SMike Smith  ********************************************************************************
2579f6b84b08SMike Smith  ********************************************************************************/
2580f6b84b08SMike Smith 
2581f6b84b08SMike Smith /********************************************************************************
2582f6b84b08SMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
2583f6b84b08SMike Smith  * (the controller is not ready to take a command).
2584f6b84b08SMike Smith  */
2585f6b84b08SMike Smith static int
mlx_v4_tryqueue(struct mlx_softc * sc,struct mlx_command * mc)2586f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
2587f6b84b08SMike Smith {
2588f6b84b08SMike Smith     int		i;
2589f6b84b08SMike Smith 
2590da8bb3a3SMike Smith     debug_called(2);
25910fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
2592f6b84b08SMike Smith 
2593f6b84b08SMike Smith     /* ready for our command? */
2594f6b84b08SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
2595f6b84b08SMike Smith 	/* copy mailbox data to window */
2596f6b84b08SMike Smith 	for (i = 0; i < 13; i++)
2597f6b84b08SMike Smith 	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
2598f6b84b08SMike Smith 
2599da8bb3a3SMike Smith 	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
26000fca6f8bSJohn Baldwin 	bus_barrier(sc->mlx_mem, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
2601da8bb3a3SMike Smith 			  BUS_SPACE_BARRIER_WRITE);
2602da8bb3a3SMike Smith 
2603f6b84b08SMike Smith 	/* post command */
2604f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
2605f6b84b08SMike Smith 	return(1);
2606f6b84b08SMike Smith     }
2607f6b84b08SMike Smith     return(0);
2608f6b84b08SMike Smith }
2609f6b84b08SMike Smith 
2610f6b84b08SMike Smith /********************************************************************************
2611f6b84b08SMike Smith  * See if a command has been completed, if so acknowledge its completion
2612f6b84b08SMike Smith  * and recover the slot number and status code.
2613f6b84b08SMike Smith  */
2614f6b84b08SMike Smith static int
mlx_v4_findcomplete(struct mlx_softc * sc,u_int8_t * slot,u_int16_t * status)2615f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
2616f6b84b08SMike Smith {
2617f6b84b08SMike Smith 
2618da8bb3a3SMike Smith     debug_called(2);
26190fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
2620f6b84b08SMike Smith 
2621f6b84b08SMike Smith     /* status available? */
2622f6b84b08SMike Smith     if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
2623f6b84b08SMike Smith 	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
2624f6b84b08SMike Smith 	*status = MLX_V4_GET_STATUS(sc);		/* get status */
2625f6b84b08SMike Smith 
2626f6b84b08SMike Smith 	/* acknowledge completion */
2627f6b84b08SMike Smith 	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
2628f6b84b08SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2629f6b84b08SMike Smith 	return(1);
2630f6b84b08SMike Smith     }
2631f6b84b08SMike Smith     return(0);
2632f6b84b08SMike Smith }
2633f6b84b08SMike Smith 
2634f6b84b08SMike Smith /********************************************************************************
2635f6b84b08SMike Smith  * Enable/disable interrupts as requested.
2636f6b84b08SMike Smith  */
2637f6b84b08SMike Smith static void
mlx_v4_intaction(struct mlx_softc * sc,int action)2638f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action)
2639f6b84b08SMike Smith {
2640da8bb3a3SMike Smith     debug_called(1);
26410fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
2642f6b84b08SMike Smith 
2643f6b84b08SMike Smith     switch(action) {
2644f6b84b08SMike Smith     case MLX_INTACTION_DISABLE:
2645f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
2646f6b84b08SMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
2647f6b84b08SMike Smith 	break;
2648f6b84b08SMike Smith     case MLX_INTACTION_ENABLE:
2649f6b84b08SMike Smith 	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
2650f6b84b08SMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
2651f6b84b08SMike Smith 	break;
2652f6b84b08SMike Smith     }
2653f6b84b08SMike Smith }
2654f6b84b08SMike Smith 
2655da8bb3a3SMike Smith /********************************************************************************
2656da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2657da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2658da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2659da8bb3a3SMike Smith  */
2660da8bb3a3SMike Smith static int
mlx_v4_fw_handshake(struct mlx_softc * sc,int * error,int * param1,int * param2,int first)26610fca6f8bSJohn Baldwin mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
26620fca6f8bSJohn Baldwin     int first)
2663da8bb3a3SMike Smith {
2664da8bb3a3SMike Smith     u_int8_t	fwerror;
2665da8bb3a3SMike Smith 
2666da8bb3a3SMike Smith     debug_called(2);
2667da8bb3a3SMike Smith 
2668da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
26690fca6f8bSJohn Baldwin     if (first) {
2670da8bb3a3SMike Smith 	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
2671da8bb3a3SMike Smith 	DELAY(1000);
2672da8bb3a3SMike Smith     }
2673da8bb3a3SMike Smith 
2674da8bb3a3SMike Smith     /* init in progress? */
2675da8bb3a3SMike Smith     if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
2676da8bb3a3SMike Smith 	return(0);
2677da8bb3a3SMike Smith 
2678da8bb3a3SMike Smith     /* test error value */
2679da8bb3a3SMike Smith     fwerror = MLX_V4_GET_FWERROR(sc);
2680da8bb3a3SMike Smith     if (!(fwerror & MLX_V4_FWERROR_PEND))
2681da8bb3a3SMike Smith 	return(1);
2682da8bb3a3SMike Smith 
2683da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2684da8bb3a3SMike Smith     *error = fwerror & ~MLX_V4_FWERROR_PEND;
2685da8bb3a3SMike Smith     *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
2686da8bb3a3SMike Smith     *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
2687da8bb3a3SMike Smith 
2688da8bb3a3SMike Smith     /* acknowledge */
2689da8bb3a3SMike Smith     MLX_V4_PUT_FWERROR(sc, 0);
2690da8bb3a3SMike Smith 
2691da8bb3a3SMike Smith     return(2);
2692da8bb3a3SMike Smith }
2693f6b84b08SMike Smith 
2694f6b84b08SMike Smith /********************************************************************************
2695f6b84b08SMike Smith  ********************************************************************************
26965792b7feSMike Smith                                                 Type 5 interface accessor methods
26975792b7feSMike Smith  ********************************************************************************
26985792b7feSMike Smith  ********************************************************************************/
26995792b7feSMike Smith 
27005792b7feSMike Smith /********************************************************************************
27015792b7feSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
27025792b7feSMike Smith  * (the controller is not ready to take a command).
27035792b7feSMike Smith  */
27045792b7feSMike Smith static int
mlx_v5_tryqueue(struct mlx_softc * sc,struct mlx_command * mc)27055792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
27065792b7feSMike Smith {
27075792b7feSMike Smith     int		i;
27085792b7feSMike Smith 
2709da8bb3a3SMike Smith     debug_called(2);
27100fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
27115792b7feSMike Smith 
27125792b7feSMike Smith     /* ready for our command? */
27135792b7feSMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
27145792b7feSMike Smith 	/* copy mailbox data to window */
27155792b7feSMike Smith 	for (i = 0; i < 13; i++)
27165792b7feSMike Smith 	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
27175792b7feSMike Smith 
27185792b7feSMike Smith 	/* post command */
27195792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
27205792b7feSMike Smith 	return(1);
27215792b7feSMike Smith     }
27225792b7feSMike Smith     return(0);
27235792b7feSMike Smith }
27245792b7feSMike Smith 
27255792b7feSMike Smith /********************************************************************************
27265792b7feSMike Smith  * See if a command has been completed, if so acknowledge its completion
27275792b7feSMike Smith  * and recover the slot number and status code.
27285792b7feSMike Smith  */
27295792b7feSMike Smith static int
mlx_v5_findcomplete(struct mlx_softc * sc,u_int8_t * slot,u_int16_t * status)27305792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
27315792b7feSMike Smith {
27325792b7feSMike Smith 
2733da8bb3a3SMike Smith     debug_called(2);
27340fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
27355792b7feSMike Smith 
27365792b7feSMike Smith     /* status available? */
27375792b7feSMike Smith     if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
27385792b7feSMike Smith 	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
27395792b7feSMike Smith 	*status = MLX_V5_GET_STATUS(sc);		/* get status */
27405792b7feSMike Smith 
27415792b7feSMike Smith 	/* acknowledge completion */
27425792b7feSMike Smith 	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
27435792b7feSMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
27445792b7feSMike Smith 	return(1);
27455792b7feSMike Smith     }
27465792b7feSMike Smith     return(0);
27475792b7feSMike Smith }
27485792b7feSMike Smith 
27495792b7feSMike Smith /********************************************************************************
27505792b7feSMike Smith  * Enable/disable interrupts as requested.
27515792b7feSMike Smith  */
27525792b7feSMike Smith static void
mlx_v5_intaction(struct mlx_softc * sc,int action)27535792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action)
27545792b7feSMike Smith {
2755da8bb3a3SMike Smith     debug_called(1);
27560fca6f8bSJohn Baldwin     MLX_IO_ASSERT_LOCKED(sc);
27575792b7feSMike Smith 
27585792b7feSMike Smith     switch(action) {
27595792b7feSMike Smith     case MLX_INTACTION_DISABLE:
2760da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
27615792b7feSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
27625792b7feSMike Smith 	break;
27635792b7feSMike Smith     case MLX_INTACTION_ENABLE:
2764da8bb3a3SMike Smith 	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
27655792b7feSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
27665792b7feSMike Smith 	break;
27675792b7feSMike Smith     }
27685792b7feSMike Smith }
27695792b7feSMike Smith 
2770da8bb3a3SMike Smith /********************************************************************************
2771da8bb3a3SMike Smith  * Poll for firmware error codes during controller initialisation.
2772da8bb3a3SMike Smith  * Returns 0 if initialisation is complete, 1 if still in progress but no
2773da8bb3a3SMike Smith  * error has been fetched, 2 if an error has been retrieved.
2774da8bb3a3SMike Smith  */
2775da8bb3a3SMike Smith static int
mlx_v5_fw_handshake(struct mlx_softc * sc,int * error,int * param1,int * param2,int first)27760fca6f8bSJohn Baldwin mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
27770fca6f8bSJohn Baldwin     int first)
2778da8bb3a3SMike Smith {
2779da8bb3a3SMike Smith     u_int8_t	fwerror;
2780da8bb3a3SMike Smith 
2781da8bb3a3SMike Smith     debug_called(2);
2782da8bb3a3SMike Smith 
2783da8bb3a3SMike Smith     /* first time around, clear any hardware completion status */
27840fca6f8bSJohn Baldwin     if (first) {
2785da8bb3a3SMike Smith 	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
2786da8bb3a3SMike Smith 	DELAY(1000);
2787da8bb3a3SMike Smith     }
2788da8bb3a3SMike Smith 
2789da8bb3a3SMike Smith     /* init in progress? */
2790da8bb3a3SMike Smith     if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
2791da8bb3a3SMike Smith 	return(0);
2792da8bb3a3SMike Smith 
2793da8bb3a3SMike Smith     /* test for error value */
2794da8bb3a3SMike Smith     fwerror = MLX_V5_GET_FWERROR(sc);
2795da8bb3a3SMike Smith     if (!(fwerror & MLX_V5_FWERROR_PEND))
2796da8bb3a3SMike Smith 	return(1);
2797da8bb3a3SMike Smith 
2798da8bb3a3SMike Smith     /* mask status pending bit, fetch status */
2799da8bb3a3SMike Smith     *error = fwerror & ~MLX_V5_FWERROR_PEND;
2800da8bb3a3SMike Smith     *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
2801da8bb3a3SMike Smith     *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
2802da8bb3a3SMike Smith 
2803da8bb3a3SMike Smith     /* acknowledge */
2804da8bb3a3SMike Smith     MLX_V5_PUT_FWERROR(sc, 0xff);
2805da8bb3a3SMike Smith 
2806da8bb3a3SMike Smith     return(2);
2807da8bb3a3SMike Smith }
28085792b7feSMike Smith 
28095792b7feSMike Smith /********************************************************************************
28105792b7feSMike Smith  ********************************************************************************
28111ac4b82bSMike Smith                                                                         Debugging
28121ac4b82bSMike Smith  ********************************************************************************
28131ac4b82bSMike Smith  ********************************************************************************/
28141ac4b82bSMike Smith 
28151ac4b82bSMike Smith /********************************************************************************
28161ac4b82bSMike Smith  * Return a status message describing (mc)
28171ac4b82bSMike Smith  */
28181ac4b82bSMike Smith static char *mlx_status_messages[] = {
28191ac4b82bSMike Smith     "normal completion",			/* 00 */
28201ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
28211ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
28221ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
28231ac4b82bSMike Smith     "bad data encountered",			/* 04 */
28241ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
28251ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
28261ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
28271ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
28281ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
28291ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
28301ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
28311ac4b82bSMike Smith     "channel is busy",				/* 12 */
28321ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
2833da8bb3a3SMike Smith     "rebuild successfully terminated",		/* 14 */
2834da8bb3a3SMike Smith     "unsupported command",			/* 15 */
2835da8bb3a3SMike Smith     "check condition received",			/* 16 */
2836da8bb3a3SMike Smith     "device is busy",				/* 17 */
2837da8bb3a3SMike Smith     "selection or command timeout",		/* 18 */
2838da8bb3a3SMike Smith     "command terminated abnormally",		/* 19 */
2839da8bb3a3SMike Smith     ""
28401ac4b82bSMike Smith };
28411ac4b82bSMike Smith 
28421ac4b82bSMike Smith static struct
28431ac4b82bSMike Smith {
28441ac4b82bSMike Smith     int		command;
28451ac4b82bSMike Smith     u_int16_t	status;
28461ac4b82bSMike Smith     int		msg;
28471ac4b82bSMike Smith } mlx_messages[] = {
2848da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0001,	 1},
2849da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0002,	 1},
2850da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x0105,	 3},
2851da8bb3a3SMike Smith     {MLX_CMD_READSG,		0x010c,	 4},
2852da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0001,	 1},
2853da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0002,	 1},
2854da8bb3a3SMike Smith     {MLX_CMD_WRITESG,		0x0105,	 3},
2855da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0001,	 1},
2856da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0002,	 1},
2857da8bb3a3SMike Smith     {MLX_CMD_READSG_OLD,	0x0105,	 3},
2858da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
2859da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
2860da8bb3a3SMike Smith     {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
28611ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
28621ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
28631ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
28641ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
28651ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
2866da8bb3a3SMike Smith     {MLX_CMD_REBUILDASYNC,	0x0107, 14},
28671ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
28681ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
28691ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
28701ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
28711ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
28721ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
28731ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
2874da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0002, 16},
2875da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0008, 17},
2876da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000e, 18},
2877da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x000f, 19},
2878da8bb3a3SMike Smith     {MLX_CMD_DIRECT_CDB,	0x0105,  8},
2879da8bb3a3SMike Smith 
2880da8bb3a3SMike Smith     {0,				0x0104, 14},
28811ac4b82bSMike Smith     {-1, 0, 0}
28821ac4b82bSMike Smith };
28831ac4b82bSMike Smith 
28841ac4b82bSMike Smith static char *
mlx_diagnose_command(struct mlx_command * mc)28851ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
28861ac4b82bSMike Smith {
28871ac4b82bSMike Smith     static char	unkmsg[80];
28881ac4b82bSMike Smith     int		i;
28891ac4b82bSMike Smith 
28901ac4b82bSMike Smith     /* look up message in table */
28911ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
2892da8bb3a3SMike Smith 	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
2893466454bdSMike Smith 	    (mc->mc_status == mlx_messages[i].status))
28941ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
28951ac4b82bSMike Smith 
28961ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
28971ac4b82bSMike Smith     return(unkmsg);
28981ac4b82bSMike Smith }
28991ac4b82bSMike Smith 
29001ac4b82bSMike Smith /*******************************************************************************
2901da8bb3a3SMike Smith  * Print a string describing the controller (sc)
29021ac4b82bSMike Smith  */
29035792b7feSMike Smith static struct
29045792b7feSMike Smith {
29055792b7feSMike Smith     int		hwid;
29065792b7feSMike Smith     char	*name;
29075792b7feSMike Smith } mlx_controller_names[] = {
29085792b7feSMike Smith     {0x01,	"960P/PD"},
29095792b7feSMike Smith     {0x02,	"960PL"},
29105792b7feSMike Smith     {0x10,	"960PG"},
29115792b7feSMike Smith     {0x11,	"960PJ"},
29129eee27f1SMike Smith     {0x12,	"960PR"},
29139eee27f1SMike Smith     {0x13,	"960PT"},
29149eee27f1SMike Smith     {0x14,	"960PTL0"},
29159eee27f1SMike Smith     {0x15,	"960PRL"},
29169eee27f1SMike Smith     {0x16,	"960PTL1"},
29179eee27f1SMike Smith     {0x20,	"1164PVX"},
29185792b7feSMike Smith     {-1, NULL}
29195792b7feSMike Smith };
29205792b7feSMike Smith 
29219eee27f1SMike Smith static void
mlx_describe_controller(struct mlx_softc * sc)29229eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc)
29231ac4b82bSMike Smith {
29241ac4b82bSMike Smith     static char		buf[80];
29255792b7feSMike Smith     char		*model;
29269eee27f1SMike Smith     int			i;
29271ac4b82bSMike Smith 
29285792b7feSMike Smith     for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
29299eee27f1SMike Smith 	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
29305792b7feSMike Smith 	    model = mlx_controller_names[i].name;
29311ac4b82bSMike Smith 	    break;
29321ac4b82bSMike Smith 	}
29335792b7feSMike Smith     }
29345792b7feSMike Smith     if (model == NULL) {
29359eee27f1SMike Smith 	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
29365792b7feSMike Smith 	model = buf;
29375792b7feSMike Smith     }
2938da8bb3a3SMike Smith     device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
29399eee27f1SMike Smith 		  model,
29409eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels,
29419eee27f1SMike Smith 		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
29429eee27f1SMike Smith 		  sc->mlx_enq2->me_firmware_id & 0xff,
29439eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
29449eee27f1SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
2945b9256fe3SMike Smith 		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
29469eee27f1SMike Smith 		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
29479eee27f1SMike Smith 
29489eee27f1SMike Smith     if (bootverbose) {
29499eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
29509eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
29519eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
29529eee27f1SMike Smith 		      sc->mlx_enq2->me_actual_channels);
29539eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
29549eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
29559eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
29569eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
29579eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
29589eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
29599eee27f1SMike Smith 		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
29609eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
29619eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
29629eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
29639eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
29649eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
29659eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
29669eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
29679eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
29689eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
29699eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
29709eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
29719eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
29729eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
29739eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
29749eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
29759eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
29769eee27f1SMike Smith 		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
29779eee27f1SMike Smith 		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
29789eee27f1SMike Smith 		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
29799eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
29809eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
29819eee27f1SMike Smith 	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
29829eee27f1SMike Smith 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
29839eee27f1SMike Smith 
29849eee27f1SMike Smith     }
29851ac4b82bSMike Smith }
29861ac4b82bSMike Smith 
2987da8bb3a3SMike Smith /*******************************************************************************
2988da8bb3a3SMike Smith  * Emit a string describing the firmware handshake status code, and return a flag
2989da8bb3a3SMike Smith  * indicating whether the code represents a fatal error.
2990da8bb3a3SMike Smith  *
2991da8bb3a3SMike Smith  * Error code interpretations are from the Linux driver, and don't directly match
2992da8bb3a3SMike Smith  * the messages printed by Mylex's BIOS.  This may change if documentation on the
2993da8bb3a3SMike Smith  * codes is forthcoming.
2994da8bb3a3SMike Smith  */
2995da8bb3a3SMike Smith static int
mlx_fw_message(struct mlx_softc * sc,int error,int param1,int param2)2996da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
2997da8bb3a3SMike Smith {
2998da8bb3a3SMike Smith     switch(error) {
2999da8bb3a3SMike Smith     case 0x00:
3000da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
3001da8bb3a3SMike Smith 	break;
3002da8bb3a3SMike Smith     case 0x08:
3003da8bb3a3SMike Smith 	/* we could be neater about this and give some indication when we receive more of them */
3004da8bb3a3SMike Smith 	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
3005da8bb3a3SMike Smith 	    device_printf(sc->mlx_dev, "spinning up drives...\n");
3006da8bb3a3SMike Smith 	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
3007da8bb3a3SMike Smith 	}
3008da8bb3a3SMike Smith 	break;
3009da8bb3a3SMike Smith     case 0x30:
3010da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "configuration checksum error\n");
3011da8bb3a3SMike Smith 	break;
3012da8bb3a3SMike Smith     case 0x60:
3013da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
3014da8bb3a3SMike Smith 	break;
3015da8bb3a3SMike Smith     case 0x70:
3016da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
3017da8bb3a3SMike Smith 	break;
3018da8bb3a3SMike Smith     case 0x90:
3019da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
3020da8bb3a3SMike Smith 	break;
3021da8bb3a3SMike Smith     case 0xa0:
3022da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
3023da8bb3a3SMike Smith 	break;
3024da8bb3a3SMike Smith     case 0xb0:
3025da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
3026da8bb3a3SMike Smith 	break;
3027da8bb3a3SMike Smith     case 0xd0:
3028da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "new controller configuration found\n");
3029da8bb3a3SMike Smith 	break;
3030da8bb3a3SMike Smith     case 0xf0:
3031da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
3032da8bb3a3SMike Smith 	return(1);
3033da8bb3a3SMike Smith     default:
3034da8bb3a3SMike Smith 	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
3035da8bb3a3SMike Smith 	break;
3036da8bb3a3SMike Smith     }
3037da8bb3a3SMike Smith     return(0);
3038da8bb3a3SMike Smith }
3039da8bb3a3SMike Smith 
30401ac4b82bSMike Smith /********************************************************************************
30411ac4b82bSMike Smith  ********************************************************************************
30421ac4b82bSMike Smith                                                                 Utility Functions
30431ac4b82bSMike Smith  ********************************************************************************
30441ac4b82bSMike Smith  ********************************************************************************/
30451ac4b82bSMike Smith 
30461ac4b82bSMike Smith /********************************************************************************
30471ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
30481ac4b82bSMike Smith  */
30491ac4b82bSMike Smith static struct mlx_sysdrive *
mlx_findunit(struct mlx_softc * sc,int unit)30501ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
30511ac4b82bSMike Smith {
30521ac4b82bSMike Smith     int		i;
30531ac4b82bSMike Smith 
30541ac4b82bSMike Smith     /* search system drives */
30550fca6f8bSJohn Baldwin     MLX_CONFIG_ASSERT_LOCKED(sc);
30561ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
30571ac4b82bSMike Smith 	/* is this one attached? */
30581ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
30591ac4b82bSMike Smith 	    /* is this the one? */
30601ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
30611ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
30621ac4b82bSMike Smith 	}
30631ac4b82bSMike Smith     }
30641ac4b82bSMike Smith     return(NULL);
30651ac4b82bSMike Smith }
3066