151973Smsmith/*-
251973Smsmith * Copyright (c) 1999 Michael Smith
351973Smsmith * All rights reserved.
451973Smsmith *
551973Smsmith * Redistribution and use in source and binary forms, with or without
651973Smsmith * modification, are permitted provided that the following conditions
751973Smsmith * are met:
851973Smsmith * 1. Redistributions of source code must retain the above copyright
951973Smsmith *    notice, this list of conditions and the following disclaimer.
1051973Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1151973Smsmith *    notice, this list of conditions and the following disclaimer in the
1251973Smsmith *    documentation and/or other materials provided with the distribution.
1351973Smsmith *
1451973Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1551973Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1651973Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1751973Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1851973Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1951973Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2051973Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2151973Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2251973Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2351973Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2451973Smsmith * SUCH DAMAGE.
2551973Smsmith *
2651973Smsmith *	$FreeBSD$
2751973Smsmith */
2851973Smsmith
2951973Smsmith/*
3051973Smsmith * Driver for the Mylex DAC960 family of RAID controllers.
3151973Smsmith */
3251973Smsmith
3351973Smsmith#include <sys/param.h>
3451973Smsmith#include <sys/systm.h>
3551973Smsmith#include <sys/malloc.h>
3651973Smsmith#include <sys/kernel.h>
3751973Smsmith
3851973Smsmith#include <sys/bus.h>
3951973Smsmith#include <sys/conf.h>
4058188Smsmith#include <sys/stat.h>
4151973Smsmith
4251973Smsmith#include <machine/resource.h>
4351973Smsmith#include <machine/bus.h>
4451973Smsmith#include <machine/clock.h>
4551973Smsmith#include <sys/rman.h>
4651973Smsmith
47112946Sphk#include <geom/geom_disk.h>
48112946Sphk
4978752Smsmith#include <dev/mlx/mlx_compat.h>
5051973Smsmith#include <dev/mlx/mlxio.h>
5151973Smsmith#include <dev/mlx/mlxvar.h>
5251973Smsmith#include <dev/mlx/mlxreg.h>
5351973Smsmith
5451973Smsmithstatic struct cdevsw mlx_cdevsw = {
55126080Sphk	.d_version =	D_VERSION,
56126080Sphk	.d_flags =	D_NEEDGIANT,
57111815Sphk	.d_open =	mlx_open,
58111815Sphk	.d_close =	mlx_close,
59111815Sphk	.d_ioctl =	mlx_ioctl,
60111815Sphk	.d_name =	"mlx",
6151973Smsmith};
6251973Smsmith
6351973Smsmithdevclass_t	mlx_devclass;
6451973Smsmith
6551973Smsmith/*
6651973Smsmith * Per-interface accessor methods
6751973Smsmith */
6851973Smsmithstatic int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
6951973Smsmithstatic int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
7051973Smsmithstatic void			mlx_v3_intaction(struct mlx_softc *sc, int action);
7158188Smsmithstatic int			mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
7251973Smsmith
7352225Smsmithstatic int			mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
7452225Smsmithstatic int			mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
7552225Smsmithstatic void			mlx_v4_intaction(struct mlx_softc *sc, int action);
7658188Smsmithstatic int			mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
7752225Smsmith
7854419Smsmithstatic int			mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
7954419Smsmithstatic int			mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
8054419Smsmithstatic void			mlx_v5_intaction(struct mlx_softc *sc, int action);
8158188Smsmithstatic int			mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2);
8254419Smsmith
8351973Smsmith/*
8451973Smsmith * Status monitoring
8551973Smsmith */
8651973Smsmithstatic void			mlx_periodic(void *data);
8751973Smsmithstatic void			mlx_periodic_enquiry(struct mlx_command *mc);
8851973Smsmithstatic void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
8951973Smsmithstatic void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
9051973Smsmithstatic void			mlx_periodic_rebuild(struct mlx_command *mc);
9151973Smsmith
9251973Smsmith/*
9351973Smsmith * Channel Pause
9451973Smsmith */
9551973Smsmithstatic void			mlx_pause_action(struct mlx_softc *sc);
9651973Smsmithstatic void			mlx_pause_done(struct mlx_command *mc);
9751973Smsmith
9851973Smsmith/*
9951973Smsmith * Command submission.
10051973Smsmith */
10151973Smsmithstatic void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
10251973Smsmith					     void (*complete)(struct mlx_command *mc));
10351973Smsmithstatic int			mlx_flush(struct mlx_softc *sc);
10459136Smsmithstatic int			mlx_check(struct mlx_softc *sc, int drive);
10551973Smsmithstatic int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
10651973Smsmithstatic int			mlx_wait_command(struct mlx_command *mc);
10751973Smsmithstatic int			mlx_poll_command(struct mlx_command *mc);
108119665Sscottlvoid				mlx_startio_cb(void *arg,
109119665Sscottl					       bus_dma_segment_t *segs,
110119665Sscottl					       int nsegments, int error);
11151973Smsmithstatic void			mlx_startio(struct mlx_softc *sc);
11251973Smsmithstatic void			mlx_completeio(struct mlx_command *mc);
113119665Sscottlstatic int			mlx_user_command(struct mlx_softc *sc,
114119665Sscottl						 struct mlx_usercommand *mu);
115119665Sscottlvoid				mlx_user_cb(void *arg, bus_dma_segment_t *segs,
116119665Sscottl					    int nsegments, int error);
11751973Smsmith
11851973Smsmith/*
11951973Smsmith * Command buffer allocation.
12051973Smsmith */
12151973Smsmithstatic struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
12251973Smsmithstatic void			mlx_releasecmd(struct mlx_command *mc);
12351973Smsmithstatic void			mlx_freecmd(struct mlx_command *mc);
12451973Smsmith
12551973Smsmith/*
12651973Smsmith * Command management.
12751973Smsmith */
12851973Smsmithstatic int			mlx_getslot(struct mlx_command *mc);
129119665Sscottlstatic void			mlx_setup_dmamap(struct mlx_command *mc,
130119665Sscottl						 bus_dma_segment_t *segs,
131119665Sscottl						 int nsegments, int error);
13251973Smsmithstatic void			mlx_unmapcmd(struct mlx_command *mc);
13351973Smsmithstatic int			mlx_start(struct mlx_command *mc);
13451973Smsmithstatic int			mlx_done(struct mlx_softc *sc);
13551973Smsmithstatic void			mlx_complete(struct mlx_softc *sc);
13651973Smsmith
13751973Smsmith/*
13851973Smsmith * Debugging.
13951973Smsmith */
14051973Smsmithstatic char			*mlx_diagnose_command(struct mlx_command *mc);
14154979Smsmithstatic void			mlx_describe_controller(struct mlx_softc *sc);
14258188Smsmithstatic int			mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
14351973Smsmith
14451973Smsmith/*
14551973Smsmith * Utility functions.
14651973Smsmith */
14751973Smsmithstatic struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
14851973Smsmith
14951973Smsmith/********************************************************************************
15051973Smsmith ********************************************************************************
15151973Smsmith                                                                Public Interfaces
15251973Smsmith ********************************************************************************
15351973Smsmith ********************************************************************************/
15451973Smsmith
15551973Smsmith/********************************************************************************
15651973Smsmith * Free all of the resources associated with (sc)
15751973Smsmith *
15851973Smsmith * Should not be called if the controller is active.
15951973Smsmith */
16051973Smsmithvoid
16151973Smsmithmlx_free(struct mlx_softc *sc)
16251973Smsmith{
16351973Smsmith    struct mlx_command	*mc;
16451973Smsmith
16558188Smsmith    debug_called(1);
16651973Smsmith
16751973Smsmith    /* cancel status timeout */
16851973Smsmith    untimeout(mlx_periodic, sc, sc->mlx_timeout);
16951973Smsmith
17051973Smsmith    /* throw away any command buffers */
17151973Smsmith    while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
17251973Smsmith	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
17351973Smsmith	mlx_freecmd(mc);
17451973Smsmith    }
17551973Smsmith
17651973Smsmith    /* destroy data-transfer DMA tag */
17751973Smsmith    if (sc->mlx_buffer_dmat)
17851973Smsmith	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
17951973Smsmith
18051973Smsmith    /* free and destroy DMA memory and tag for s/g lists */
18151973Smsmith    if (sc->mlx_sgtable)
18251973Smsmith	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
18351973Smsmith    if (sc->mlx_sg_dmat)
18451973Smsmith	bus_dma_tag_destroy(sc->mlx_sg_dmat);
18551973Smsmith
18651973Smsmith    /* disconnect the interrupt handler */
18751973Smsmith    if (sc->mlx_intr)
18851973Smsmith	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
18951973Smsmith    if (sc->mlx_irq != NULL)
19051973Smsmith	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
19151973Smsmith
19251973Smsmith    /* destroy the parent DMA tag */
19351973Smsmith    if (sc->mlx_parent_dmat)
19451973Smsmith	bus_dma_tag_destroy(sc->mlx_parent_dmat);
19551973Smsmith
19651973Smsmith    /* release the register window mapping */
19751973Smsmith    if (sc->mlx_mem != NULL)
19869292Smdodd	bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem);
19954979Smsmith
20054979Smsmith    /* free controller enquiry data */
20154979Smsmith    if (sc->mlx_enq2 != NULL)
20254979Smsmith	free(sc->mlx_enq2, M_DEVBUF);
20358188Smsmith
20458188Smsmith    /* destroy control device */
205130585Sphk    if (sc->mlx_dev_t != (struct cdev *)NULL)
20658188Smsmith	destroy_dev(sc->mlx_dev_t);
20751973Smsmith}
20851973Smsmith
20951973Smsmith/********************************************************************************
21051973Smsmith * Map the scatter/gather table into bus space
21151973Smsmith */
21251973Smsmithstatic void
21351973Smsmithmlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
21451973Smsmith{
21551973Smsmith    struct mlx_softc	*sc = (struct mlx_softc *)arg;
21651973Smsmith
21758188Smsmith    debug_called(1);
21851973Smsmith
21951973Smsmith    /* save base of s/g table's address in bus space */
22051973Smsmith    sc->mlx_sgbusaddr = segs->ds_addr;
22151973Smsmith}
22251973Smsmith
22351973Smsmithstatic int
22451973Smsmithmlx_sglist_map(struct mlx_softc *sc)
22551973Smsmith{
22651973Smsmith    size_t	segsize;
22760074Smsmith    int		error, ncmd;
22851973Smsmith
22958188Smsmith    debug_called(1);
23051973Smsmith
23151973Smsmith    /* destroy any existing mappings */
23251973Smsmith    if (sc->mlx_sgtable)
23351973Smsmith	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
23451973Smsmith    if (sc->mlx_sg_dmat)
23551973Smsmith	bus_dma_tag_destroy(sc->mlx_sg_dmat);
23651973Smsmith
23751973Smsmith    /*
23851973Smsmith     * Create a single tag describing a region large enough to hold all of
23960074Smsmith     * the s/g lists we will need.  If we're called early on, we don't know how
24060074Smsmith     * many commands we're going to be asked to support, so only allocate enough
24160074Smsmith     * for a couple.
24251973Smsmith     */
24360074Smsmith    if (sc->mlx_enq2 == NULL) {
24460074Smsmith	ncmd = 2;
24560074Smsmith    } else {
24660074Smsmith	ncmd = sc->mlx_enq2->me_max_commands;
24760074Smsmith    }
24860074Smsmith    segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd;
24951973Smsmith    error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
250118707Sscottl			       1, 0, 			/* alignment,boundary */
25151973Smsmith			       BUS_SPACE_MAXADDR,	/* lowaddr */
25251973Smsmith			       BUS_SPACE_MAXADDR, 	/* highaddr */
25351973Smsmith			       NULL, NULL, 		/* filter, filterarg */
25451973Smsmith			       segsize, 1,		/* maxsize, nsegments */
25551973Smsmith			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
25651973Smsmith			       0,			/* flags */
257118707Sscottl			       NULL, NULL,		/* lockfunc, lockarg */
25851973Smsmith			       &sc->mlx_sg_dmat);
25951973Smsmith    if (error != 0) {
26051973Smsmith	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
26151973Smsmith	return(ENOMEM);
26251973Smsmith    }
26351973Smsmith
26451973Smsmith    /*
26551973Smsmith     * Allocate enough s/g maps for all commands and permanently map them into
26651973Smsmith     * controller-visible space.
26751973Smsmith     *
26851973Smsmith     * XXX this assumes we can get enough space for all the s/g maps in one
269118707Sscottl     * contiguous slab.  We may need to switch to a more complex arrangement
270118707Sscottl     * where we allocate in smaller chunks and keep a lookup table from slot
271118707Sscottl     * to bus address.
27251973Smsmith     */
273118707Sscottl    error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable,
274118707Sscottl			     BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
27551973Smsmith    if (error) {
27651973Smsmith	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
27751973Smsmith	return(ENOMEM);
27851973Smsmith    }
279119665Sscottl    (void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable,
280119665Sscottl			  segsize, mlx_dma_map_sg, sc, 0);
28151973Smsmith    return(0);
28251973Smsmith}
28351973Smsmith
28451973Smsmith/********************************************************************************
28551973Smsmith * Initialise the controller and softc
28651973Smsmith */
28751973Smsmithint
28851973Smsmithmlx_attach(struct mlx_softc *sc)
28951973Smsmith{
29058188Smsmith    struct mlx_enquiry_old	*meo;
29158188Smsmith    int				rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
29251973Smsmith
29358188Smsmith    debug_called(1);
29451973Smsmith
29551973Smsmith    /*
29651973Smsmith     * Initialise per-controller queues.
29751973Smsmith     */
29852544Smsmith    TAILQ_INIT(&sc->mlx_work);
29951973Smsmith    TAILQ_INIT(&sc->mlx_freecmds);
30078752Smsmith    MLX_BIO_QINIT(sc->mlx_bioq);
30151973Smsmith
30251973Smsmith    /*
30351973Smsmith     * Select accessor methods based on controller interface type.
30451973Smsmith     */
30551973Smsmith    switch(sc->mlx_iftype) {
30658188Smsmith    case MLX_IFTYPE_2:
30751973Smsmith    case MLX_IFTYPE_3:
30851973Smsmith	sc->mlx_tryqueue	= mlx_v3_tryqueue;
30951973Smsmith	sc->mlx_findcomplete	= mlx_v3_findcomplete;
31051973Smsmith	sc->mlx_intaction	= mlx_v3_intaction;
31158188Smsmith	sc->mlx_fw_handshake	= mlx_v3_fw_handshake;
31251973Smsmith	break;
31352225Smsmith    case MLX_IFTYPE_4:
31452225Smsmith	sc->mlx_tryqueue	= mlx_v4_tryqueue;
31552225Smsmith	sc->mlx_findcomplete	= mlx_v4_findcomplete;
31652225Smsmith	sc->mlx_intaction	= mlx_v4_intaction;
31758188Smsmith	sc->mlx_fw_handshake	= mlx_v4_fw_handshake;
31852225Smsmith	break;
31954419Smsmith    case MLX_IFTYPE_5:
32054419Smsmith	sc->mlx_tryqueue	= mlx_v5_tryqueue;
32154419Smsmith	sc->mlx_findcomplete	= mlx_v5_findcomplete;
32254419Smsmith	sc->mlx_intaction	= mlx_v5_intaction;
32358188Smsmith	sc->mlx_fw_handshake	= mlx_v5_fw_handshake;
32454419Smsmith	break;
32551973Smsmith    default:
32651973Smsmith	return(ENXIO);		/* should never happen */
32751973Smsmith    }
32851973Smsmith
32951973Smsmith    /* disable interrupts before we start talking to the controller */
33051973Smsmith    sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
33151973Smsmith
33251973Smsmith    /*
33358188Smsmith     * Wait for the controller to come ready, handshake with the firmware if required.
33458188Smsmith     * This is typically only necessary on platforms where the controller BIOS does not
33558188Smsmith     * run.
33658188Smsmith     */
33758188Smsmith    hsmsg = 0;
33858188Smsmith    DELAY(1000);
33958188Smsmith    while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) {
34058188Smsmith	/* report first time around... */
34158188Smsmith	if (hsmsg == 0) {
34258188Smsmith	    device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
34358188Smsmith	    hsmsg = 1;
34458188Smsmith	}
34558188Smsmith	/* did we get a real message? */
34658188Smsmith	if (hscode == 2) {
34758188Smsmith	    hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
34858188Smsmith	    /* fatal initialisation error? */
34958188Smsmith	    if (hscode != 0) {
35058188Smsmith		return(ENXIO);
35158188Smsmith	    }
35258188Smsmith	}
35358188Smsmith    }
35458188Smsmith    if (hsmsg == 1)
35558188Smsmith	device_printf(sc->mlx_dev, "initialisation complete.\n");
35658188Smsmith
35758188Smsmith    /*
35851973Smsmith     * Allocate and connect our interrupt.
35951973Smsmith     */
36051973Smsmith    rid = 0;
361127135Snjl    sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid,
362127135Snjl        RF_SHAREABLE | RF_ACTIVE);
36351973Smsmith    if (sc->mlx_irq == NULL) {
36459136Smsmith	device_printf(sc->mlx_dev, "can't allocate interrupt\n");
36551973Smsmith	return(ENXIO);
36651973Smsmith    }
367166901Spiso    error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY, NULL, mlx_intr, sc, &sc->mlx_intr);
36851973Smsmith    if (error) {
36959136Smsmith	device_printf(sc->mlx_dev, "can't set up interrupt\n");
37051973Smsmith	return(ENXIO);
37151973Smsmith    }
37251973Smsmith
37351973Smsmith    /*
37451973Smsmith     * Create DMA tag for mapping buffers into controller-addressable space.
37551973Smsmith     */
376119665Sscottl    error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
377119665Sscottl			       1, 0, 			/* align, boundary */
378119665Sscottl			       BUS_SPACE_MAXADDR,	/* lowaddr */
379119665Sscottl			       BUS_SPACE_MAXADDR, 	/* highaddr */
380119665Sscottl			       NULL, NULL, 		/* filter, filterarg */
381119665Sscottl			       MAXBSIZE, MLX_NSEG,	/* maxsize, nsegments */
382119665Sscottl			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
383119665Sscottl			       0,			/* flags */
384117126Sscottl			       busdma_lock_mutex,	/* lockfunc */
385117126Sscottl			       &Giant,			/* lockarg */
38651973Smsmith			       &sc->mlx_buffer_dmat);
38751973Smsmith    if (error != 0) {
38851973Smsmith	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
38951973Smsmith	return(ENOMEM);
39051973Smsmith    }
39151973Smsmith
39251973Smsmith    /*
393119665Sscottl     * Create some initial scatter/gather mappings so we can run the probe
394119665Sscottl     * commands.
39551973Smsmith     */
39651973Smsmith    error = mlx_sglist_map(sc);
39751973Smsmith    if (error != 0) {
39859136Smsmith	device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
39951973Smsmith	return(error);
40051973Smsmith    }
40151973Smsmith
40260074Smsmith    /*
40360074Smsmith     * We don't (yet) know where the event log is up to.
40460074Smsmith     */
40560074Smsmith    sc->mlx_currevent = -1;
40660074Smsmith
40760074Smsmith    /*
40860074Smsmith     * Obtain controller feature information
40960074Smsmith     */
41054979Smsmith    if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
41151973Smsmith	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
41251973Smsmith	return(ENXIO);
41351973Smsmith    }
41451973Smsmith
41554979Smsmith    /*
41651973Smsmith     * Do quirk/feature related things.
41751973Smsmith     */
41854979Smsmith    fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
41951973Smsmith    switch(sc->mlx_iftype) {
42058188Smsmith    case MLX_IFTYPE_2:
42158188Smsmith	/* These controllers don't report the firmware version in the ENQUIRY2 response */
42258188Smsmith	if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
42358188Smsmith	    device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
42458188Smsmith	    return(ENXIO);
42558188Smsmith	}
42658188Smsmith	sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
42758188Smsmith
42858188Smsmith	/* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */
42958188Smsmith	if (meo->me_fwminor < 42) {
43058188Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
43158188Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
43258188Smsmith	}
433126113Scperciva	free(meo, M_DEVBUF);
43458188Smsmith	break;
43551973Smsmith    case MLX_IFTYPE_3:
43652225Smsmith	/* XXX certify 3.52? */
43754979Smsmith	if (fwminor < 51) {
43852544Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
43952544Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
44051973Smsmith	}
44151973Smsmith	break;
44252225Smsmith    case MLX_IFTYPE_4:
44352225Smsmith	/* XXX certify firmware versions? */
44454979Smsmith	if (fwminor < 6) {
44552544Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
44652544Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
44752225Smsmith	}
44852225Smsmith	break;
44954419Smsmith    case MLX_IFTYPE_5:
45054979Smsmith	if (fwminor < 7) {
45154419Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
45254419Smsmith	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
45354419Smsmith	}
45454419Smsmith	break;
45551973Smsmith    default:
45651973Smsmith	return(ENXIO);		/* should never happen */
45751973Smsmith    }
45851973Smsmith
45951973Smsmith    /*
46060074Smsmith     * Create the final scatter/gather mappings now that we have characterised the controller.
46151973Smsmith     */
46251973Smsmith    error = mlx_sglist_map(sc);
46351973Smsmith    if (error != 0) {
46460074Smsmith	device_printf(sc->mlx_dev, "can't make final s/g list mapping\n");
46551973Smsmith	return(error);
46651973Smsmith    }
46751973Smsmith
46851973Smsmith    /*
46959136Smsmith     * No user-requested background operation is in progress.
47051973Smsmith     */
47159136Smsmith    sc->mlx_background = 0;
47259136Smsmith    sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
47351973Smsmith
47451973Smsmith    /*
47558188Smsmith     * Create the control device.
47651973Smsmith     */
477191242Sed    sc->mlx_dev_t = make_dev(&mlx_cdevsw, 0, UID_ROOT, GID_OPERATOR,
47858188Smsmith			     S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
479191242Sed    sc->mlx_dev_t->si_drv1 = sc;
48051973Smsmith
48151973Smsmith    /*
48251973Smsmith     * Start the timeout routine.
48351973Smsmith     */
48451973Smsmith    sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
48551973Smsmith
48658188Smsmith    /* print a little information about the controller */
48758188Smsmith    mlx_describe_controller(sc);
48858188Smsmith
48951973Smsmith    return(0);
49051973Smsmith}
49151973Smsmith
49251973Smsmith/********************************************************************************
49351973Smsmith * Locate disk resources and attach children to them.
49451973Smsmith */
49551973Smsmithvoid
49651973Smsmithmlx_startup(struct mlx_softc *sc)
49751973Smsmith{
49851973Smsmith    struct mlx_enq_sys_drive	*mes;
49951973Smsmith    struct mlx_sysdrive		*dr;
50051973Smsmith    int				i, error;
50151973Smsmith
50258188Smsmith    debug_called(1);
50351973Smsmith
50451973Smsmith    /*
50551973Smsmith     * Scan all the system drives and attach children for those that
50651973Smsmith     * don't currently have them.
50751973Smsmith     */
50851973Smsmith    mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
50951973Smsmith    if (mes == NULL) {
51054979Smsmith	device_printf(sc->mlx_dev, "error fetching drive status\n");
51151973Smsmith	return;
51251973Smsmith    }
51351973Smsmith
51451973Smsmith    /* iterate over drives returned */
51551973Smsmith    for (i = 0, dr = &sc->mlx_sysdrive[0];
51651973Smsmith	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
51751973Smsmith	 i++, dr++) {
51851973Smsmith	/* are we already attached to this drive? */
51951973Smsmith    	if (dr->ms_disk == 0) {
52051973Smsmith	    /* pick up drive information */
52151973Smsmith	    dr->ms_size = mes[i].sd_size;
52252225Smsmith	    dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
52351973Smsmith	    dr->ms_state = mes[i].sd_state;
52451973Smsmith
52551973Smsmith	    /* generate geometry information */
52651973Smsmith	    if (sc->mlx_geom == MLX_GEOM_128_32) {
52751973Smsmith		dr->ms_heads = 128;
52851973Smsmith		dr->ms_sectors = 32;
52951973Smsmith		dr->ms_cylinders = dr->ms_size / (128 * 32);
53051973Smsmith	    } else {        /* MLX_GEOM_255/63 */
53151973Smsmith		dr->ms_heads = 255;
53251973Smsmith		dr->ms_sectors = 63;
53351973Smsmith		dr->ms_cylinders = dr->ms_size / (255 * 63);
53451973Smsmith	    }
53554073Smdodd	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1);
53651973Smsmith	    if (dr->ms_disk == 0)
53751973Smsmith		device_printf(sc->mlx_dev, "device_add_child failed\n");
53854073Smdodd	    device_set_ivars(dr->ms_disk, dr);
53951973Smsmith	}
54051973Smsmith    }
54151973Smsmith    free(mes, M_DEVBUF);
54251973Smsmith    if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
54351973Smsmith	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
54451973Smsmith
54551973Smsmith    /* mark controller back up */
54651973Smsmith    sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
54751973Smsmith
54851973Smsmith    /* enable interrupts */
54951973Smsmith    sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
55051973Smsmith}
55151973Smsmith
55251973Smsmith/********************************************************************************
55351973Smsmith * Disconnect from the controller completely, in preparation for unload.
55451973Smsmith */
55551973Smsmithint
55651973Smsmithmlx_detach(device_t dev)
55751973Smsmith{
55851973Smsmith    struct mlx_softc	*sc = device_get_softc(dev);
55954419Smsmith    struct mlxd_softc	*mlxd;
56054419Smsmith    int			i, s, error;
56151973Smsmith
56258188Smsmith    debug_called(1);
56351973Smsmith
56454419Smsmith    error = EBUSY;
56554419Smsmith    s = splbio();
56651973Smsmith    if (sc->mlx_state & MLX_STATE_OPEN)
56754419Smsmith	goto out;
56851973Smsmith
56954419Smsmith    for (i = 0; i < MLX_MAXDRIVES; i++) {
57054419Smsmith	if (sc->mlx_sysdrive[i].ms_disk != 0) {
57154419Smsmith	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
57254419Smsmith	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort detach */
57354419Smsmith		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
57454419Smsmith		goto out;
57554419Smsmith	    }
57654419Smsmith	}
57754419Smsmith    }
57851973Smsmith    if ((error = mlx_shutdown(dev)))
57954419Smsmith	goto out;
58051973Smsmith
58151973Smsmith    mlx_free(sc);
58251973Smsmith
58354419Smsmith    error = 0;
58454419Smsmith out:
58554419Smsmith    splx(s);
58654419Smsmith    return(error);
58751973Smsmith}
58851973Smsmith
58951973Smsmith/********************************************************************************
59051973Smsmith * Bring the controller down to a dormant state and detach all child devices.
59151973Smsmith *
59251973Smsmith * This function is called before detach, system shutdown, or before performing
59351973Smsmith * an operation which may add or delete system disks.  (Call mlx_startup to
59451973Smsmith * resume normal operation.)
59551973Smsmith *
59659249Sphk * Note that we can assume that the bioq on the controller is empty, as we won't
59751973Smsmith * allow shutdown if any device is open.
59851973Smsmith */
59951973Smsmithint
60051973Smsmithmlx_shutdown(device_t dev)
60151973Smsmith{
60251973Smsmith    struct mlx_softc	*sc = device_get_softc(dev);
60351973Smsmith    int			i, s, error;
60451973Smsmith
60558188Smsmith    debug_called(1);
60651973Smsmith
60751973Smsmith    s = splbio();
60851973Smsmith    error = 0;
60951973Smsmith
61051973Smsmith    sc->mlx_state |= MLX_STATE_SHUTDOWN;
61154419Smsmith    sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
61251973Smsmith
61351973Smsmith    /* flush controller */
61451973Smsmith    device_printf(sc->mlx_dev, "flushing cache...");
61551973Smsmith    if (mlx_flush(sc)) {
61651973Smsmith	printf("failed\n");
61751973Smsmith    } else {
61851973Smsmith	printf("done\n");
61951973Smsmith    }
62051973Smsmith
62151973Smsmith    /* delete all our child devices */
62251973Smsmith    for (i = 0; i < MLX_MAXDRIVES; i++) {
62351973Smsmith	if (sc->mlx_sysdrive[i].ms_disk != 0) {
62451973Smsmith	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
62551973Smsmith		goto out;
62651973Smsmith	    sc->mlx_sysdrive[i].ms_disk = 0;
62751973Smsmith	}
62851973Smsmith    }
62951973Smsmith
63051973Smsmith out:
63151973Smsmith    splx(s);
63251973Smsmith    return(error);
63351973Smsmith}
63451973Smsmith
63551973Smsmith/********************************************************************************
63651973Smsmith * Bring the controller to a quiescent state, ready for system suspend.
63751973Smsmith */
63851973Smsmithint
63951973Smsmithmlx_suspend(device_t dev)
64051973Smsmith{
64151973Smsmith    struct mlx_softc	*sc = device_get_softc(dev);
64251973Smsmith    int			s;
64351973Smsmith
64458188Smsmith    debug_called(1);
64551973Smsmith
64651973Smsmith    s = splbio();
64751973Smsmith    sc->mlx_state |= MLX_STATE_SUSPEND;
64851973Smsmith
64951973Smsmith    /* flush controller */
65051973Smsmith    device_printf(sc->mlx_dev, "flushing cache...");
65151973Smsmith    printf("%s\n", mlx_flush(sc) ? "failed" : "done");
65251973Smsmith
65351973Smsmith    sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
65451973Smsmith    splx(s);
65551973Smsmith
65651973Smsmith    return(0);
65751973Smsmith}
65851973Smsmith
65951973Smsmith/********************************************************************************
66051973Smsmith * Bring the controller back to a state ready for operation.
66151973Smsmith */
66251973Smsmithint
66351973Smsmithmlx_resume(device_t dev)
66451973Smsmith{
66551973Smsmith    struct mlx_softc	*sc = device_get_softc(dev);
66651973Smsmith
66758188Smsmith    debug_called(1);
66851973Smsmith
66951973Smsmith    sc->mlx_state &= ~MLX_STATE_SUSPEND;
67051973Smsmith    sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
67151973Smsmith
67251973Smsmith    return(0);
67351973Smsmith}
67451973Smsmith
67551973Smsmith/*******************************************************************************
67651973Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy
67751973Smsmith * status.
67851973Smsmith */
67951973Smsmithvoid
68051973Smsmithmlx_intr(void *arg)
68151973Smsmith{
68251973Smsmith    struct mlx_softc	*sc = (struct mlx_softc *)arg;
68351973Smsmith
68458188Smsmith    debug_called(1);
68551973Smsmith
68654419Smsmith    /* collect finished commands, queue anything waiting */
68754419Smsmith    mlx_done(sc);
68851973Smsmith};
68951973Smsmith
69051973Smsmith/*******************************************************************************
69151973Smsmith * Receive a buf structure from a child device and queue it on a particular
69251973Smsmith * disk resource, then poke the disk resource to start as much work as it can.
69351973Smsmith */
69451973Smsmithint
69578752Smsmithmlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp)
69651973Smsmith{
69752544Smsmith    int		s;
69852544Smsmith
69958188Smsmith    debug_called(1);
70051973Smsmith
70152544Smsmith    s = splbio();
70278752Smsmith    MLX_BIO_QINSERT(sc->mlx_bioq, bp);
70351973Smsmith    sc->mlx_waitbufs++;
70452544Smsmith    splx(s);
70551973Smsmith    mlx_startio(sc);
70651973Smsmith    return(0);
70751973Smsmith}
70851973Smsmith
70951973Smsmith/********************************************************************************
71051973Smsmith * Accept an open operation on the control device.
71151973Smsmith */
71251973Smsmithint
713130585Sphkmlx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
71451973Smsmith{
715191242Sed    struct mlx_softc	*sc = dev->si_drv1;
71651973Smsmith
71751973Smsmith    sc->mlx_state |= MLX_STATE_OPEN;
71851973Smsmith    return(0);
71951973Smsmith}
72051973Smsmith
72151973Smsmith/********************************************************************************
72251973Smsmith * Accept the last close on the control device.
72351973Smsmith */
72451973Smsmithint
725130585Sphkmlx_close(struct cdev *dev, int flags, int fmt, struct thread *td)
72651973Smsmith{
727191242Sed    struct mlx_softc	*sc = dev->si_drv1;
72851973Smsmith
72951973Smsmith    sc->mlx_state &= ~MLX_STATE_OPEN;
73051973Smsmith    return (0);
73151973Smsmith}
73251973Smsmith
73351973Smsmith/********************************************************************************
73451973Smsmith * Handle controller-specific control operations.
73551973Smsmith */
73651973Smsmithint
737130585Sphkmlx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
73851973Smsmith{
739191242Sed    struct mlx_softc		*sc = dev->si_drv1;
74059136Smsmith    struct mlx_rebuild_request	*rb = (struct mlx_rebuild_request *)addr;
74159136Smsmith    struct mlx_rebuild_status	*rs = (struct mlx_rebuild_status *)addr;
74259136Smsmith    int				*arg = (int *)addr;
74359136Smsmith    struct mlx_pause		*mp;
74459136Smsmith    struct mlx_sysdrive		*dr;
74559136Smsmith    struct mlxd_softc		*mlxd;
74659136Smsmith    int				i, error;
74751973Smsmith
74851973Smsmith    switch(cmd) {
74951973Smsmith	/*
75051973Smsmith	 * Enumerate connected system drives; returns the first system drive's
75151973Smsmith	 * unit number if *arg is -1, or the next unit after *arg if it's
75251973Smsmith	 * a valid unit on this controller.
75351973Smsmith	 */
75451973Smsmith    case MLX_NEXT_CHILD:
75551973Smsmith	/* search system drives */
75651973Smsmith	for (i = 0; i < MLX_MAXDRIVES; i++) {
75751973Smsmith	    /* is this one attached? */
75851973Smsmith	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
75951973Smsmith		/* looking for the next one we come across? */
76051973Smsmith		if (*arg == -1) {
761182965Ssepotvin		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
76251973Smsmith		    return(0);
76351973Smsmith		}
76451973Smsmith		/* we want the one after this one */
76551973Smsmith		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
76651973Smsmith		    *arg = -1;
76751973Smsmith	    }
76851973Smsmith	}
76951973Smsmith	return(ENOENT);
77051973Smsmith
77151973Smsmith	/*
77251973Smsmith	 * Scan the controller to see whether new drives have appeared.
77351973Smsmith	 */
77451973Smsmith    case MLX_RESCAN_DRIVES:
77551973Smsmith	mlx_startup(sc);
77651973Smsmith	return(0);
77751973Smsmith
77851973Smsmith	/*
77951973Smsmith	 * Disconnect from the specified drive; it may be about to go
78051973Smsmith	 * away.
78151973Smsmith	 */
78251973Smsmith    case MLX_DETACH_DRIVE:			/* detach one drive */
78351973Smsmith
78451973Smsmith	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
78551973Smsmith	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
78651973Smsmith	    return(ENOENT);
78751973Smsmith
78851973Smsmith	device_printf(dr->ms_disk, "detaching...");
78951973Smsmith	error = 0;
79051973Smsmith	if (mlxd->mlxd_flags & MLXD_OPEN) {
79151973Smsmith	    error = EBUSY;
79251973Smsmith	    goto detach_out;
79351973Smsmith	}
79451973Smsmith
79551973Smsmith	/* flush controller */
79651973Smsmith	if (mlx_flush(sc)) {
79751973Smsmith	    error = EBUSY;
79851973Smsmith	    goto detach_out;
79951973Smsmith	}
80051973Smsmith
80151973Smsmith	/* nuke drive */
80251973Smsmith	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
80351973Smsmith	    goto detach_out;
80451973Smsmith	dr->ms_disk = 0;
80551973Smsmith
80651973Smsmith    detach_out:
80751973Smsmith	if (error) {
80851973Smsmith	    printf("failed\n");
80951973Smsmith	} else {
81051973Smsmith	    printf("done\n");
81151973Smsmith	}
81251973Smsmith	return(error);
81351973Smsmith
81451973Smsmith	/*
81551973Smsmith	 * Pause one or more SCSI channels for a period of time, to assist
81651973Smsmith	 * in the process of hot-swapping devices.
81751973Smsmith	 *
81851973Smsmith	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
81951973Smsmith	 * to do this right.
82051973Smsmith	 */
82151973Smsmith    case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
82251973Smsmith	/* Does this command work on this firmware? */
82351973Smsmith	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
82451973Smsmith	    return(EOPNOTSUPP);
82551973Smsmith
82651973Smsmith	mp = (struct mlx_pause *)addr;
82751973Smsmith	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
82851973Smsmith	    /* cancel a pending pause operation */
82951973Smsmith	    sc->mlx_pause.mp_which = 0;
83051973Smsmith	} else {
83151973Smsmith	    /* fix for legal channels */
83254979Smsmith	    mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
83351973Smsmith	    /* check time values */
83451973Smsmith	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
83551973Smsmith		return(EINVAL);
83651973Smsmith	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
83751973Smsmith		return(EINVAL);
83851973Smsmith
83951973Smsmith	    /* check for a pause currently running */
84051973Smsmith	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
84151973Smsmith		return(EBUSY);
84251973Smsmith
84351973Smsmith	    /* looks ok, go with it */
84451973Smsmith	    sc->mlx_pause.mp_which = mp->mp_which;
84551973Smsmith	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
84651973Smsmith	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
84751973Smsmith	}
84851973Smsmith	return(0);
84951973Smsmith
85051973Smsmith	/*
85151973Smsmith	 * Accept a command passthrough-style.
85251973Smsmith	 */
85351973Smsmith    case MLX_COMMAND:
85451973Smsmith	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
85551973Smsmith
85659136Smsmith	/*
85759136Smsmith	 * Start a rebuild on a given SCSI disk
85859136Smsmith	 */
85959136Smsmith    case MLX_REBUILDASYNC:
86059136Smsmith	if (sc->mlx_background != 0) {
86159136Smsmith	    rb->rr_status = 0x0106;
86259136Smsmith	    return(EBUSY);
86359136Smsmith	}
86459136Smsmith	rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
86559136Smsmith	switch (rb->rr_status) {
86659136Smsmith	case 0:
86759136Smsmith	    error = 0;
86859136Smsmith	    break;
86959136Smsmith	case 0x10000:
87059136Smsmith	    error = ENOMEM;		/* couldn't set up the command */
87159136Smsmith	    break;
87259136Smsmith	case 0x0002:
87359136Smsmith	    error = EBUSY;
87459136Smsmith	    break;
87559136Smsmith	case 0x0104:
87659136Smsmith	    error = EIO;
87759136Smsmith	    break;
87859136Smsmith	case 0x0105:
87959136Smsmith	    error = ERANGE;
88059136Smsmith	    break;
88159136Smsmith	case 0x0106:
88259136Smsmith	    error = EBUSY;
88359136Smsmith	    break;
88459136Smsmith	default:
88559136Smsmith	    error = EINVAL;
88659136Smsmith	    break;
88759136Smsmith	}
88859136Smsmith	if (error == 0)
88959136Smsmith	    sc->mlx_background = MLX_BACKGROUND_REBUILD;
89059136Smsmith	return(error);
89159136Smsmith
89259136Smsmith	/*
89359136Smsmith	 * Get the status of the current rebuild or consistency check.
89459136Smsmith	 */
89559136Smsmith    case MLX_REBUILDSTAT:
89659136Smsmith	*rs = sc->mlx_rebuildstat;
89759136Smsmith	return(0);
89859136Smsmith
89959136Smsmith	/*
90059136Smsmith	 * Return the per-controller system drive number matching the
90159136Smsmith	 * disk device number in (arg), if it happens to belong to us.
90259136Smsmith	 */
90359136Smsmith    case MLX_GET_SYSDRIVE:
90459136Smsmith	error = ENOENT;
90559136Smsmith	mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg);
90659136Smsmith	if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
90759136Smsmith	    (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
90859136Smsmith	    error = 0;
90959136Smsmith	    *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
91059136Smsmith	}
91159136Smsmith	return(error);
91259136Smsmith
91351973Smsmith    default:
91451973Smsmith	return(ENOTTY);
91551973Smsmith    }
91651973Smsmith}
91751973Smsmith
91851973Smsmith/********************************************************************************
91951973Smsmith * Handle operations requested by a System Drive connected to this controller.
92051973Smsmith */
92151973Smsmithint
92251973Smsmithmlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
92383366Sjulian		caddr_t addr, int32_t flag, struct thread *td)
92451973Smsmith{
92551973Smsmith    int				*arg = (int *)addr;
92659136Smsmith    int				error, result;
92751973Smsmith
92851973Smsmith    switch(cmd) {
92951973Smsmith	/*
93051973Smsmith	 * Return the current status of this drive.
93151973Smsmith	 */
93251973Smsmith    case MLXD_STATUS:
93351973Smsmith	*arg = drive->ms_state;
93451973Smsmith	return(0);
93551973Smsmith
93651973Smsmith	/*
93759136Smsmith	 * Start a background consistency check on this drive.
93851973Smsmith	 */
93959136Smsmith    case MLXD_CHECKASYNC:		/* start a background consistency check */
94059136Smsmith	if (sc->mlx_background != 0) {
94159136Smsmith	    *arg = 0x0106;
94251973Smsmith	    return(EBUSY);
94359136Smsmith	}
94459136Smsmith	result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
94559136Smsmith	switch (result) {
94651973Smsmith	case 0:
94751973Smsmith	    error = 0;
94851973Smsmith	    break;
94951973Smsmith	case 0x10000:
95051973Smsmith	    error = ENOMEM;		/* couldn't set up the command */
95151973Smsmith	    break;
95259136Smsmith	case 0x0002:
95351973Smsmith	    error = EIO;
95451973Smsmith	    break;
95551973Smsmith	case 0x0105:
95651973Smsmith	    error = ERANGE;
95751973Smsmith	    break;
95859136Smsmith	case 0x0106:
95959136Smsmith	    error = EBUSY;
96059136Smsmith	    break;
96151973Smsmith	default:
96251973Smsmith	    error = EINVAL;
96351973Smsmith	    break;
96451973Smsmith	}
96559136Smsmith	if (error == 0)
96659136Smsmith	    sc->mlx_background = MLX_BACKGROUND_CHECK;
96759136Smsmith	*arg = result;
96851973Smsmith	return(error);
96951973Smsmith
97051973Smsmith    }
97151973Smsmith    return(ENOIOCTL);
97251973Smsmith}
97351973Smsmith
97451973Smsmith
97551973Smsmith/********************************************************************************
97651973Smsmith ********************************************************************************
97751973Smsmith                                                                Status Monitoring
97851973Smsmith ********************************************************************************
97951973Smsmith ********************************************************************************/
98051973Smsmith
98151973Smsmith/********************************************************************************
98251973Smsmith * Fire off commands to periodically check the status of connected drives.
98351973Smsmith */
98451973Smsmithstatic void
98551973Smsmithmlx_periodic(void *data)
98651973Smsmith{
98751973Smsmith    struct mlx_softc *sc = (struct mlx_softc *)data;
98851973Smsmith
98958188Smsmith    debug_called(1);
99051973Smsmith
99151973Smsmith    /*
99251973Smsmith     * Run a bus pause?
99351973Smsmith     */
99451973Smsmith    if ((sc->mlx_pause.mp_which != 0) &&
99551973Smsmith	(sc->mlx_pause.mp_when > 0) &&
99651973Smsmith	(time_second >= sc->mlx_pause.mp_when)){
99751973Smsmith
99851973Smsmith	mlx_pause_action(sc);		/* pause is running */
99951973Smsmith	sc->mlx_pause.mp_when = 0;
100051973Smsmith	sysbeep(500, hz);
100151973Smsmith
100251973Smsmith	/*
100351973Smsmith	 * Bus pause still running?
100451973Smsmith	 */
100551973Smsmith    } else if ((sc->mlx_pause.mp_which != 0) &&
100651973Smsmith	       (sc->mlx_pause.mp_when == 0)) {
100751973Smsmith
100851973Smsmith	/* time to stop bus pause? */
100951973Smsmith	if (time_second >= sc->mlx_pause.mp_howlong) {
101051973Smsmith	    mlx_pause_action(sc);
101151973Smsmith	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
101251973Smsmith	    sysbeep(500, hz);
101351973Smsmith	} else {
101451973Smsmith	    sysbeep((time_second % 5) * 100 + 500, hz/8);
101551973Smsmith	}
101651973Smsmith
101751973Smsmith	/*
101851973Smsmith	 * Run normal periodic activities?
101951973Smsmith	 */
102054419Smsmith    } else if (time_second > (sc->mlx_lastpoll + 10)) {
102154419Smsmith	sc->mlx_lastpoll = time_second;
102251973Smsmith
102354419Smsmith	/*
102454419Smsmith	 * Check controller status.
102554419Smsmith	 *
102654419Smsmith	 * XXX Note that this may not actually launch a command in situations of high load.
102754419Smsmith	 */
102858188Smsmith	mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
102958188Smsmith		    imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
103051973Smsmith
103154419Smsmith	/*
103254419Smsmith	 * Check system drive status.
103354419Smsmith	 *
103454419Smsmith	 * XXX This might be better left to event-driven detection, eg. I/O to an offline
103554419Smsmith	 *     drive will detect it's offline, rebuilds etc. should detect the drive is back
103654419Smsmith	 *     online.
103754419Smsmith	 */
103854419Smsmith	mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
103951973Smsmith			mlx_periodic_enquiry);
104054419Smsmith
104151973Smsmith    }
104251973Smsmith
104359136Smsmith    /* get drive rebuild/check status */
104459136Smsmith    /* XXX should check sc->mlx_background if this is only valid while in progress */
104559136Smsmith    mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
104659136Smsmith
104754419Smsmith    /* deal with possibly-missed interrupts and timed-out commands */
104854419Smsmith    mlx_done(sc);
104951973Smsmith
105051973Smsmith    /* reschedule another poll next second or so */
105151973Smsmith    sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
105251973Smsmith}
105351973Smsmith
105451973Smsmith/********************************************************************************
105551973Smsmith * Handle the result of an ENQUIRY command instigated by periodic status polling.
105651973Smsmith */
105751973Smsmithstatic void
105851973Smsmithmlx_periodic_enquiry(struct mlx_command *mc)
105951973Smsmith{
106051973Smsmith    struct mlx_softc		*sc = mc->mc_sc;
106151973Smsmith
106258188Smsmith    debug_called(1);
106351973Smsmith
106451973Smsmith    /* Command completed OK? */
106551973Smsmith    if (mc->mc_status != 0) {
106658188Smsmith	device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
106751973Smsmith	goto out;
106851973Smsmith    }
106951973Smsmith
107051973Smsmith    /* respond to command */
107151973Smsmith    switch(mc->mc_mailbox[0]) {
107251973Smsmith	/*
107358188Smsmith	 * This is currently a bit fruitless, as we don't know how to extract the eventlog
107458188Smsmith	 * pointer yet.
107558188Smsmith	 */
107658188Smsmith    case MLX_CMD_ENQUIRY_OLD:
107758188Smsmith    {
107858188Smsmith	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
107958188Smsmith	struct mlx_enquiry_old		*meo = (struct mlx_enquiry_old *)mc->mc_data;
108058188Smsmith	int				i;
108158188Smsmith
108258188Smsmith	/* convert data in-place to new format */
108358188Smsmith	for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
108458188Smsmith	    me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
108558188Smsmith	    me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
108658188Smsmith	}
108758188Smsmith	me->me_misc_flags        = 0;
108858188Smsmith	me->me_rebuild_count     = meo->me_rebuild_count;
108958188Smsmith	me->me_dead_count        = meo->me_dead_count;
109058188Smsmith	me->me_critical_sd_count = meo->me_critical_sd_count;
109158188Smsmith	me->me_event_log_seq_num = 0;
109258188Smsmith	me->me_offline_sd_count  = meo->me_offline_sd_count;
109358188Smsmith	me->me_max_commands      = meo->me_max_commands;
109458188Smsmith	me->me_rebuild_flag      = meo->me_rebuild_flag;
109558188Smsmith	me->me_fwmajor           = meo->me_fwmajor;
109658188Smsmith	me->me_fwminor           = meo->me_fwminor;
109758188Smsmith	me->me_status_flags      = meo->me_status_flags;
109858188Smsmith	me->me_flash_age         = meo->me_flash_age;
109958188Smsmith	for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
110058188Smsmith	    if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
110158188Smsmith		me->me_drvsize[i] = 0;		/* drive beyond supported range */
110258188Smsmith	    } else {
110358188Smsmith		me->me_drvsize[i] = meo->me_drvsize[i];
110458188Smsmith	    }
110558188Smsmith	}
110658188Smsmith	me->me_num_sys_drvs = meo->me_num_sys_drvs;
110758188Smsmith    }
110858188Smsmith    /* FALLTHROUGH */
110958188Smsmith
111058188Smsmith	/*
111151973Smsmith	 * Generic controller status update.  We could do more with this than just
111251973Smsmith	 * checking the event log.
111351973Smsmith	 */
111451973Smsmith    case MLX_CMD_ENQUIRY:
111551973Smsmith    {
111651973Smsmith	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
111751973Smsmith
111859136Smsmith	if (sc->mlx_currevent == -1) {
111954979Smsmith	    /* initialise our view of the event log */
112054979Smsmith	    sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
112159447Smsmith	} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
112251973Smsmith	    /* record where current events are up to */
112351973Smsmith	    sc->mlx_currevent = me->me_event_log_seq_num;
112458188Smsmith	    debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
112551973Smsmith
112659447Smsmith	    /* mark the event log as busy */
112759447Smsmith	    atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
112859447Smsmith
112954979Smsmith	    /* drain new eventlog entries */
113051973Smsmith	    mlx_periodic_eventlog_poll(sc);
113151973Smsmith	}
113251973Smsmith	break;
113351973Smsmith    }
113451973Smsmith    case MLX_CMD_ENQSYSDRIVE:
113551973Smsmith    {
113651973Smsmith	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
113751973Smsmith	struct mlx_sysdrive		*dr;
113851973Smsmith	int				i;
113951973Smsmith
114051973Smsmith	for (i = 0, dr = &sc->mlx_sysdrive[0];
114151973Smsmith	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
114251973Smsmith	     i++) {
114351973Smsmith
114459136Smsmith	    /* has state been changed by controller? */
114559136Smsmith	    if (dr->ms_state != mes[i].sd_state) {
114659136Smsmith		switch(mes[i].sd_state) {
114759136Smsmith		case MLX_SYSD_OFFLINE:
114859136Smsmith		    device_printf(dr->ms_disk, "drive offline\n");
114959136Smsmith		    break;
115059136Smsmith		case MLX_SYSD_ONLINE:
115159136Smsmith		    device_printf(dr->ms_disk, "drive online\n");
115259136Smsmith		    break;
115359136Smsmith		case MLX_SYSD_CRITICAL:
115459136Smsmith		    device_printf(dr->ms_disk, "drive critical\n");
115559136Smsmith		    break;
115651973Smsmith		}
115759136Smsmith		/* save new state */
115859136Smsmith		dr->ms_state = mes[i].sd_state;
115951973Smsmith	    }
116051973Smsmith	}
116151973Smsmith	break;
116251973Smsmith    }
116351973Smsmith    default:
116487599Sobrien	device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]);
116551973Smsmith	break;
116651973Smsmith    }
116751973Smsmith
116851973Smsmith out:
116951973Smsmith    free(mc->mc_data, M_DEVBUF);
117051973Smsmith    mlx_releasecmd(mc);
117151973Smsmith}
117251973Smsmith
1173119665Sscottlstatic void
1174119665Sscottlmlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
1175119665Sscottl{
1176119665Sscottl    struct mlx_command *mc;
1177119665Sscottl
1178119665Sscottl    mc = (struct mlx_command *)arg;
1179119665Sscottl    mlx_setup_dmamap(mc, segs, nsegments, error);
1180119665Sscottl
1181119665Sscottl    /* build the command to get one entry */
1182119665Sscottl    mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1,
1183119665Sscottl		   mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
1184119665Sscottl    mc->mc_complete = mlx_periodic_eventlog_respond;
1185119665Sscottl    mc->mc_private = mc;
1186119665Sscottl
1187119665Sscottl    /* start the command */
1188119665Sscottl    if (mlx_start(mc) != 0) {
1189119665Sscottl	mlx_releasecmd(mc);
1190119665Sscottl	free(mc->mc_data, M_DEVBUF);
1191119665Sscottl	mc->mc_data = NULL;
1192119665Sscottl    }
1193119665Sscottl
1194119665Sscottl}
1195119665Sscottl
119651973Smsmith/********************************************************************************
119751973Smsmith * Instigate a poll for one event log message on (sc).
119851973Smsmith * We only poll for one message at a time, to keep our command usage down.
119951973Smsmith */
120051973Smsmithstatic void
120151973Smsmithmlx_periodic_eventlog_poll(struct mlx_softc *sc)
120251973Smsmith{
120351973Smsmith    struct mlx_command	*mc;
120451973Smsmith    void		*result = NULL;
1205119665Sscottl    int			error = 0;
120651973Smsmith
120758188Smsmith    debug_called(1);
120851973Smsmith
120951973Smsmith    /* get ourselves a command buffer */
121051973Smsmith    error = 1;
121151973Smsmith    if ((mc = mlx_alloccmd(sc)) == NULL)
121251973Smsmith	goto out;
1213119665Sscottl
121451973Smsmith    /* allocate the response structure */
1215119665Sscottl    if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF,
1216119665Sscottl			 M_NOWAIT)) == NULL)
121751973Smsmith	goto out;
1218119665Sscottl
121951973Smsmith    /* get a command slot */
122051973Smsmith    if (mlx_getslot(mc))
122151973Smsmith	goto out;
122251973Smsmith
122351973Smsmith    /* map the command so the controller can see it */
122451973Smsmith    mc->mc_data = result;
122552276Smsmith    mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024;
1226119665Sscottl    error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
1227119665Sscottl			    mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT);
122851973Smsmith
122951973Smsmith out:
123052276Smsmith    if (error != 0) {
123152276Smsmith	if (mc != NULL)
123252276Smsmith	    mlx_releasecmd(mc);
1233119665Sscottl	if ((result != NULL) && (mc->mc_data != NULL))
123452276Smsmith	    free(result, M_DEVBUF);
123551973Smsmith    }
123651973Smsmith}
123751973Smsmith
123851973Smsmith/********************************************************************************
123951973Smsmith * Handle the result of polling for a log message, generate diagnostic output.
124051973Smsmith * If this wasn't the last message waiting for us, we'll go collect another.
124151973Smsmith */
124251973Smsmithstatic char *mlx_sense_messages[] = {
124351973Smsmith    "because write recovery failed",
124451973Smsmith    "because of SCSI bus reset failure",
124551973Smsmith    "because of double check condition",
124651973Smsmith    "because it was removed",
124751973Smsmith    "because of gross error on SCSI chip",
124851973Smsmith    "because of bad tag returned from drive",
124951973Smsmith    "because of timeout on SCSI command",
125051973Smsmith    "because of reset SCSI command issued from system",
125151973Smsmith    "because busy or parity error count exceeded limit",
125251973Smsmith    "because of 'kill drive' command from system",
125351973Smsmith    "because of selection timeout",
125451973Smsmith    "due to SCSI phase sequence error",
125551973Smsmith    "due to unknown status"
125651973Smsmith};
125751973Smsmith
125851973Smsmithstatic void
125951973Smsmithmlx_periodic_eventlog_respond(struct mlx_command *mc)
126051973Smsmith{
126151973Smsmith    struct mlx_softc		*sc = mc->mc_sc;
126251973Smsmith    struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
126351973Smsmith    char			*reason;
126451973Smsmith
126558188Smsmith    debug_called(1);
126651973Smsmith
126754419Smsmith    sc->mlx_lastevent++;		/* next message... */
126851973Smsmith    if (mc->mc_status == 0) {
126951973Smsmith
127051973Smsmith	/* handle event log message */
127151973Smsmith	switch(el->el_type) {
127251973Smsmith	    /*
127351973Smsmith	     * This is the only sort of message we understand at the moment.
127451973Smsmith	     * The tests here are probably incomplete.
127551973Smsmith	     */
127651973Smsmith	case MLX_LOGMSG_SENSE:	/* sense data */
127751973Smsmith	    /* Mylex vendor-specific message indicating a drive was killed? */
127851973Smsmith	    if ((el->el_sensekey == 9) &&
127951973Smsmith		(el->el_asc == 0x80)) {
128051973Smsmith		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
128151973Smsmith		    reason = mlx_sense_messages[el->el_asq];
128251973Smsmith		} else {
128351973Smsmith		    reason = "for unknown reason";
128451973Smsmith		}
128551973Smsmith		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
128651973Smsmith			      el->el_channel, el->el_target, reason);
128751973Smsmith	    }
128851973Smsmith	    /* SCSI drive was reset? */
128951973Smsmith	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
129051973Smsmith		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
129151973Smsmith			      el->el_channel, el->el_target);
129251973Smsmith	    }
129351973Smsmith	    /* SCSI drive error? */
129451973Smsmith	    if (!((el->el_sensekey == 0) ||
129551973Smsmith		  ((el->el_sensekey == 2) &&
129651973Smsmith		   (el->el_asc == 0x04) &&
129751973Smsmith		   ((el->el_asq == 0x01) ||
129851973Smsmith		    (el->el_asq == 0x02))))) {
129951973Smsmith		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
130051973Smsmith			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
130151973Smsmith		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
130251973Smsmith	    }
130351973Smsmith	    break;
130451973Smsmith
130551973Smsmith	default:
130651973Smsmith	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
130751973Smsmith	    break;
130851973Smsmith	}
130951973Smsmith    } else {
131051973Smsmith	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
131159447Smsmith	/* give up on all the outstanding messages, as we may have come unsynched */
131259447Smsmith	sc->mlx_lastevent = sc->mlx_currevent;
131351973Smsmith    }
131451973Smsmith
131551973Smsmith    /* dispose of command and data */
131651973Smsmith    free(mc->mc_data, M_DEVBUF);
131751973Smsmith    mlx_releasecmd(mc);
131851973Smsmith
131951973Smsmith    /* is there another message to obtain? */
132059447Smsmith    if (sc->mlx_lastevent != sc->mlx_currevent) {
132151973Smsmith	mlx_periodic_eventlog_poll(sc);
132259447Smsmith    } else {
132359447Smsmith	/* clear log-busy status */
132459447Smsmith	atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY);
132559447Smsmith    }
132651973Smsmith}
132751973Smsmith
132851973Smsmith/********************************************************************************
132959136Smsmith * Handle check/rebuild operations in progress.
133051973Smsmith */
133151973Smsmithstatic void
133251973Smsmithmlx_periodic_rebuild(struct mlx_command *mc)
133351973Smsmith{
133451973Smsmith    struct mlx_softc		*sc = mc->mc_sc;
133559136Smsmith    struct mlx_rebuild_status	*mr = (struct mlx_rebuild_status *)mc->mc_data;
133651973Smsmith
133751973Smsmith    switch(mc->mc_status) {
133859136Smsmith    case 0:				/* operation running, update stats */
133959136Smsmith	sc->mlx_rebuildstat = *mr;
134059136Smsmith
134159136Smsmith	/* spontaneous rebuild/check? */
134259136Smsmith	if (sc->mlx_background == 0) {
134359136Smsmith	    sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
134459136Smsmith	    device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
134559136Smsmith	}
134651973Smsmith	break;
134751973Smsmith
134859136Smsmith    case 0x0105:			/* nothing running, finalise stats and report */
134959136Smsmith	switch(sc->mlx_background) {
135059136Smsmith	case MLX_BACKGROUND_CHECK:
135159136Smsmith	    device_printf(sc->mlx_dev, "consistency check completed\n");	/* XXX print drive? */
135259136Smsmith	    break;
135359136Smsmith	case MLX_BACKGROUND_REBUILD:
135459136Smsmith	    device_printf(sc->mlx_dev, "drive rebuild completed\n");	/* XXX print channel/target? */
135559136Smsmith	    break;
135659136Smsmith	case MLX_BACKGROUND_SPONTANEOUS:
135759136Smsmith	default:
135859136Smsmith	    /* if we have previously been non-idle, report the transition */
135959136Smsmith	    if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
136059136Smsmith		device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
136159136Smsmith	    }
136251973Smsmith	}
136359136Smsmith	sc->mlx_background = 0;
136459136Smsmith	sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
136551973Smsmith	break;
136651973Smsmith    }
136751973Smsmith    free(mc->mc_data, M_DEVBUF);
136851973Smsmith    mlx_releasecmd(mc);
136951973Smsmith}
137051973Smsmith
137151973Smsmith/********************************************************************************
137251973Smsmith ********************************************************************************
137351973Smsmith                                                                    Channel Pause
137451973Smsmith ********************************************************************************
137551973Smsmith ********************************************************************************/
137651973Smsmith
137751973Smsmith/********************************************************************************
137851973Smsmith * It's time to perform a channel pause action for (sc), either start or stop
137951973Smsmith * the pause.
138051973Smsmith */
138151973Smsmithstatic void
138251973Smsmithmlx_pause_action(struct mlx_softc *sc)
138351973Smsmith{
138451973Smsmith    struct mlx_command	*mc;
138551973Smsmith    int			failsafe, i, command;
138651973Smsmith
138751973Smsmith    /* What are we doing here? */
138851973Smsmith    if (sc->mlx_pause.mp_when == 0) {
138951973Smsmith	command = MLX_CMD_STARTCHANNEL;
139051973Smsmith	failsafe = 0;
139151973Smsmith
139251973Smsmith    } else {
139351973Smsmith	command = MLX_CMD_STOPCHANNEL;
139451973Smsmith
139551973Smsmith	/*
139651973Smsmith	 * Channels will always start again after the failsafe period,
139751973Smsmith	 * which is specified in multiples of 30 seconds.
139851973Smsmith	 * This constrains us to a maximum pause of 450 seconds.
139951973Smsmith	 */
140051973Smsmith	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
140151973Smsmith	if (failsafe > 0xf) {
140251973Smsmith	    failsafe = 0xf;
140351973Smsmith	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
140451973Smsmith	}
140551973Smsmith    }
140651973Smsmith
140751973Smsmith    /* build commands for every channel requested */
140854979Smsmith    for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
140951973Smsmith	if ((1 << i) & sc->mlx_pause.mp_which) {
141051973Smsmith
141151973Smsmith	    /* get ourselves a command buffer */
141251973Smsmith	    if ((mc = mlx_alloccmd(sc)) == NULL)
141351973Smsmith		goto fail;
141451973Smsmith	    /* get a command slot */
141551973Smsmith	    mc->mc_flags |= MLX_CMD_PRIORITY;
141651973Smsmith	    if (mlx_getslot(mc))
141751973Smsmith		goto fail;
141851973Smsmith
141951973Smsmith	    /* build the command */
142051973Smsmith	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
142151973Smsmith	    mc->mc_complete = mlx_pause_done;
142251973Smsmith	    mc->mc_private = sc;		/* XXX not needed */
142351973Smsmith	    if (mlx_start(mc))
142451973Smsmith		goto fail;
142551973Smsmith	    /* command submitted OK */
142651973Smsmith	    return;
142751973Smsmith
142851973Smsmith	fail:
142951973Smsmith	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
143051973Smsmith			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
143151973Smsmith	    if (mc != NULL)
143251973Smsmith		mlx_releasecmd(mc);
143351973Smsmith	}
143451973Smsmith    }
143551973Smsmith}
143651973Smsmith
143751973Smsmithstatic void
143851973Smsmithmlx_pause_done(struct mlx_command *mc)
143951973Smsmith{
144051973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
144151973Smsmith    int			command = mc->mc_mailbox[0];
144251973Smsmith    int			channel = mc->mc_mailbox[2] & 0xf;
144351973Smsmith
144451973Smsmith    if (mc->mc_status != 0) {
144551973Smsmith	device_printf(sc->mlx_dev, "%s command failed - %s\n",
144651973Smsmith		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
144751973Smsmith    } else if (command == MLX_CMD_STOPCHANNEL) {
144851973Smsmith	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
144954515Speter		      channel, (long)(sc->mlx_pause.mp_howlong - time_second));
145051973Smsmith    } else {
145151973Smsmith	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
145251973Smsmith    }
145351973Smsmith    mlx_releasecmd(mc);
145451973Smsmith}
145551973Smsmith
145651973Smsmith/********************************************************************************
145751973Smsmith ********************************************************************************
145851973Smsmith                                                               Command Submission
145951973Smsmith ********************************************************************************
146051973Smsmith ********************************************************************************/
146151973Smsmith
1462119665Sscottlstatic void
1463119665Sscottlmlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
1464119665Sscottl{
1465119665Sscottl    struct mlx_softc *sc;
1466119665Sscottl    struct mlx_command *mc;
1467119665Sscottl
1468119665Sscottl    mc = (struct mlx_command *)arg;
1469119665Sscottl    if (error)
1470119665Sscottl	return;
1471119665Sscottl
1472119665Sscottl    mlx_setup_dmamap(mc, segs, nsegments, error);
1473119665Sscottl
1474119665Sscottl    /* build an enquiry command */
1475119665Sscottl    sc = mc->mc_sc;
1476119665Sscottl    mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
1477119665Sscottl
1478119665Sscottl    /* do we want a completion callback? */
1479119665Sscottl    if (mc->mc_complete != NULL) {
1480119665Sscottl	if ((error = mlx_start(mc)) != 0)
1481119665Sscottl	    return;
1482119665Sscottl    } else {
1483119665Sscottl	/* run the command in either polled or wait mode */
1484119665Sscottl	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) :
1485119665Sscottl						mlx_poll_command(mc))
1486119665Sscottl	    return;
1487119665Sscottl
1488119665Sscottl	/* command completed OK? */
1489119665Sscottl	if (mc->mc_status != 0) {
1490119665Sscottl	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n",
1491119665Sscottl			  mlx_diagnose_command(mc));
1492119665Sscottl	    return;
1493119665Sscottl	}
1494119665Sscottl    }
1495119665Sscottl}
1496119665Sscottl
149751973Smsmith/********************************************************************************
149851973Smsmith * Perform an Enquiry command using a type-3 command buffer and a return a single
149951973Smsmith * linear result buffer.  If the completion function is specified, it will
150051973Smsmith * be called with the completed command (and the result response will not be
150151973Smsmith * valid until that point).  Otherwise, the command will either be busy-waited
150251973Smsmith * for (interrupts not enabled), or slept for.
150351973Smsmith */
150451973Smsmithstatic void *
150551973Smsmithmlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
150651973Smsmith{
150751973Smsmith    struct mlx_command	*mc;
150851973Smsmith    void		*result;
150951973Smsmith    int			error;
151051973Smsmith
151158188Smsmith    debug_called(1);
151251973Smsmith
151351973Smsmith    /* get ourselves a command buffer */
151451973Smsmith    error = 1;
151551973Smsmith    result = NULL;
151651973Smsmith    if ((mc = mlx_alloccmd(sc)) == NULL)
151751973Smsmith	goto out;
151851973Smsmith    /* allocate the response structure */
151951973Smsmith    if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
152051973Smsmith	goto out;
152151973Smsmith    /* get a command slot */
152251973Smsmith    mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
152351973Smsmith    if (mlx_getslot(mc))
152451973Smsmith	goto out;
152551973Smsmith
152651973Smsmith    /* map the command so the controller can see it */
152751973Smsmith    mc->mc_data = result;
152851973Smsmith    mc->mc_length = bufsize;
1529119665Sscottl    mc->mc_command = command;
153051973Smsmith
153151973Smsmith    if (complete != NULL) {
153251973Smsmith	mc->mc_complete = complete;
153351973Smsmith	mc->mc_private = mc;
153451973Smsmith    }
1535119665Sscottl
1536119665Sscottl    error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
1537119665Sscottl			    mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT);
1538119665Sscottl
153951973Smsmith out:
154051973Smsmith    /* we got a command, but nobody else will free it */
1541144434Ssam    if ((mc != NULL) && (mc->mc_complete == NULL))
154251973Smsmith	mlx_releasecmd(mc);
154352276Smsmith    /* we got an error, and we allocated a result */
1544126461Sscottl    if ((error != 0) && (result != NULL)) {
1545126461Sscottl	free(result, M_DEVBUF);
1546126463Sscottl	result = NULL;
154751973Smsmith    }
154851973Smsmith    return(result);
154951973Smsmith}
155051973Smsmith
155151973Smsmith
155251973Smsmith/********************************************************************************
155351973Smsmith * Perform a Flush command on the nominated controller.
155451973Smsmith *
155551973Smsmith * May be called with interrupts enabled or disabled; will not return until
155651973Smsmith * the flush operation completes or fails.
155751973Smsmith */
155851973Smsmithstatic int
155951973Smsmithmlx_flush(struct mlx_softc *sc)
156051973Smsmith{
156151973Smsmith    struct mlx_command	*mc;
156251973Smsmith    int			error;
156351973Smsmith
156458188Smsmith    debug_called(1);
156551973Smsmith
156651973Smsmith    /* get ourselves a command buffer */
156751973Smsmith    error = 1;
156851973Smsmith    if ((mc = mlx_alloccmd(sc)) == NULL)
156951973Smsmith	goto out;
157051973Smsmith    /* get a command slot */
157151973Smsmith    if (mlx_getslot(mc))
157251973Smsmith	goto out;
157351973Smsmith
157451973Smsmith    /* build a flush command */
157551973Smsmith    mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
157651973Smsmith
157754419Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
157854419Smsmith    if (mlx_poll_command(mc))
157951973Smsmith	goto out;
158051973Smsmith
158151973Smsmith    /* command completed OK? */
158251973Smsmith    if (mc->mc_status != 0) {
158351973Smsmith	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
158451973Smsmith	goto out;
158551973Smsmith    }
158651973Smsmith
158751973Smsmith    error = 0;			/* success */
158851973Smsmith out:
158951973Smsmith    if (mc != NULL)
159051973Smsmith	mlx_releasecmd(mc);
159151973Smsmith    return(error);
159251973Smsmith}
159351973Smsmith
159451973Smsmith/********************************************************************************
159559136Smsmith * Start a background consistency check on (drive).
159651973Smsmith *
159751973Smsmith * May be called with interrupts enabled or disabled; will return as soon as the
159851973Smsmith * operation has started or been refused.
159951973Smsmith */
160051973Smsmithstatic int
160159136Smsmithmlx_check(struct mlx_softc *sc, int drive)
160259136Smsmith{
160359136Smsmith    struct mlx_command	*mc;
160459136Smsmith    int			error;
160559136Smsmith
160659136Smsmith    debug_called(1);
160759136Smsmith
160859136Smsmith    /* get ourselves a command buffer */
160959136Smsmith    error = 0x10000;
161059136Smsmith    if ((mc = mlx_alloccmd(sc)) == NULL)
161159136Smsmith	goto out;
161259136Smsmith    /* get a command slot */
161359136Smsmith    if (mlx_getslot(mc))
161459136Smsmith	goto out;
161559136Smsmith
161659136Smsmith    /* build a checkasync command, set the "fix it" flag */
161759136Smsmith    mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
161859136Smsmith
161959136Smsmith    /* start the command and wait for it to be returned */
162059136Smsmith    if (mlx_wait_command(mc))
162159136Smsmith	goto out;
162259136Smsmith
162359136Smsmith    /* command completed OK? */
162459136Smsmith    if (mc->mc_status != 0) {
162559136Smsmith	device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
162659136Smsmith    } else {
162759136Smsmith	device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
162859136Smsmith    }
162959136Smsmith    error = mc->mc_status;
163059136Smsmith
163159136Smsmith out:
163259136Smsmith    if (mc != NULL)
163359136Smsmith	mlx_releasecmd(mc);
163459136Smsmith    return(error);
163559136Smsmith}
163659136Smsmith
163759136Smsmith/********************************************************************************
163859136Smsmith * Start a background rebuild of the physical drive at (channel),(target).
163959136Smsmith *
164059136Smsmith * May be called with interrupts enabled or disabled; will return as soon as the
164159136Smsmith * operation has started or been refused.
164259136Smsmith */
164359136Smsmithstatic int
164451973Smsmithmlx_rebuild(struct mlx_softc *sc, int channel, int target)
164551973Smsmith{
164651973Smsmith    struct mlx_command	*mc;
164751973Smsmith    int			error;
164851973Smsmith
164958188Smsmith    debug_called(1);
165051973Smsmith
165151973Smsmith    /* get ourselves a command buffer */
165251973Smsmith    error = 0x10000;
165351973Smsmith    if ((mc = mlx_alloccmd(sc)) == NULL)
165451973Smsmith	goto out;
165551973Smsmith    /* get a command slot */
165651973Smsmith    if (mlx_getslot(mc))
165751973Smsmith	goto out;
165851973Smsmith
165959136Smsmith    /* build a checkasync command, set the "fix it" flag */
166051973Smsmith    mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
166151973Smsmith
166259136Smsmith    /* start the command and wait for it to be returned */
166359136Smsmith    if (mlx_wait_command(mc))
166451973Smsmith	goto out;
166551973Smsmith
166651973Smsmith    /* command completed OK? */
166751973Smsmith    if (mc->mc_status != 0) {
166851973Smsmith	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
166951973Smsmith    } else {
167059136Smsmith	device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
167151973Smsmith    }
167251973Smsmith    error = mc->mc_status;
167351973Smsmith
167451973Smsmith out:
167551973Smsmith    if (mc != NULL)
167651973Smsmith	mlx_releasecmd(mc);
167751973Smsmith    return(error);
167851973Smsmith}
167951973Smsmith
168051973Smsmith/********************************************************************************
168151973Smsmith * Run the command (mc) and return when it completes.
168251973Smsmith *
168351973Smsmith * Interrupts need to be enabled; returns nonzero on error.
168451973Smsmith */
168551973Smsmithstatic int
168651973Smsmithmlx_wait_command(struct mlx_command *mc)
168751973Smsmith{
168851973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
168951973Smsmith    int			error, count;
169051973Smsmith
169158188Smsmith    debug_called(1);
169251973Smsmith
169351973Smsmith    mc->mc_complete = NULL;
169451973Smsmith    mc->mc_private = mc;		/* wake us when you're done */
169551973Smsmith    if ((error = mlx_start(mc)) != 0)
169651973Smsmith	return(error);
169751973Smsmith
169851973Smsmith    count = 0;
169951973Smsmith    /* XXX better timeout? */
170051973Smsmith    while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
170151973Smsmith	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
170251973Smsmith    }
170351973Smsmith
170451973Smsmith    if (mc->mc_status != 0) {
170558188Smsmith	device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
170651973Smsmith	return(EIO);
170751973Smsmith    }
170851973Smsmith    return(0);
170951973Smsmith}
171051973Smsmith
171151973Smsmith
171251973Smsmith/********************************************************************************
171351973Smsmith * Start the command (mc) and busy-wait for it to complete.
171451973Smsmith *
171558188Smsmith * Should only be used when interrupts can't be relied upon. Returns 0 on
171651973Smsmith * success, nonzero on error.
171751973Smsmith * Successfully completed commands are dequeued.
171851973Smsmith */
171951973Smsmithstatic int
172051973Smsmithmlx_poll_command(struct mlx_command *mc)
172151973Smsmith{
172251973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
172351973Smsmith    int			error, count, s;
172451973Smsmith
172558188Smsmith    debug_called(1);
172651973Smsmith
172751973Smsmith    mc->mc_complete = NULL;
172851973Smsmith    mc->mc_private = NULL;	/* we will poll for it */
172951973Smsmith    if ((error = mlx_start(mc)) != 0)
173051973Smsmith	return(error);
173151973Smsmith
173251973Smsmith    count = 0;
173351973Smsmith    do {
173451973Smsmith	/* poll for completion */
173551973Smsmith	mlx_done(mc->mc_sc);
173658188Smsmith
173758188Smsmith    } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
173851973Smsmith    if (mc->mc_status != MLX_STATUS_BUSY) {
173951973Smsmith	s = splbio();
174052544Smsmith	TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
174151973Smsmith	splx(s);
174251973Smsmith	return(0);
174351973Smsmith    }
174459136Smsmith    device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
174551973Smsmith    return(EIO);
174651973Smsmith}
174751973Smsmith
1748119665Sscottlvoid
1749119665Sscottlmlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
1750119665Sscottl{
1751119665Sscottl    struct mlx_command	*mc;
1752119665Sscottl    struct mlxd_softc	*mlxd;
1753119665Sscottl    struct mlx_softc	*sc;
1754119665Sscottl    mlx_bio		*bp;
1755119665Sscottl    int			blkcount;
1756119665Sscottl    int			driveno;
1757119665Sscottl    int			cmd;
1758119665Sscottl
1759119665Sscottl    mc = (struct mlx_command *)arg;
1760119665Sscottl    mlx_setup_dmamap(mc, segs, nsegments, error);
1761119665Sscottl
1762119665Sscottl    sc = mc->mc_sc;
1763119665Sscottl    bp = mc->mc_private;
1764119665Sscottl
1765119665Sscottl    if (MLX_BIO_IS_READ(bp)) {
1766119665Sscottl	mc->mc_flags |= MLX_CMD_DATAIN;
1767119665Sscottl	cmd = MLX_CMD_READSG;
1768119665Sscottl    } else {
1769119665Sscottl	mc->mc_flags |= MLX_CMD_DATAOUT;
1770119665Sscottl	cmd = MLX_CMD_WRITESG;
1771119665Sscottl    }
1772119665Sscottl
1773119665Sscottl    /* build a suitable I/O command (assumes 512-byte rounded transfers) */
1774119665Sscottl    mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp);
1775119665Sscottl    driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
1776119665Sscottl    blkcount = (MLX_BIO_LENGTH(bp) + MLX_BLKSIZE - 1) / MLX_BLKSIZE;
1777119665Sscottl
1778119665Sscottl    if ((MLX_BIO_LBA(bp) + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
1779119665Sscottl	device_printf(sc->mlx_dev,
1780119665Sscottl		      "I/O beyond end of unit (%lld,%d > %lu)\n",
1781119665Sscottl		      (long long)MLX_BIO_LBA(bp), blkcount,
1782119665Sscottl		      (u_long)sc->mlx_sysdrive[driveno].ms_size);
1783119665Sscottl
1784119665Sscottl    /*
1785119665Sscottl     * Build the I/O command.  Note that the SG list type bits are set to zero,
1786119665Sscottl     * denoting the format of SG list that we are using.
1787119665Sscottl     */
1788119665Sscottl    if (sc->mlx_iftype == MLX_IFTYPE_2) {
1789119665Sscottl	mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD :
1790119665Sscottl						      MLX_CMD_READSG_OLD,
1791119665Sscottl		       blkcount & 0xff, 	/* xfer length low byte */
1792119665Sscottl		       MLX_BIO_LBA(bp),		/* physical block number */
1793119665Sscottl		       driveno,			/* target drive number */
1794119665Sscottl		       mc->mc_sgphys,		/* location of SG list */
1795119665Sscottl		       mc->mc_nsgent & 0x3f);	/* size of SG list */
1796119665Sscottl	} else {
1797119665Sscottl	mlx_make_type5(mc, cmd,
1798119665Sscottl		       blkcount & 0xff, 	/* xfer length low byte */
1799119665Sscottl		       (driveno << 3) | ((blkcount >> 8) & 0x07),
1800119665Sscottl						/* target+length high 3 bits */
1801119665Sscottl		       MLX_BIO_LBA(bp),		/* physical block number */
1802119665Sscottl		       mc->mc_sgphys,		/* location of SG list */
1803119665Sscottl		       mc->mc_nsgent & 0x3f);	/* size of SG list */
1804119665Sscottl    }
1805119665Sscottl
1806119665Sscottl    /* try to give command to controller */
1807119665Sscottl    if (mlx_start(mc) != 0) {
1808119665Sscottl	/* fail the command */
1809119665Sscottl	mc->mc_status = MLX_STATUS_WEDGED;
1810119665Sscottl	mlx_completeio(mc);
1811119665Sscottl    }
1812119665Sscottl}
1813119665Sscottl
181451973Smsmith/********************************************************************************
181551973Smsmith * Pull as much work off the softc's work queue as possible and give it to the
181651973Smsmith * controller.  Leave a couple of slots free for emergencies.
181751973Smsmith *
181851973Smsmith * Must be called at splbio or in an equivalent fashion that prevents
181959249Sphk * reentry or activity on the bioq.
182051973Smsmith */
182151973Smsmithstatic void
182251973Smsmithmlx_startio(struct mlx_softc *sc)
182351973Smsmith{
182451973Smsmith    struct mlx_command	*mc;
182578752Smsmith    mlx_bio		*bp;
182652544Smsmith    int			s;
1827119665Sscottl    int			error;
182851973Smsmith
182954419Smsmith    /* avoid reentrancy */
183054419Smsmith    if (mlx_lock_tas(sc, MLX_LOCK_STARTING))
183154419Smsmith	return;
183254419Smsmith
183351973Smsmith    /* spin until something prevents us from doing any work */
183452544Smsmith    s = splbio();
183551973Smsmith    for (;;) {
183651973Smsmith
183751973Smsmith	/* see if there's work to be done */
183878752Smsmith	if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL)
183951973Smsmith	    break;
184051973Smsmith	/* get a command */
184151973Smsmith	if ((mc = mlx_alloccmd(sc)) == NULL)
184251973Smsmith	    break;
184351973Smsmith	/* get a slot for the command */
184451973Smsmith	if (mlx_getslot(mc) != 0) {
184551973Smsmith	    mlx_releasecmd(mc);
184651973Smsmith	    break;
184751973Smsmith	}
184851973Smsmith	/* get the buf containing our work */
184978752Smsmith	MLX_BIO_QREMOVE(sc->mlx_bioq, bp);
185051973Smsmith	sc->mlx_waitbufs--;
185152544Smsmith	splx(s);
185251973Smsmith
185351973Smsmith	/* connect the buf to the command */
185451973Smsmith	mc->mc_complete = mlx_completeio;
185551973Smsmith	mc->mc_private = bp;
185678752Smsmith	mc->mc_data = MLX_BIO_DATA(bp);
185778752Smsmith	mc->mc_length = MLX_BIO_LENGTH(bp);
185851973Smsmith
185951973Smsmith	/* map the command so the controller can work with it */
1860119665Sscottl	error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
1861119665Sscottl				mc->mc_length, mlx_startio_cb, mc, 0);
1862119665Sscottl	if (error == EINPROGRESS) {
1863119665Sscottl		break;
186458188Smsmith	}
186551973Smsmith
186652544Smsmith	s = splbio();
186751973Smsmith    }
186852544Smsmith    splx(s);
186954419Smsmith    mlx_lock_clr(sc, MLX_LOCK_STARTING);
187051973Smsmith}
187151973Smsmith
187251973Smsmith/********************************************************************************
187351973Smsmith * Handle completion of an I/O command.
187451973Smsmith */
187551973Smsmithstatic void
187651973Smsmithmlx_completeio(struct mlx_command *mc)
187751973Smsmith{
187851973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
187978752Smsmith    mlx_bio		*bp = (mlx_bio *)mc->mc_private;
188078752Smsmith    struct mlxd_softc	*mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp);
188151973Smsmith
188251973Smsmith    if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
188378752Smsmith	MLX_BIO_SET_ERROR(bp, EIO);
188451973Smsmith
188551973Smsmith	switch(mc->mc_status) {
188651973Smsmith	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
188751973Smsmith	    device_printf(mlxd->mlxd_dev, "drive offline\n");
188852225Smsmith	    /* should signal this with a return code */
188951973Smsmith	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
189051973Smsmith	    break;
189151973Smsmith
189251973Smsmith	default:				/* other I/O error */
189351973Smsmith	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
189451973Smsmith#if 0
189552439Smsmith	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_pblkno %d\n",
189678752Smsmith			  MLX_BIO_LENGTH(bp), MLX_BIO_LENGTH(bp) / MLX_BLKSIZE, MLX_BIO_LBA(bp));
189751973Smsmith	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
189851973Smsmith#endif
189951973Smsmith	    break;
190051973Smsmith	}
190151973Smsmith    }
190251973Smsmith    mlx_releasecmd(mc);
190351973Smsmith    mlxd_intr(bp);
190451973Smsmith}
190551973Smsmith
1906119665Sscottlvoid
1907119665Sscottlmlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
1908119665Sscottl{
1909119665Sscottl    struct mlx_usercommand *mu;
1910119665Sscottl    struct mlx_command *mc;
1911119665Sscottl    struct mlx_dcdb	*dcdb;
1912119665Sscottl
1913119665Sscottl    mc = (struct mlx_command *)arg;
1914119665Sscottl    if (error)
1915119665Sscottl	return;
1916119665Sscottl
1917119665Sscottl    mlx_setup_dmamap(mc, segs, nsegments, error);
1918119665Sscottl
1919119665Sscottl    mu = (struct mlx_usercommand *)mc->mc_private;
1920119665Sscottl    dcdb = NULL;
1921119665Sscottl
1922119665Sscottl    /*
1923119665Sscottl     * If this is a passthrough SCSI command, the DCDB is packed at the
1924119665Sscottl     * beginning of the data area.  Fix up the DCDB to point to the correct
1925119665Sscottl     * physical address and override any bufptr supplied by the caller since
1926119665Sscottl     * we know what it's meant to be.
1927119665Sscottl     */
1928119665Sscottl    if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
1929119665Sscottl	dcdb = (struct mlx_dcdb *)mc->mc_data;
1930119665Sscottl	dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
1931119665Sscottl	mu->mu_bufptr = 8;
1932119665Sscottl    }
1933119665Sscottl
1934119665Sscottl    /*
1935119665Sscottl     * If there's a data buffer, fix up the command's buffer pointer.
1936119665Sscottl     */
1937119665Sscottl    if (mu->mu_datasize > 0) {
1938119665Sscottl	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_dataphys        & 0xff;
1939119665Sscottl	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8)  & 0xff;
1940119665Sscottl	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
1941119665Sscottl	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
1942119665Sscottl    }
1943119665Sscottl    debug(0, "command fixup");
1944119665Sscottl
1945119665Sscottl    /* submit the command and wait */
1946119665Sscottl    if (mlx_wait_command(mc) != 0)
1947119665Sscottl	return;
1948119665Sscottl
1949119665Sscottl}
1950119665Sscottl
195151973Smsmith/********************************************************************************
195251973Smsmith * Take a command from user-space and try to run it.
195358188Smsmith *
195458188Smsmith * XXX Note that this can't perform very much in the way of error checking, and
195558188Smsmith *     as such, applications _must_ be considered trustworthy.
195658188Smsmith * XXX Commands using S/G for data are not supported.
195751973Smsmith */
195851973Smsmithstatic int
195951973Smsmithmlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
196051973Smsmith{
196151973Smsmith    struct mlx_command	*mc;
196251973Smsmith    void		*kbuf;
196351973Smsmith    int			error;
196451973Smsmith
196558188Smsmith    debug_called(0);
196658188Smsmith
196751973Smsmith    kbuf = NULL;
196851973Smsmith    mc = NULL;
196951973Smsmith    error = ENOMEM;
197058188Smsmith
197158188Smsmith    /* get ourselves a command and copy in from user space */
197258188Smsmith    if ((mc = mlx_alloccmd(sc)) == NULL)
1973144433Ssam	return(error);
197458188Smsmith    bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
197558188Smsmith    debug(0, "got command buffer");
197658188Smsmith
1977119665Sscottl    /*
1978119665Sscottl     * if we need a buffer for data transfer, allocate one and copy in its
1979119665Sscottl     * initial contents
1980119665Sscottl     */
198151973Smsmith    if (mu->mu_datasize > 0) {
1982195534Sscottl	if (mu->mu_datasize > MLX_MAXPHYS) {
1983144436Ssam	    error = EINVAL;
1984144436Ssam	    goto out;
1985144436Ssam	}
1986111119Simp	if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) ||
198758188Smsmith	    (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))
198851973Smsmith	    goto out;
198958188Smsmith	debug(0, "got kernel buffer");
199051973Smsmith    }
199151973Smsmith
199251973Smsmith    /* get a command slot */
199351973Smsmith    if (mlx_getslot(mc))
199451973Smsmith	goto out;
199558188Smsmith    debug(0, "got a slot");
199651973Smsmith
199751973Smsmith    if (mu->mu_datasize > 0) {
199858188Smsmith
199958188Smsmith	/* range check the pointer to physical buffer address */
2000119665Sscottl	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) -
2001119665Sscottl						     sizeof(u_int32_t)))) {
200258188Smsmith	    error = EINVAL;
200358188Smsmith	    goto out;
200458188Smsmith	}
200551973Smsmith    }
200651973Smsmith
2007119665Sscottl    /* map the command so the controller can see it */
2008119665Sscottl    mc->mc_data = kbuf;
2009119665Sscottl    mc->mc_length = mu->mu_datasize;
2010119665Sscottl    mc->mc_private = mu;
2011119665Sscottl    error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
2012119665Sscottl			    mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT);
201351973Smsmith
201451973Smsmith    /* copy out status and data */
201551973Smsmith    mu->mu_status = mc->mc_status;
2016119665Sscottl    if ((mu->mu_datasize > 0) &&
2017119665Sscottl	((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
201851973Smsmith	goto out;
2019119665Sscottl
202051973Smsmith    error = 0;
2021119665Sscottl
202251973Smsmith out:
202351973Smsmith    mlx_releasecmd(mc);
202451973Smsmith    if (kbuf != NULL)
202551973Smsmith	free(kbuf, M_DEVBUF);
202651973Smsmith    return(error);
202751973Smsmith}
202851973Smsmith
202951973Smsmith/********************************************************************************
203051973Smsmith ********************************************************************************
203151973Smsmith                                                        Command I/O to Controller
203251973Smsmith ********************************************************************************
203351973Smsmith ********************************************************************************/
203451973Smsmith
203551973Smsmith/********************************************************************************
203651973Smsmith * Find a free command slot for (mc).
203751973Smsmith *
203851973Smsmith * Don't hand out a slot to a normal-priority command unless there are at least
203951973Smsmith * 4 slots free for priority commands.
204051973Smsmith */
204151973Smsmithstatic int
204251973Smsmithmlx_getslot(struct mlx_command *mc)
204351973Smsmith{
204451973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
204560074Smsmith    int			s, slot, limit;
204651973Smsmith
204758188Smsmith    debug_called(1);
204851973Smsmith
204960074Smsmith    /*
205060074Smsmith     * Enforce slot-usage limit, if we have the required information.
205160074Smsmith     */
205260074Smsmith    if (sc->mlx_enq2 != NULL) {
205360074Smsmith	limit = sc->mlx_enq2->me_max_commands;
205460074Smsmith    } else {
205560074Smsmith	limit = 2;
205660074Smsmith    }
205760074Smsmith    if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4))
205851973Smsmith	return(EBUSY);
205951973Smsmith
206051973Smsmith    /*
206151973Smsmith     * Allocate an outstanding command slot
206251973Smsmith     *
206351973Smsmith     * XXX linear search is slow
206451973Smsmith     */
206551973Smsmith    s = splbio();
206660074Smsmith    for (slot = 0; slot < limit; slot++) {
206758188Smsmith	debug(2, "try slot %d", slot);
206851973Smsmith	if (sc->mlx_busycmd[slot] == NULL)
206951973Smsmith	    break;
207051973Smsmith    }
207160074Smsmith    if (slot < limit) {
207251973Smsmith	sc->mlx_busycmd[slot] = mc;
207351973Smsmith	sc->mlx_busycmds++;
207451973Smsmith    }
207551973Smsmith    splx(s);
207651973Smsmith
207751973Smsmith    /* out of slots? */
207860074Smsmith    if (slot >= limit)
207951973Smsmith	return(EBUSY);
208051973Smsmith
208158188Smsmith    debug(2, "got slot %d", slot);
208251973Smsmith    mc->mc_slot = slot;
208351973Smsmith    return(0);
208451973Smsmith}
208551973Smsmith
208651973Smsmith/********************************************************************************
208751973Smsmith * Map/unmap (mc)'s data in the controller's addressable space.
208851973Smsmith */
208951973Smsmithstatic void
2090119665Sscottlmlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments,
2091119665Sscottl		 int error)
209251973Smsmith{
209351973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
209451973Smsmith    struct mlx_sgentry	*sg;
209551973Smsmith    int			i;
209651973Smsmith
209758188Smsmith    debug_called(1);
209851973Smsmith
209960074Smsmith    /* XXX should be unnecessary */
210060074Smsmith    if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
2101119665Sscottl	panic("MLX: too many s/g segments (%d, max %d)", nsegments,
2102119665Sscottl	      sc->mlx_enq2->me_max_sg);
210360074Smsmith
210451973Smsmith    /* get base address of s/g table */
210560074Smsmith    sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
210651973Smsmith
210751973Smsmith    /* save s/g table information in command */
210851973Smsmith    mc->mc_nsgent = nsegments;
2109119665Sscottl    mc->mc_sgphys = sc->mlx_sgbusaddr +
2110119665Sscottl		   (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
211151973Smsmith    mc->mc_dataphys = segs[0].ds_addr;
211251973Smsmith
211351973Smsmith    /* populate s/g table */
211451973Smsmith    for (i = 0; i < nsegments; i++, sg++) {
211551973Smsmith	sg->sg_addr = segs[i].ds_addr;
211651973Smsmith	sg->sg_count = segs[i].ds_len;
211751973Smsmith    }
211851973Smsmith
2119119665Sscottl    /* Make sure the buffers are visible on the bus. */
2120119665Sscottl    if (mc->mc_flags & MLX_CMD_DATAIN)
2121119665Sscottl	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
2122119665Sscottl			BUS_DMASYNC_PREREAD);
2123119665Sscottl    if (mc->mc_flags & MLX_CMD_DATAOUT)
2124119665Sscottl	bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
2125119665Sscottl			BUS_DMASYNC_PREWRITE);
212651973Smsmith}
212751973Smsmith
212851973Smsmithstatic void
212951973Smsmithmlx_unmapcmd(struct mlx_command *mc)
213051973Smsmith{
213151973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
213251973Smsmith
213358188Smsmith    debug_called(1);
213451973Smsmith
213551973Smsmith    /* if the command involved data at all */
213651973Smsmith    if (mc->mc_data != NULL) {
213751973Smsmith
213851973Smsmith	if (mc->mc_flags & MLX_CMD_DATAIN)
213951973Smsmith	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
214051973Smsmith	if (mc->mc_flags & MLX_CMD_DATAOUT)
214151973Smsmith	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
214251973Smsmith
214351973Smsmith	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
214451973Smsmith    }
214551973Smsmith}
214651973Smsmith
214751973Smsmith/********************************************************************************
214854419Smsmith * Try to deliver (mc) to the controller.
214951973Smsmith *
215051973Smsmith * Can be called at any interrupt level, with or without interrupts enabled.
215151973Smsmith */
215251973Smsmithstatic int
215351973Smsmithmlx_start(struct mlx_command *mc)
215451973Smsmith{
215551973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
215654419Smsmith    int			i, s, done;
215751973Smsmith
215858188Smsmith    debug_called(1);
215951973Smsmith
216051973Smsmith    /* save the slot number as ident so we can handle this command when complete */
216151973Smsmith    mc->mc_mailbox[0x1] = mc->mc_slot;
216251973Smsmith
216352544Smsmith    /* mark the command as currently being processed */
216451973Smsmith    mc->mc_status = MLX_STATUS_BUSY;
216554419Smsmith
216654419Smsmith    /* set a default 60-second timeout  XXX tunable?  XXX not currently used */
216754419Smsmith    mc->mc_timeout = time_second + 60;
216851973Smsmith
216951973Smsmith    /* spin waiting for the mailbox */
217051973Smsmith    for (i = 100000, done = 0; (i > 0) && !done; i--) {
217151973Smsmith	s = splbio();
217252544Smsmith	if (sc->mlx_tryqueue(sc, mc)) {
217352544Smsmith	    done = 1;
217452544Smsmith	    /* move command to work queue */
217552544Smsmith	    TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
217652544Smsmith	}
217754419Smsmith	splx(s);	/* drop spl to allow completion interrupts */
217851973Smsmith    }
217951973Smsmith
218051973Smsmith    /* command is enqueued */
218151973Smsmith    if (done)
218251973Smsmith	return(0);
218351973Smsmith
218451973Smsmith    /*
218551973Smsmith     * We couldn't get the controller to take the command.  Revoke the slot
218651973Smsmith     * that the command was given and return it with a bad status.
218751973Smsmith     */
218851973Smsmith    sc->mlx_busycmd[mc->mc_slot] = NULL;
218951973Smsmith    device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
219051973Smsmith    mc->mc_status = MLX_STATUS_WEDGED;
219154419Smsmith    mlx_complete(sc);
219251973Smsmith    return(EIO);
219351973Smsmith}
219451973Smsmith
219551973Smsmith/********************************************************************************
219654419Smsmith * Poll the controller (sc) for completed commands.
219754419Smsmith * Update command status and free slots for reuse.  If any slots were freed,
219854419Smsmith * new commands may be posted.
219951973Smsmith *
220054419Smsmith * Returns nonzero if one or more commands were completed.
220151973Smsmith */
220251973Smsmithstatic int
220351973Smsmithmlx_done(struct mlx_softc *sc)
220451973Smsmith{
220551973Smsmith    struct mlx_command	*mc;
220654419Smsmith    int			s, result;
220751973Smsmith    u_int8_t		slot;
220851973Smsmith    u_int16_t		status;
220951973Smsmith
221058188Smsmith    debug_called(2);
221151973Smsmith
221254419Smsmith    result = 0;
221351973Smsmith
221454419Smsmith    /* loop collecting completed commands */
221552544Smsmith    s = splbio();
221654419Smsmith    for (;;) {
221754419Smsmith	/* poll for a completed command's identifier and status */
221854419Smsmith	if (sc->mlx_findcomplete(sc, &slot, &status)) {
221954419Smsmith	    result = 1;
222054419Smsmith	    mc = sc->mlx_busycmd[slot];			/* find command */
222154419Smsmith	    if (mc != NULL) {				/* paranoia */
222254419Smsmith		if (mc->mc_status == MLX_STATUS_BUSY) {
222354419Smsmith		    mc->mc_status = status;		/* save status */
222451973Smsmith
222554419Smsmith		    /* free slot for reuse */
222654419Smsmith		    sc->mlx_busycmd[slot] = NULL;
222754419Smsmith		    sc->mlx_busycmds--;
222854419Smsmith		} else {
222954419Smsmith		    device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
223054419Smsmith		}
223151973Smsmith	    } else {
223254419Smsmith		device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
223351973Smsmith	    }
223451973Smsmith	} else {
223554419Smsmith	    break;
223651973Smsmith	}
223751973Smsmith    }
223860074Smsmith    splx(s);
223951973Smsmith
224054419Smsmith    /* if we've completed any commands, try posting some more */
224154419Smsmith    if (result)
224254419Smsmith	mlx_startio(sc);
224354419Smsmith
224454419Smsmith    /* handle completion and timeouts */
224554419Smsmith    mlx_complete(sc);
224654419Smsmith
224754419Smsmith    return(result);
224851973Smsmith}
224951973Smsmith
225051973Smsmith/********************************************************************************
225154419Smsmith * Perform post-completion processing for commands on (sc).
225251973Smsmith */
225351973Smsmithstatic void
225451973Smsmithmlx_complete(struct mlx_softc *sc)
225551973Smsmith{
225651973Smsmith    struct mlx_command	*mc, *nc;
225751973Smsmith    int			s, count;
225851973Smsmith
225958188Smsmith    debug_called(2);
226051973Smsmith
226154419Smsmith    /* avoid reentrancy  XXX might want to signal and request a restart */
226254419Smsmith    if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING))
226354419Smsmith	return;
226454419Smsmith
226551973Smsmith    s = splbio();
226651973Smsmith    count = 0;
226754419Smsmith
226854419Smsmith    /* scan the list of busy/done commands */
226952544Smsmith    mc = TAILQ_FIRST(&sc->mlx_work);
227051973Smsmith    while (mc != NULL) {
227151973Smsmith	nc = TAILQ_NEXT(mc, mc_link);
227251973Smsmith
227354419Smsmith	/* Command has been completed in some fashion */
227452544Smsmith	if (mc->mc_status != MLX_STATUS_BUSY) {
227554419Smsmith
227654419Smsmith	    /* unmap the command's data buffer */
227754419Smsmith	    mlx_unmapcmd(mc);
227852544Smsmith	    /*
227954419Smsmith	     * Does the command have a completion handler?
228054419Smsmith	     */
228152544Smsmith	    if (mc->mc_complete != NULL) {
228252544Smsmith		/* remove from list and give to handler */
228352544Smsmith		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
228452544Smsmith		mc->mc_complete(mc);
228551973Smsmith
228652544Smsmith		/*
228752544Smsmith		 * Is there a sleeper waiting on this command?
228852544Smsmith		 */
228952544Smsmith	    } else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
229051973Smsmith
229152544Smsmith		/* remove from list and wake up sleeper */
229252544Smsmith		TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
229352544Smsmith		wakeup_one(mc->mc_private);
229451973Smsmith
229552544Smsmith		/*
229652544Smsmith		 * Leave the command for a caller that's polling for it.
229752544Smsmith		 */
229852544Smsmith	    } else {
229952544Smsmith	    }
230051973Smsmith	}
230151973Smsmith	mc = nc;
230251973Smsmith    }
230351973Smsmith    splx(s);
230451973Smsmith
230554419Smsmith    mlx_lock_clr(sc, MLX_LOCK_COMPLETING);
230651973Smsmith}
230751973Smsmith
230851973Smsmith/********************************************************************************
230951973Smsmith ********************************************************************************
231051973Smsmith                                                        Command Buffer Management
231151973Smsmith ********************************************************************************
231251973Smsmith ********************************************************************************/
231351973Smsmith
231451973Smsmith/********************************************************************************
231551973Smsmith * Get a new command buffer.
231651973Smsmith *
231751973Smsmith * This may return NULL in low-memory cases.
231851973Smsmith *
231951973Smsmith * Note that using malloc() is expensive (the command buffer is << 1 page) but
232051973Smsmith * necessary if we are to be a loadable module before the zone allocator is fixed.
232151973Smsmith *
232251973Smsmith * If possible, we recycle a command buffer that's been used before.
232351973Smsmith *
232451973Smsmith * XXX Note that command buffers are not cleaned out - it is the caller's
232551973Smsmith *     responsibility to ensure that all required fields are filled in before
232651973Smsmith *     using a buffer.
232751973Smsmith */
232851973Smsmithstatic struct mlx_command *
232951973Smsmithmlx_alloccmd(struct mlx_softc *sc)
233051973Smsmith{
233151973Smsmith    struct mlx_command	*mc;
233251973Smsmith    int			error;
233351973Smsmith    int			s;
233451973Smsmith
233558188Smsmith    debug_called(1);
233651973Smsmith
233751973Smsmith    s = splbio();
233851973Smsmith    if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
233951973Smsmith	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
234051973Smsmith    splx(s);
234151973Smsmith
234251973Smsmith    /* allocate a new command buffer? */
234351973Smsmith    if (mc == NULL) {
234468877Sdwmalone	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO);
234551973Smsmith	if (mc != NULL) {
234651973Smsmith	    mc->mc_sc = sc;
234751973Smsmith	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
234851973Smsmith	    if (error) {
234951973Smsmith		free(mc, M_DEVBUF);
235051973Smsmith		return(NULL);
235151973Smsmith	    }
235251973Smsmith	}
235351973Smsmith    }
235451973Smsmith    return(mc);
235551973Smsmith}
235651973Smsmith
235751973Smsmith/********************************************************************************
235851973Smsmith * Release a command buffer for recycling.
235951973Smsmith *
236051973Smsmith * XXX It might be a good idea to limit the number of commands we save for reuse
236151973Smsmith *     if it's shown that this list bloats out massively.
236251973Smsmith */
236351973Smsmithstatic void
236451973Smsmithmlx_releasecmd(struct mlx_command *mc)
236551973Smsmith{
236651973Smsmith    int		s;
236751973Smsmith
236858188Smsmith    debug_called(1);
236951973Smsmith
237051973Smsmith    s = splbio();
237151973Smsmith    TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
237251973Smsmith    splx(s);
237351973Smsmith}
237451973Smsmith
237551973Smsmith/********************************************************************************
237651973Smsmith * Permanently discard a command buffer.
237751973Smsmith */
237851973Smsmithstatic void
237951973Smsmithmlx_freecmd(struct mlx_command *mc)
238051973Smsmith{
238151973Smsmith    struct mlx_softc	*sc = mc->mc_sc;
238251973Smsmith
238358188Smsmith    debug_called(1);
238451973Smsmith    bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
238551973Smsmith    free(mc, M_DEVBUF);
238651973Smsmith}
238751973Smsmith
238851973Smsmith
238951973Smsmith/********************************************************************************
239051973Smsmith ********************************************************************************
239151973Smsmith                                                Type 3 interface accessor methods
239251973Smsmith ********************************************************************************
239351973Smsmith ********************************************************************************/
239451973Smsmith
239551973Smsmith/********************************************************************************
239651973Smsmith * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
239751973Smsmith * (the controller is not ready to take a command).
239851973Smsmith *
239951973Smsmith * Must be called at splbio or in a fashion that prevents reentry.
240051973Smsmith */
240151973Smsmithstatic int
240251973Smsmithmlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
240351973Smsmith{
240451973Smsmith    int		i;
240551973Smsmith
240658188Smsmith    debug_called(2);
240751973Smsmith
240851973Smsmith    /* ready for our command? */
240951973Smsmith    if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
241051973Smsmith	/* copy mailbox data to window */
241151973Smsmith	for (i = 0; i < 13; i++)
241251973Smsmith	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
241351973Smsmith
241451973Smsmith	/* post command */
241552225Smsmith	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
241651973Smsmith	return(1);
241751973Smsmith    }
241851973Smsmith    return(0);
241951973Smsmith}
242051973Smsmith
242151973Smsmith/********************************************************************************
242251973Smsmith * See if a command has been completed, if so acknowledge its completion
242351973Smsmith * and recover the slot number and status code.
242451973Smsmith *
242551973Smsmith * Must be called at splbio or in a fashion that prevents reentry.
242651973Smsmith */
242751973Smsmithstatic int
242851973Smsmithmlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
242951973Smsmith{
243051973Smsmith
243158188Smsmith    debug_called(2);
243251973Smsmith
243351973Smsmith    /* status available? */
243451973Smsmith    if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
243551973Smsmith	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
243651973Smsmith	*status = MLX_V3_GET_STATUS(sc);		/* get status */
243751973Smsmith
243851973Smsmith	/* acknowledge completion */
243952225Smsmith	MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
244052225Smsmith	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
244151973Smsmith	return(1);
244251973Smsmith    }
244351973Smsmith    return(0);
244451973Smsmith}
244551973Smsmith
244651973Smsmith/********************************************************************************
244751973Smsmith * Enable/disable interrupts as requested. (No acknowledge required)
244851973Smsmith *
244951973Smsmith * Must be called at splbio or in a fashion that prevents reentry.
245051973Smsmith */
245151973Smsmithstatic void
245251973Smsmithmlx_v3_intaction(struct mlx_softc *sc, int action)
245351973Smsmith{
245458188Smsmith    debug_called(1);
245551973Smsmith
245651973Smsmith    switch(action) {
245751973Smsmith    case MLX_INTACTION_DISABLE:
245851973Smsmith	MLX_V3_PUT_IER(sc, 0);
245951973Smsmith	sc->mlx_state &= ~MLX_STATE_INTEN;
246051973Smsmith	break;
246151973Smsmith    case MLX_INTACTION_ENABLE:
246251973Smsmith	MLX_V3_PUT_IER(sc, 1);
246351973Smsmith	sc->mlx_state |= MLX_STATE_INTEN;
246451973Smsmith	break;
246551973Smsmith    }
246651973Smsmith}
246751973Smsmith
246858188Smsmith/********************************************************************************
246958188Smsmith * Poll for firmware error codes during controller initialisation.
247058188Smsmith * Returns 0 if initialisation is complete, 1 if still in progress but no
247158188Smsmith * error has been fetched, 2 if an error has been retrieved.
247258188Smsmith */
247358188Smsmithstatic int
247458188Smsmithmlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
247558188Smsmith{
247658188Smsmith    u_int8_t	fwerror;
247758188Smsmith    static int	initted = 0;
247851973Smsmith
247958188Smsmith    debug_called(2);
248058188Smsmith
248158188Smsmith    /* first time around, clear any hardware completion status */
248258188Smsmith    if (!initted) {
248358188Smsmith	MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
248458188Smsmith	DELAY(1000);
248558188Smsmith	initted = 1;
248658188Smsmith    }
248758188Smsmith
248858188Smsmith    /* init in progress? */
248958188Smsmith    if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
249058188Smsmith	return(0);
249158188Smsmith
249258188Smsmith    /* test error value */
249358188Smsmith    fwerror = MLX_V3_GET_FWERROR(sc);
249458188Smsmith    if (!(fwerror & MLX_V3_FWERROR_PEND))
249558188Smsmith	return(1);
249658188Smsmith
249758188Smsmith    /* mask status pending bit, fetch status */
249858188Smsmith    *error = fwerror & ~MLX_V3_FWERROR_PEND;
249958188Smsmith    *param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
250058188Smsmith    *param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
250158188Smsmith
250258188Smsmith    /* acknowledge */
250358188Smsmith    MLX_V3_PUT_FWERROR(sc, 0);
250458188Smsmith
250558188Smsmith    return(2);
250658188Smsmith}
250758188Smsmith
250851973Smsmith/********************************************************************************
250951973Smsmith ********************************************************************************
251052225Smsmith                                                Type 4 interface accessor methods
251152225Smsmith ********************************************************************************
251252225Smsmith ********************************************************************************/
251352225Smsmith
251452225Smsmith/********************************************************************************
251552225Smsmith * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
251652225Smsmith * (the controller is not ready to take a command).
251752225Smsmith *
251852225Smsmith * Must be called at splbio or in a fashion that prevents reentry.
251952225Smsmith */
252052225Smsmithstatic int
252152225Smsmithmlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
252252225Smsmith{
252352225Smsmith    int		i;
252452225Smsmith
252558188Smsmith    debug_called(2);
252652225Smsmith
252752225Smsmith    /* ready for our command? */
252852225Smsmith    if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
252952225Smsmith	/* copy mailbox data to window */
253052225Smsmith	for (i = 0; i < 13; i++)
253152225Smsmith	    MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
253252225Smsmith
253358188Smsmith	/* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */
253458188Smsmith	bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
253558188Smsmith			  BUS_SPACE_BARRIER_WRITE);
253658188Smsmith
253752225Smsmith	/* post command */
253852225Smsmith	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
253952225Smsmith	return(1);
254052225Smsmith    }
254152225Smsmith    return(0);
254252225Smsmith}
254352225Smsmith
254452225Smsmith/********************************************************************************
254552225Smsmith * See if a command has been completed, if so acknowledge its completion
254652225Smsmith * and recover the slot number and status code.
254752225Smsmith *
254852225Smsmith * Must be called at splbio or in a fashion that prevents reentry.
254952225Smsmith */
255052225Smsmithstatic int
255152225Smsmithmlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
255252225Smsmith{
255352225Smsmith
255458188Smsmith    debug_called(2);
255552225Smsmith
255652225Smsmith    /* status available? */
255752225Smsmith    if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
255852225Smsmith	*slot = MLX_V4_GET_STATUS_IDENT(sc);		/* get command identifier */
255952225Smsmith	*status = MLX_V4_GET_STATUS(sc);		/* get status */
256052225Smsmith
256152225Smsmith	/* acknowledge completion */
256252225Smsmith	MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
256352225Smsmith	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
256452225Smsmith	return(1);
256552225Smsmith    }
256652225Smsmith    return(0);
256752225Smsmith}
256852225Smsmith
256952225Smsmith/********************************************************************************
257052225Smsmith * Enable/disable interrupts as requested.
257152225Smsmith *
257252225Smsmith * Must be called at splbio or in a fashion that prevents reentry.
257352225Smsmith */
257452225Smsmithstatic void
257552225Smsmithmlx_v4_intaction(struct mlx_softc *sc, int action)
257652225Smsmith{
257758188Smsmith    debug_called(1);
257852225Smsmith
257952225Smsmith    switch(action) {
258052225Smsmith    case MLX_INTACTION_DISABLE:
258152225Smsmith	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
258252225Smsmith	sc->mlx_state &= ~MLX_STATE_INTEN;
258352225Smsmith	break;
258452225Smsmith    case MLX_INTACTION_ENABLE:
258552225Smsmith	MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
258652225Smsmith	sc->mlx_state |= MLX_STATE_INTEN;
258752225Smsmith	break;
258852225Smsmith    }
258952225Smsmith}
259052225Smsmith
259158188Smsmith/********************************************************************************
259258188Smsmith * Poll for firmware error codes during controller initialisation.
259358188Smsmith * Returns 0 if initialisation is complete, 1 if still in progress but no
259458188Smsmith * error has been fetched, 2 if an error has been retrieved.
259558188Smsmith */
259658188Smsmithstatic int
259758188Smsmithmlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
259858188Smsmith{
259958188Smsmith    u_int8_t	fwerror;
260058188Smsmith    static int	initted = 0;
260152225Smsmith
260258188Smsmith    debug_called(2);
260358188Smsmith
260458188Smsmith    /* first time around, clear any hardware completion status */
260558188Smsmith    if (!initted) {
260658188Smsmith	MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
260758188Smsmith	DELAY(1000);
260858188Smsmith	initted = 1;
260958188Smsmith    }
261058188Smsmith
261158188Smsmith    /* init in progress? */
261258188Smsmith    if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
261358188Smsmith	return(0);
261458188Smsmith
261558188Smsmith    /* test error value */
261658188Smsmith    fwerror = MLX_V4_GET_FWERROR(sc);
261758188Smsmith    if (!(fwerror & MLX_V4_FWERROR_PEND))
261858188Smsmith	return(1);
261958188Smsmith
262058188Smsmith    /* mask status pending bit, fetch status */
262158188Smsmith    *error = fwerror & ~MLX_V4_FWERROR_PEND;
262258188Smsmith    *param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
262358188Smsmith    *param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
262458188Smsmith
262558188Smsmith    /* acknowledge */
262658188Smsmith    MLX_V4_PUT_FWERROR(sc, 0);
262758188Smsmith
262858188Smsmith    return(2);
262958188Smsmith}
263058188Smsmith
263152225Smsmith/********************************************************************************
263252225Smsmith ********************************************************************************
263354419Smsmith                                                Type 5 interface accessor methods
263454419Smsmith ********************************************************************************
263554419Smsmith ********************************************************************************/
263654419Smsmith
263754419Smsmith/********************************************************************************
263854419Smsmith * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
263954419Smsmith * (the controller is not ready to take a command).
264054419Smsmith *
264154419Smsmith * Must be called at splbio or in a fashion that prevents reentry.
264254419Smsmith */
264354419Smsmithstatic int
264454419Smsmithmlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
264554419Smsmith{
264654419Smsmith    int		i;
264754419Smsmith
264858188Smsmith    debug_called(2);
264958188Smsmith
265054419Smsmith    /* ready for our command? */
265154419Smsmith    if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
265254419Smsmith	/* copy mailbox data to window */
265354419Smsmith	for (i = 0; i < 13; i++)
265454419Smsmith	    MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
265558188Smsmith
265654419Smsmith	/* post command */
265754419Smsmith	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
265854419Smsmith	return(1);
265954419Smsmith    }
266054419Smsmith    return(0);
266154419Smsmith}
266254419Smsmith
266354419Smsmith/********************************************************************************
266454419Smsmith * See if a command has been completed, if so acknowledge its completion
266554419Smsmith * and recover the slot number and status code.
266654419Smsmith *
266754419Smsmith * Must be called at splbio or in a fashion that prevents reentry.
266854419Smsmith */
266954419Smsmithstatic int
267054419Smsmithmlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
267154419Smsmith{
267254419Smsmith
267358188Smsmith    debug_called(2);
267454419Smsmith
267554419Smsmith    /* status available? */
267654419Smsmith    if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
267754419Smsmith	*slot = MLX_V5_GET_STATUS_IDENT(sc);		/* get command identifier */
267854419Smsmith	*status = MLX_V5_GET_STATUS(sc);		/* get status */
267954419Smsmith
268054419Smsmith	/* acknowledge completion */
268154419Smsmith	MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
268254419Smsmith	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
268354419Smsmith	return(1);
268454419Smsmith    }
268554419Smsmith    return(0);
268654419Smsmith}
268754419Smsmith
268854419Smsmith/********************************************************************************
268954419Smsmith * Enable/disable interrupts as requested.
269054419Smsmith *
269154419Smsmith * Must be called at splbio or in a fashion that prevents reentry.
269254419Smsmith */
269354419Smsmithstatic void
269454419Smsmithmlx_v5_intaction(struct mlx_softc *sc, int action)
269554419Smsmith{
269658188Smsmith    debug_called(1);
269754419Smsmith
269854419Smsmith    switch(action) {
269954419Smsmith    case MLX_INTACTION_DISABLE:
270058188Smsmith	MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
270154419Smsmith	sc->mlx_state &= ~MLX_STATE_INTEN;
270254419Smsmith	break;
270354419Smsmith    case MLX_INTACTION_ENABLE:
270458188Smsmith	MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
270554419Smsmith	sc->mlx_state |= MLX_STATE_INTEN;
270654419Smsmith	break;
270754419Smsmith    }
270854419Smsmith}
270954419Smsmith
271058188Smsmith/********************************************************************************
271158188Smsmith * Poll for firmware error codes during controller initialisation.
271258188Smsmith * Returns 0 if initialisation is complete, 1 if still in progress but no
271358188Smsmith * error has been fetched, 2 if an error has been retrieved.
271458188Smsmith */
271558188Smsmithstatic int
271658188Smsmithmlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2)
271758188Smsmith{
271858188Smsmith    u_int8_t	fwerror;
271958188Smsmith    static int	initted = 0;
272054419Smsmith
272158188Smsmith    debug_called(2);
272258188Smsmith
272358188Smsmith    /* first time around, clear any hardware completion status */
272458188Smsmith    if (!initted) {
272558188Smsmith	MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
272658188Smsmith	DELAY(1000);
272758188Smsmith	initted = 1;
272858188Smsmith    }
272958188Smsmith
273058188Smsmith    /* init in progress? */
273158188Smsmith    if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
273258188Smsmith	return(0);
273358188Smsmith
273458188Smsmith    /* test for error value */
273558188Smsmith    fwerror = MLX_V5_GET_FWERROR(sc);
273658188Smsmith    if (!(fwerror & MLX_V5_FWERROR_PEND))
273758188Smsmith	return(1);
273858188Smsmith
273958188Smsmith    /* mask status pending bit, fetch status */
274058188Smsmith    *error = fwerror & ~MLX_V5_FWERROR_PEND;
274158188Smsmith    *param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
274258188Smsmith    *param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
274358188Smsmith
274458188Smsmith    /* acknowledge */
274558188Smsmith    MLX_V5_PUT_FWERROR(sc, 0xff);
274658188Smsmith
274758188Smsmith    return(2);
274858188Smsmith}
274958188Smsmith
275054419Smsmith/********************************************************************************
275154419Smsmith ********************************************************************************
275251973Smsmith                                                                        Debugging
275351973Smsmith ********************************************************************************
275451973Smsmith ********************************************************************************/
275551973Smsmith
275651973Smsmith/********************************************************************************
275751973Smsmith * Return a status message describing (mc)
275851973Smsmith */
275951973Smsmithstatic char *mlx_status_messages[] = {
276051973Smsmith    "normal completion",			/* 00 */
276151973Smsmith    "irrecoverable data error",			/* 01 */
276251973Smsmith    "drive does not exist, or is offline",	/* 02 */
276351973Smsmith    "attempt to write beyond end of drive",	/* 03 */
276451973Smsmith    "bad data encountered",			/* 04 */
276551973Smsmith    "invalid log entry request",		/* 05 */
276651973Smsmith    "attempt to rebuild online drive",		/* 06 */
276751973Smsmith    "new disk failed during rebuild",		/* 07 */
276851973Smsmith    "invalid channel/target",			/* 08 */
276951973Smsmith    "rebuild/check already in progress",	/* 09 */
277051973Smsmith    "one or more disks are dead",		/* 10 */
277151973Smsmith    "invalid or non-redundant drive",		/* 11 */
277251973Smsmith    "channel is busy",				/* 12 */
277351973Smsmith    "channel is not stopped",			/* 13 */
277458188Smsmith    "rebuild successfully terminated",		/* 14 */
277558188Smsmith    "unsupported command",			/* 15 */
277658188Smsmith    "check condition received",			/* 16 */
277758188Smsmith    "device is busy",				/* 17 */
277858188Smsmith    "selection or command timeout",		/* 18 */
277958188Smsmith    "command terminated abnormally",		/* 19 */
278058188Smsmith    ""
278151973Smsmith};
278251973Smsmith
278351973Smsmithstatic struct
278451973Smsmith{
278551973Smsmith    int		command;
278651973Smsmith    u_int16_t	status;
278751973Smsmith    int		msg;
278851973Smsmith} mlx_messages[] = {
278958188Smsmith    {MLX_CMD_READSG,		0x0001,	 1},
279058188Smsmith    {MLX_CMD_READSG,		0x0002,	 1},
279158188Smsmith    {MLX_CMD_READSG,		0x0105,	 3},
279258188Smsmith    {MLX_CMD_READSG,		0x010c,	 4},
279358188Smsmith    {MLX_CMD_WRITESG,		0x0001,	 1},
279458188Smsmith    {MLX_CMD_WRITESG,		0x0002,	 1},
279558188Smsmith    {MLX_CMD_WRITESG,		0x0105,	 3},
279658188Smsmith    {MLX_CMD_READSG_OLD,	0x0001,	 1},
279758188Smsmith    {MLX_CMD_READSG_OLD,	0x0002,	 1},
279858188Smsmith    {MLX_CMD_READSG_OLD,	0x0105,	 3},
279958188Smsmith    {MLX_CMD_WRITESG_OLD,	0x0001,	 1},
280058188Smsmith    {MLX_CMD_WRITESG_OLD,	0x0002,	 1},
280158188Smsmith    {MLX_CMD_WRITESG_OLD,	0x0105,	 3},
280251973Smsmith    {MLX_CMD_LOGOP,		0x0105,	 5},
280351973Smsmith    {MLX_CMD_REBUILDASYNC,	0x0002,  6},
280451973Smsmith    {MLX_CMD_REBUILDASYNC,	0x0004,  7},
280551973Smsmith    {MLX_CMD_REBUILDASYNC,	0x0105,  8},
280651973Smsmith    {MLX_CMD_REBUILDASYNC,	0x0106,  9},
280758188Smsmith    {MLX_CMD_REBUILDASYNC,	0x0107, 14},
280851973Smsmith    {MLX_CMD_CHECKASYNC,	0x0002, 10},
280951973Smsmith    {MLX_CMD_CHECKASYNC,	0x0105, 11},
281051973Smsmith    {MLX_CMD_CHECKASYNC,	0x0106,  9},
281151973Smsmith    {MLX_CMD_STOPCHANNEL,	0x0106, 12},
281251973Smsmith    {MLX_CMD_STOPCHANNEL,	0x0105,  8},
281351973Smsmith    {MLX_CMD_STARTCHANNEL,	0x0005, 13},
281451973Smsmith    {MLX_CMD_STARTCHANNEL,	0x0105,  8},
281558188Smsmith    {MLX_CMD_DIRECT_CDB,	0x0002, 16},
281658188Smsmith    {MLX_CMD_DIRECT_CDB,	0x0008, 17},
281758188Smsmith    {MLX_CMD_DIRECT_CDB,	0x000e, 18},
281858188Smsmith    {MLX_CMD_DIRECT_CDB,	0x000f, 19},
281958188Smsmith    {MLX_CMD_DIRECT_CDB,	0x0105,  8},
282058188Smsmith
282158188Smsmith    {0,				0x0104, 14},
282251973Smsmith    {-1, 0, 0}
282351973Smsmith};
282451973Smsmith
282551973Smsmithstatic char *
282651973Smsmithmlx_diagnose_command(struct mlx_command *mc)
282751973Smsmith{
282851973Smsmith    static char	unkmsg[80];
282951973Smsmith    int		i;
283051973Smsmith
283151973Smsmith    /* look up message in table */
283251973Smsmith    for (i = 0; mlx_messages[i].command != -1; i++)
283358188Smsmith	if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
283452275Smsmith	    (mc->mc_status == mlx_messages[i].status))
283551973Smsmith	    return(mlx_status_messages[mlx_messages[i].msg]);
283651973Smsmith
283751973Smsmith    sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
283851973Smsmith    return(unkmsg);
283951973Smsmith}
284051973Smsmith
284151973Smsmith/*******************************************************************************
284258188Smsmith * Print a string describing the controller (sc)
284351973Smsmith */
284454419Smsmithstatic struct
284554419Smsmith{
284654419Smsmith    int		hwid;
284754419Smsmith    char	*name;
284854419Smsmith} mlx_controller_names[] = {
284954419Smsmith    {0x01,	"960P/PD"},
285054419Smsmith    {0x02,	"960PL"},
285154419Smsmith    {0x10,	"960PG"},
285254419Smsmith    {0x11,	"960PJ"},
285354979Smsmith    {0x12,	"960PR"},
285454979Smsmith    {0x13,	"960PT"},
285554979Smsmith    {0x14,	"960PTL0"},
285654979Smsmith    {0x15,	"960PRL"},
285754979Smsmith    {0x16,	"960PTL1"},
285854979Smsmith    {0x20,	"1164PVX"},
285954419Smsmith    {-1, NULL}
286054419Smsmith};
286154419Smsmith
286254979Smsmithstatic void
286354979Smsmithmlx_describe_controller(struct mlx_softc *sc)
286451973Smsmith{
286551973Smsmith    static char		buf[80];
286654419Smsmith    char		*model;
286754979Smsmith    int			i;
286854419Smsmith
286954419Smsmith    for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
287054979Smsmith	if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
287154419Smsmith	    model = mlx_controller_names[i].name;
287254419Smsmith	    break;
287354419Smsmith	}
287451973Smsmith    }
287554419Smsmith    if (model == NULL) {
287654979Smsmith	sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
287754419Smsmith	model = buf;
287854419Smsmith    }
287958188Smsmith    device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
288054979Smsmith		  model,
288154979Smsmith		  sc->mlx_enq2->me_actual_channels,
288254979Smsmith		  sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
288354979Smsmith		  sc->mlx_enq2->me_firmware_id & 0xff,
288454979Smsmith		  (sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
288555080Smsmith		  (sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
288655093Smsmith		  (sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
288754979Smsmith		  sc->mlx_enq2->me_mem_size / (1024 * 1024));
288854979Smsmith
288954979Smsmith    if (bootverbose) {
289054979Smsmith	device_printf(sc->mlx_dev, "  Hardware ID                 0x%08x\n", sc->mlx_enq2->me_hardware_id);
289154979Smsmith	device_printf(sc->mlx_dev, "  Firmware ID                 0x%08x\n", sc->mlx_enq2->me_firmware_id);
289254979Smsmith	device_printf(sc->mlx_dev, "  Configured/Actual channels  %d/%d\n", sc->mlx_enq2->me_configured_channels,
289354979Smsmith		      sc->mlx_enq2->me_actual_channels);
289454979Smsmith	device_printf(sc->mlx_dev, "  Max Targets                 %d\n", sc->mlx_enq2->me_max_targets);
289554979Smsmith	device_printf(sc->mlx_dev, "  Max Tags                    %d\n", sc->mlx_enq2->me_max_tags);
289654979Smsmith	device_printf(sc->mlx_dev, "  Max System Drives           %d\n", sc->mlx_enq2->me_max_sys_drives);
289754979Smsmith	device_printf(sc->mlx_dev, "  Max Arms                    %d\n", sc->mlx_enq2->me_max_arms);
289854979Smsmith	device_printf(sc->mlx_dev, "  Max Spans                   %d\n", sc->mlx_enq2->me_max_spans);
289954979Smsmith	device_printf(sc->mlx_dev, "  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
290054979Smsmith		      sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
290154979Smsmith	device_printf(sc->mlx_dev, "  DRAM type                   %d\n", sc->mlx_enq2->me_mem_type);
290254979Smsmith	device_printf(sc->mlx_dev, "  Clock Speed                 %dns\n", sc->mlx_enq2->me_clock_speed);
290354979Smsmith	device_printf(sc->mlx_dev, "  Hardware Speed              %dns\n", sc->mlx_enq2->me_hardware_speed);
290454979Smsmith	device_printf(sc->mlx_dev, "  Max Commands                %d\n", sc->mlx_enq2->me_max_commands);
290554979Smsmith	device_printf(sc->mlx_dev, "  Max SG Entries              %d\n", sc->mlx_enq2->me_max_sg);
290654979Smsmith	device_printf(sc->mlx_dev, "  Max DP                      %d\n", sc->mlx_enq2->me_max_dp);
290754979Smsmith	device_printf(sc->mlx_dev, "  Max IOD                     %d\n", sc->mlx_enq2->me_max_iod);
290854979Smsmith	device_printf(sc->mlx_dev, "  Max Comb                    %d\n", sc->mlx_enq2->me_max_comb);
290954979Smsmith	device_printf(sc->mlx_dev, "  Latency                     %ds\n", sc->mlx_enq2->me_latency);
291054979Smsmith	device_printf(sc->mlx_dev, "  SCSI Timeout                %ds\n", sc->mlx_enq2->me_scsi_timeout);
291154979Smsmith	device_printf(sc->mlx_dev, "  Min Free Lines              %d\n", sc->mlx_enq2->me_min_freelines);
291254979Smsmith	device_printf(sc->mlx_dev, "  Rate Constant               %d\n", sc->mlx_enq2->me_rate_const);
291354979Smsmith	device_printf(sc->mlx_dev, "  MAXBLK                      %d\n", sc->mlx_enq2->me_maxblk);
291454979Smsmith	device_printf(sc->mlx_dev, "  Blocking Factor             %d sectors\n", sc->mlx_enq2->me_blocking_factor);
291554979Smsmith	device_printf(sc->mlx_dev, "  Cache Line Size             %d blocks\n", sc->mlx_enq2->me_cacheline);
291654979Smsmith	device_printf(sc->mlx_dev, "  SCSI Capability             %s%dMHz, %d bit\n",
291754979Smsmith		      sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
291854979Smsmith		      (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
291954979Smsmith		      8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
292054979Smsmith	device_printf(sc->mlx_dev, "  Firmware Build Number       %d\n", sc->mlx_enq2->me_firmware_build);
292154979Smsmith	device_printf(sc->mlx_dev, "  Fault Management Type       %d\n", sc->mlx_enq2->me_fault_mgmt_type);
292254979Smsmith	device_printf(sc->mlx_dev, "  Features                    %b\n", sc->mlx_enq2->me_firmware_features,
292354979Smsmith		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
292454979Smsmith
292554979Smsmith    }
292651973Smsmith}
292751973Smsmith
292858188Smsmith/*******************************************************************************
292958188Smsmith * Emit a string describing the firmware handshake status code, and return a flag
293058188Smsmith * indicating whether the code represents a fatal error.
293158188Smsmith *
293258188Smsmith * Error code interpretations are from the Linux driver, and don't directly match
293358188Smsmith * the messages printed by Mylex's BIOS.  This may change if documentation on the
293458188Smsmith * codes is forthcoming.
293558188Smsmith */
293658188Smsmithstatic int
293758188Smsmithmlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
293858188Smsmith{
293958188Smsmith    switch(error) {
294058188Smsmith    case 0x00:
294158188Smsmith	device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
294258188Smsmith	break;
294358188Smsmith    case 0x08:
294458188Smsmith	/* we could be neater about this and give some indication when we receive more of them */
294558188Smsmith	if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
294658188Smsmith	    device_printf(sc->mlx_dev, "spinning up drives...\n");
294758188Smsmith	    sc->mlx_flags |= MLX_SPINUP_REPORTED;
294858188Smsmith	}
294958188Smsmith	break;
295058188Smsmith    case 0x30:
295158188Smsmith	device_printf(sc->mlx_dev, "configuration checksum error\n");
295258188Smsmith	break;
295358188Smsmith    case 0x60:
295458188Smsmith	device_printf(sc->mlx_dev, "mirror race recovery failed\n");
295558188Smsmith	break;
295658188Smsmith    case 0x70:
295758188Smsmith	device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
295858188Smsmith	break;
295958188Smsmith    case 0x90:
296058188Smsmith	device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
296158188Smsmith	break;
296258188Smsmith    case 0xa0:
296358188Smsmith	device_printf(sc->mlx_dev, "logical drive installation aborted\n");
296458188Smsmith	break;
296558188Smsmith    case 0xb0:
296658188Smsmith	device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
296758188Smsmith	break;
296858188Smsmith    case 0xd0:
296958188Smsmith	device_printf(sc->mlx_dev, "new controller configuration found\n");
297058188Smsmith	break;
297158188Smsmith    case 0xf0:
297258188Smsmith	device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
297358188Smsmith	return(1);
297458188Smsmith    default:
297558188Smsmith	device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
297658188Smsmith	break;
297758188Smsmith    }
297858188Smsmith    return(0);
297958188Smsmith}
298058188Smsmith
298151973Smsmith/********************************************************************************
298251973Smsmith ********************************************************************************
298351973Smsmith                                                                Utility Functions
298451973Smsmith ********************************************************************************
298551973Smsmith ********************************************************************************/
298651973Smsmith
298751973Smsmith/********************************************************************************
298851973Smsmith * Find the disk whose unit number is (unit) on this controller
298951973Smsmith */
299051973Smsmithstatic struct mlx_sysdrive *
299151973Smsmithmlx_findunit(struct mlx_softc *sc, int unit)
299251973Smsmith{
299351973Smsmith    int		i;
299451973Smsmith
299551973Smsmith    /* search system drives */
299651973Smsmith    for (i = 0; i < MLX_MAXDRIVES; i++) {
299751973Smsmith	/* is this one attached? */
299851973Smsmith	if (sc->mlx_sysdrive[i].ms_disk != 0) {
299951973Smsmith	    /* is this the one? */
300051973Smsmith	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
300151973Smsmith		return(&sc->mlx_sysdrive[i]);
300251973Smsmith	}
300351973Smsmith    }
300451973Smsmith    return(NULL);
300551973Smsmith}
3006