amr.c revision 52543
151974Smsmith/*-
251974Smsmith * Copyright (c) 1999 Michael Smith
351974Smsmith * All rights reserved.
451974Smsmith *
551974Smsmith * Redistribution and use in source and binary forms, with or without
651974Smsmith * modification, are permitted provided that the following conditions
751974Smsmith * are met:
851974Smsmith * 1. Redistributions of source code must retain the above copyright
951974Smsmith *    notice, this list of conditions and the following disclaimer.
1051974Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1151974Smsmith *    notice, this list of conditions and the following disclaimer in the
1251974Smsmith *    documentation and/or other materials provided with the distribution.
1351974Smsmith *
1451974Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1551974Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1651974Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1751974Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1851974Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1951974Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2051974Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2151974Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2251974Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2351974Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2451974Smsmith * SUCH DAMAGE.
2551974Smsmith *
2651974Smsmith *	$FreeBSD: head/sys/dev/amr/amr.c 52543 1999-10-26 23:18:57Z msmith $
2751974Smsmith */
2851974Smsmith
2951974Smsmith/*
3051974Smsmith * Driver for the AMI MegaRaid family of controllers
3151974Smsmith */
3251974Smsmith
3351974Smsmith#include <sys/param.h>
3451974Smsmith#include <sys/systm.h>
3551974Smsmith#include <sys/malloc.h>
3651974Smsmith#include <sys/kernel.h>
3751974Smsmith
3851974Smsmith#include <sys/buf.h>
3951974Smsmith#include <sys/bus.h>
4051974Smsmith#include <sys/conf.h>
4151974Smsmith#include <sys/devicestat.h>
4251974Smsmith#include <sys/disk.h>
4351974Smsmith
4451974Smsmith#include <machine/resource.h>
4551974Smsmith#include <machine/bus.h>
4651974Smsmith#include <machine/clock.h>
4751974Smsmith#include <sys/rman.h>
4851974Smsmith
4951974Smsmith#include <dev/amr/amrio.h>
5051974Smsmith#include <dev/amr/amrreg.h>
5151974Smsmith#include <dev/amr/amrvar.h>
5251974Smsmith
5351974Smsmith#if 0
5451974Smsmith#define debug(fmt, args...)	printf("%s: " fmt "\n", __FUNCTION__ , ##args)
5551974Smsmith#else
5651974Smsmith#define debug(fmt, args...)
5751974Smsmith#endif
5851974Smsmith
5951974Smsmith#define AMR_CDEV_MAJOR	132
6051974Smsmith
6151974Smsmithstatic struct cdevsw amr_cdevsw = {
6251974Smsmith		/* open */	amr_open,
6351974Smsmith		/* close */	amr_close,
6451974Smsmith		/* read */	noread,
6551974Smsmith		/* write */	nowrite,
6651974Smsmith		/* ioctl */	amr_ioctl,
6751974Smsmith		/* poll */	nopoll,
6851974Smsmith		/* mmap */	nommap,
6951974Smsmith		/* strategy */	nostrategy,
7051974Smsmith		/* name */ 	"amr",
7151974Smsmith		/* maj */	AMR_CDEV_MAJOR,
7251974Smsmith		/* dump */	nodump,
7351974Smsmith		/* psize */ 	nopsize,
7451974Smsmith		/* flags */	0,
7551974Smsmith		/* bmaj */	254	/* XXX magic no-bdev */
7651974Smsmith};
7751974Smsmith
7851974Smsmithstatic int	cdev_registered = 0;
7951974Smsmithdevclass_t	amr_devclass;
8051974Smsmith
8151974Smsmith/*
8251974Smsmith * Command wrappers
8351974Smsmith */
8451974Smsmithstatic int			amr_query_controller(struct amr_softc *sc);
8551974Smsmithstatic void			*amr_enquiry(struct amr_softc *sc, size_t bufsize,
8651974Smsmith					     u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual);
8751974Smsmithstatic int			amr_flush(struct amr_softc *sc);
8851974Smsmithstatic void			amr_startio(struct amr_softc *sc);
8951974Smsmithstatic void			amr_completeio(struct amr_command *ac);
9051974Smsmith
9151974Smsmith/*
9251974Smsmith * Command processing.
9351974Smsmith */
9451974Smsmithstatic int			amr_wait_command(struct amr_command *ac);
9551974Smsmithstatic int			amr_poll_command(struct amr_command *ac);
9651974Smsmithstatic int			amr_getslot(struct amr_command *ac);
9751974Smsmithstatic void			amr_mapcmd(struct amr_command *ac);
9851974Smsmithstatic void			amr_unmapcmd(struct amr_command *ac);
9951974Smsmithstatic int			amr_start(struct amr_command *ac);
10051974Smsmithstatic int			amr_done(struct amr_softc *sc);
10151974Smsmithstatic void			amr_complete(struct amr_softc *sc);
10251974Smsmith
10351974Smsmith/*
10451974Smsmith * Command buffer allocation.
10551974Smsmith */
10651974Smsmithstatic struct amr_command	*amr_alloccmd(struct amr_softc *sc);
10751974Smsmithstatic void			amr_releasecmd(struct amr_command *ac);
10851974Smsmithstatic void			amr_freecmd(struct amr_command *ac);
10951974Smsmith
11051974Smsmith/*
11151974Smsmith * Interface-specific shims
11251974Smsmith */
11351974Smsmithstatic void			amr_quartz_submit_command(struct amr_softc *sc);
11451974Smsmithstatic int			amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
11551974Smsmithstatic void			amr_quartz_attach_mailbox(struct amr_softc *sc);
11651974Smsmith
11751974Smsmithstatic void			amr_std_submit_command(struct amr_softc *sc);
11851974Smsmithstatic int			amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
11951974Smsmithstatic void			amr_std_attach_mailbox(struct amr_softc *sc);
12051974Smsmith
12151974Smsmith/*
12251974Smsmith * Debugging
12351974Smsmith */
12451974Smsmithstatic void			amr_printcommand(struct amr_command *ac);
12551974Smsmith
12651974Smsmith/********************************************************************************
12751974Smsmith ********************************************************************************
12851974Smsmith                                                                Public Interfaces
12951974Smsmith ********************************************************************************
13051974Smsmith ********************************************************************************/
13151974Smsmith
13251974Smsmith/********************************************************************************
13351974Smsmith * Free all of the resources associated with (sc)
13451974Smsmith *
13551974Smsmith * Should not be called if the controller is active.
13651974Smsmith */
13751974Smsmithvoid
13851974Smsmithamr_free(struct amr_softc *sc)
13951974Smsmith{
14051974Smsmith    struct amr_command	*ac;
14151974Smsmith    u_int8_t		*p;
14251974Smsmith
14351974Smsmith    debug("called");
14451974Smsmith
14551974Smsmith
14651974Smsmith    /* throw away any command buffers */
14751974Smsmith    while ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL) {
14851974Smsmith	TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
14951974Smsmith	amr_freecmd(ac);
15051974Smsmith    }
15151974Smsmith
15251974Smsmith    /* destroy data-transfer DMA tag */
15351974Smsmith    if (sc->amr_buffer_dmat)
15451974Smsmith	bus_dma_tag_destroy(sc->amr_buffer_dmat);
15551974Smsmith
15651974Smsmith    /* free and destroy DMA memory and tag for s/g lists */
15751974Smsmith    if (sc->amr_sgtable)
15851974Smsmith	bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
15951974Smsmith    if (sc->amr_sg_dmat)
16051974Smsmith	bus_dma_tag_destroy(sc->amr_sg_dmat);
16151974Smsmith
16251974Smsmith    /* free and destroy DMA memory and tag for mailbox */
16351974Smsmith    if (sc->amr_mailbox) {
16451974Smsmith	p = (u_int8_t *)sc->amr_mailbox;
16551974Smsmith	bus_dmamem_free(sc->amr_sg_dmat, p - 16, sc->amr_sg_dmamap);
16651974Smsmith    }
16751974Smsmith    if (sc->amr_sg_dmat)
16851974Smsmith	bus_dma_tag_destroy(sc->amr_sg_dmat);
16951974Smsmith
17051974Smsmith    /* disconnect the interrupt handler */
17151974Smsmith    if (sc->amr_intr)
17251974Smsmith	bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr);
17351974Smsmith    if (sc->amr_irq != NULL)
17451974Smsmith	bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq);
17551974Smsmith
17651974Smsmith    /* destroy the parent DMA tag */
17751974Smsmith    if (sc->amr_parent_dmat)
17851974Smsmith	bus_dma_tag_destroy(sc->amr_parent_dmat);
17951974Smsmith
18051974Smsmith    /* release the register window mapping */
18151974Smsmith    if (sc->amr_reg != NULL)
18251974Smsmith	bus_release_resource(sc->amr_dev,
18351974Smsmith			     (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT,
18451974Smsmith			     AMR_CFG_BASE, sc->amr_reg);
18551974Smsmith}
18651974Smsmith
18751974Smsmith/********************************************************************************
18851974Smsmith * Allocate and map the scatter/gather table in bus space.
18951974Smsmith */
19051974Smsmithstatic void
19151974Smsmithamr_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
19251974Smsmith{
19351974Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
19451974Smsmith
19551974Smsmith    debug("called");
19651974Smsmith
19751974Smsmith    /* save base of s/g table's address in bus space */
19851974Smsmith    sc->amr_sgbusaddr = segs->ds_addr;
19951974Smsmith}
20051974Smsmith
20151974Smsmithstatic int
20251974Smsmithamr_sglist_map(struct amr_softc *sc)
20351974Smsmith{
20451974Smsmith    size_t	segsize;
20551974Smsmith    int		error;
20651974Smsmith
20751974Smsmith    debug("called");
20851974Smsmith
20951974Smsmith    /* destroy any existing mappings */
21051974Smsmith    if (sc->amr_sgtable)
21151974Smsmith	bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
21251974Smsmith    if (sc->amr_sg_dmat)
21351974Smsmith	bus_dma_tag_destroy(sc->amr_sg_dmat);
21451974Smsmith
21551974Smsmith    /*
21651974Smsmith     * Create a single tag describing a region large enough to hold all of
21751974Smsmith     * the s/g lists we will need.
21851974Smsmith     */
21951974Smsmith    segsize = sizeof(struct amr_sgentry) * AMR_NSEG * sc->amr_maxio;
22051974Smsmith    error = bus_dma_tag_create(sc->amr_parent_dmat, 	/* parent */
22151974Smsmith			       1, 0, 			/* alignment, boundary */
22251974Smsmith			       BUS_SPACE_MAXADDR,	/* lowaddr */
22351974Smsmith			       BUS_SPACE_MAXADDR, 	/* highaddr */
22451974Smsmith			       NULL, NULL, 		/* filter, filterarg */
22551974Smsmith			       segsize, 1,		/* maxsize, nsegments */
22651974Smsmith			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
22751974Smsmith			       0,			/* flags */
22851974Smsmith			       &sc->amr_sg_dmat);
22951974Smsmith    if (error != 0) {
23051974Smsmith	device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n");
23151974Smsmith	return(ENOMEM);
23251974Smsmith    }
23351974Smsmith
23451974Smsmith    /*
23551974Smsmith     * Allocate enough s/g maps for all commands and permanently map them into
23651974Smsmith     * controller-visible space.
23751974Smsmith     *
23851974Smsmith     * XXX this assumes we can get enough space for all the s/g maps in one
23951974Smsmith     * contiguous slab.  We may need to switch to a more complex arrangement where
24051974Smsmith     * we allocate in smaller chunks and keep a lookup table from slot to bus address.
24151974Smsmith     */
24251974Smsmith    error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap);
24351974Smsmith    if (error) {
24451974Smsmith	device_printf(sc->amr_dev, "can't allocate s/g table\n");
24551974Smsmith	return(ENOMEM);
24651974Smsmith    }
24751974Smsmith    bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_dma_map_sg, sc, 0);
24851974Smsmith    return(0);
24951974Smsmith}
25051974Smsmith
25151974Smsmith/********************************************************************************
25251974Smsmith * Allocate and set up mailbox areas for the controller (sc)
25351974Smsmith *
25451974Smsmith * The basic mailbox structure should be 16-byte aligned.  This means that the
25551974Smsmith * mailbox64 structure has 4 bytes hanging off the bottom.
25651974Smsmith */
25751974Smsmithstatic void
25851974Smsmithamr_map_mailbox(void *arg, bus_dma_segment_t *segs, int nseg, int error)
25951974Smsmith{
26051974Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
26151974Smsmith
26251974Smsmith    debug("called");
26351974Smsmith
26451974Smsmith    /* save phsyical base of the basic mailbox structure */
26551974Smsmith    sc->amr_mailboxphys = segs->ds_addr + 16;
26651974Smsmith}
26751974Smsmith
26851974Smsmithstatic int
26951974Smsmithamr_setup_mbox(struct amr_softc *sc)
27051974Smsmith{
27151974Smsmith    int		error;
27251974Smsmith    u_int8_t	*p;
27351974Smsmith
27451974Smsmith    debug("called");
27551974Smsmith
27651974Smsmith    /*
27751974Smsmith     * Create a single tag describing a region large enough to hold the entire
27851974Smsmith     * mailbox.
27951974Smsmith     */
28051974Smsmith    error = bus_dma_tag_create(sc->amr_parent_dmat,	/* parent */
28151974Smsmith			       16, 0,			/* alignment, boundary */
28251974Smsmith			       BUS_SPACE_MAXADDR,	/* lowaddr */
28351974Smsmith			       BUS_SPACE_MAXADDR,	/* highaddr */
28451974Smsmith			       NULL, NULL,		/* filter, filterarg */
28551974Smsmith			       sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */
28651974Smsmith			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
28751974Smsmith			       0,			/* flags */
28851974Smsmith			       &sc->amr_mailbox_dmat);
28951974Smsmith    if (error != 0) {
29051974Smsmith	device_printf(sc->amr_dev, "can't allocate mailbox tag\n");
29151974Smsmith	return(ENOMEM);
29251974Smsmith    }
29351974Smsmith
29451974Smsmith    /*
29551974Smsmith     * Allocate the mailbox structure and permanently map it into
29651974Smsmith     * controller-visible space.
29751974Smsmith     */
29851974Smsmith    error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT,
29951974Smsmith			     &sc->amr_mailbox_dmamap);
30051974Smsmith    if (error) {
30151974Smsmith	device_printf(sc->amr_dev, "can't allocate mailbox memory\n");
30251974Smsmith	return(ENOMEM);
30351974Smsmith    }
30451974Smsmith    bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p,
30551974Smsmith		    sizeof(struct amr_mailbox64), amr_map_mailbox, sc, 0);
30651974Smsmith    /*
30751974Smsmith     * Conventional mailbox is inside the mailbox64 region.
30851974Smsmith     */
30951974Smsmith    bzero(p, sizeof(struct amr_mailbox64));
31051974Smsmith    sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12);
31151974Smsmith    sc->amr_mailbox = (struct amr_mailbox *)(p + 16);
31251974Smsmith
31351974Smsmith    if (sc->amr_type == AMR_TYPE_STD) {
31451974Smsmith	/* XXX we have to tell the controller where we put it */
31551974Smsmith    }
31651974Smsmith    return(0);
31751974Smsmith}
31851974Smsmith
31951974Smsmith
32051974Smsmith/********************************************************************************
32151974Smsmith * Initialise the controller and softc.
32251974Smsmith */
32351974Smsmithint
32451974Smsmithamr_attach(struct amr_softc *sc)
32551974Smsmith{
32651974Smsmith    int			rid, error;
32751974Smsmith
32851974Smsmith    /*
32951974Smsmith     * Initialise per-controller queues.
33051974Smsmith     */
33152543Smsmith    TAILQ_INIT(&sc->amr_work);
33251974Smsmith    TAILQ_INIT(&sc->amr_freecmds);
33351974Smsmith    bufq_init(&sc->amr_bufq);
33451974Smsmith
33551974Smsmith    /*
33651974Smsmith     * Configure for this controller type.
33751974Smsmith     */
33851974Smsmith    if (sc->amr_type == AMR_TYPE_QUARTZ) {
33951974Smsmith	sc->amr_submit_command = amr_quartz_submit_command;
34051974Smsmith	sc->amr_get_work       = amr_quartz_get_work;
34151974Smsmith	sc->amr_attach_mailbox = amr_quartz_attach_mailbox;
34251974Smsmith    } else {
34351974Smsmith	sc->amr_submit_command = amr_std_submit_command;
34451974Smsmith	sc->amr_get_work       = amr_std_get_work;
34551974Smsmith	sc->amr_attach_mailbox = amr_std_attach_mailbox;
34651974Smsmith    }
34751974Smsmith
34851974Smsmith    /*
34951974Smsmith     * Allocate and connect our interrupt.
35051974Smsmith     */
35151974Smsmith    rid = 0;
35251974Smsmith    sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
35351974Smsmith    if (sc->amr_irq == NULL) {
35451974Smsmith        device_printf(sc->amr_dev, "couldn't allocate interrupt\n");
35551974Smsmith        amr_free(sc);
35651974Smsmith        return(ENXIO);
35751974Smsmith    }
35851974Smsmith    error = bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO,  amr_intr, sc, &sc->amr_intr);
35951974Smsmith    if (error) {
36051974Smsmith        device_printf(sc->amr_dev, "couldn't set up interrupt\n");
36151974Smsmith        amr_free(sc);
36251974Smsmith        return(ENXIO);
36351974Smsmith    }
36451974Smsmith
36551974Smsmith    /*
36651974Smsmith     * Create DMA tag for mapping buffers into controller-addressable space.
36751974Smsmith     */
36851974Smsmith    error = bus_dma_tag_create(sc->amr_parent_dmat,     /* parent */
36951974Smsmith                               1, 0,                    /* alignment, boundary */
37051974Smsmith                               BUS_SPACE_MAXADDR,       /* lowaddr */
37151974Smsmith                               BUS_SPACE_MAXADDR,       /* highaddr */
37251974Smsmith                               NULL, NULL,              /* filter, filterarg */
37351974Smsmith                               MAXBSIZE, AMR_NSEG,      /* maxsize, nsegments */
37451974Smsmith                               BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
37551974Smsmith                               0,                       /* flags */
37651974Smsmith                               &sc->amr_buffer_dmat);
37751974Smsmith    if (error != 0) {
37851974Smsmith        device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
37951974Smsmith        return(ENOMEM);
38051974Smsmith    }
38151974Smsmith
38251974Smsmith    /*
38351974Smsmith     * Allocate and set up mailbox in a bus-visible fashion, attach to controller.
38451974Smsmith     */
38551974Smsmith    if ((error = amr_setup_mbox(sc)) != 0)
38651974Smsmith	return(error);
38751974Smsmith    sc->amr_attach_mailbox(sc);
38851974Smsmith
38951974Smsmith    /*
39051974Smsmith     * Build a temporary set of scatter/gather buffers.
39151974Smsmith     */
39251974Smsmith    sc->amr_maxio = 2;
39351974Smsmith    if (amr_sglist_map(sc))
39451974Smsmith	return(ENXIO);
39551974Smsmith
39651974Smsmith    /*
39751974Smsmith     * Quiz controller for features and limits.
39851974Smsmith     */
39951974Smsmith    if (amr_query_controller(sc))
40051974Smsmith	return(ENXIO);
40151974Smsmith
40251974Smsmith    /*
40351974Smsmith     * Rebuild the scatter/gather buffers now we know how many we need.
40451974Smsmith     */
40551974Smsmith    if (amr_sglist_map(sc))
40651974Smsmith	return(ENXIO);
40751974Smsmith
40851974Smsmith    return(0);
40951974Smsmith}
41051974Smsmith
41151974Smsmith/********************************************************************************
41251974Smsmith * Locate disk resources and attach children to them.
41351974Smsmith */
41451974Smsmithvoid
41551974Smsmithamr_startup(struct amr_softc *sc)
41651974Smsmith{
41751974Smsmith    struct amr_logdrive	*dr;
41851974Smsmith    int			i, error;
41951974Smsmith
42051974Smsmith    debug("called");
42151974Smsmith
42251974Smsmith    /* get up-to-date drive information */
42351974Smsmith    if (amr_query_controller(sc)) {
42451974Smsmith	device_printf(sc->amr_dev, "couldn't scan controller for drives\n");
42551974Smsmith	return;
42651974Smsmith    }
42751974Smsmith
42851974Smsmith    /* iterate over available drives */
42951974Smsmith    for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
43051974Smsmith	/* are we already attached to this drive? */
43151974Smsmith	if (dr->al_disk == 0) {
43251974Smsmith	    /* generate geometry information */
43351974Smsmith	    if (dr->al_size > 0x200000) {	/* extended translation? */
43451974Smsmith		dr->al_heads = 255;
43551974Smsmith		dr->al_sectors = 63;
43651974Smsmith	    } else {
43751974Smsmith		dr->al_heads = 64;
43851974Smsmith		dr->al_sectors = 32;
43951974Smsmith	    }
44051974Smsmith	    dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
44151974Smsmith
44251974Smsmith	    dr->al_disk = device_add_child(sc->amr_dev, NULL, -1, dr);
44351974Smsmith	    if (dr->al_disk == 0)
44451974Smsmith		device_printf(sc->amr_dev, "device_add_child failed\n");
44551974Smsmith	}
44651974Smsmith    }
44751974Smsmith
44851974Smsmith    if ((error = bus_generic_attach(sc->amr_dev)) != 0)
44951974Smsmith	device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
45051974Smsmith
45151974Smsmith    /* mark controller back up */
45251974Smsmith    sc->amr_state &= ~AMR_STATE_SHUTDOWN;
45351974Smsmith
45451974Smsmith    /* interrupts will be enabled before we do anything more */
45551974Smsmith    sc->amr_state |= AMR_STATE_INTEN;
45651974Smsmith}
45751974Smsmith
45851974Smsmith/********************************************************************************
45951974Smsmith * Disconnect from the controller completely, in preparation for unload.
46051974Smsmith */
46151974Smsmithint
46251974Smsmithamr_detach(device_t dev)
46351974Smsmith{
46451974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
46551974Smsmith    int			error;
46651974Smsmith
46751974Smsmith    debug("called");
46851974Smsmith
46951974Smsmith    if (sc->amr_state & AMR_STATE_OPEN)
47051974Smsmith	return(EBUSY);
47151974Smsmith
47251974Smsmith    if ((error = amr_shutdown(dev)))
47351974Smsmith	return(error);
47451974Smsmith
47551974Smsmith    amr_free(sc);
47651974Smsmith
47751974Smsmith    /*
47851974Smsmith     * Deregister the control device on last detach.
47951974Smsmith     */
48051974Smsmith    if (--cdev_registered == 0)
48151974Smsmith	cdevsw_remove(&amr_cdevsw);
48251974Smsmith
48351974Smsmith    return(0);
48451974Smsmith}
48551974Smsmith
48651974Smsmith/********************************************************************************
48751974Smsmith * Bring the controller down to a dormant state and detach all child devices.
48851974Smsmith *
48951974Smsmith * This function is called before detach, system shutdown, or before performing
49051974Smsmith * an operation which may add or delete system disks.  (Call amr_startup to
49151974Smsmith * resume normal operation.)
49251974Smsmith *
49351974Smsmith * Note that we can assume that the bufq on the controller is empty, as we won't
49451974Smsmith * allow shutdown if any device is open.
49551974Smsmith */
49651974Smsmithint
49751974Smsmithamr_shutdown(device_t dev)
49851974Smsmith{
49951974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
50051974Smsmith    struct amrd_softc	*ad;
50151974Smsmith    int			i, s, error;
50251974Smsmith
50351974Smsmith    debug("called");
50451974Smsmith
50551974Smsmith    s = splbio();
50651974Smsmith    error = 0;
50751974Smsmith
50851974Smsmith    /* assume we're going to shut down */
50951974Smsmith    sc->amr_state |= AMR_STATE_SHUTDOWN;
51051974Smsmith    for (i = 0; i < AMR_MAXLD; i++) {
51151974Smsmith	if (sc->amr_drive[i].al_disk != 0) {
51251974Smsmith	    ad = device_get_softc(sc->amr_drive[i].al_disk);
51351974Smsmith	    if (ad->amrd_flags & AMRD_OPEN) {		/* drive is mounted, abort shutdown */
51451974Smsmith		sc->amr_state &= ~AMR_STATE_SHUTDOWN;
51551974Smsmith		device_printf(sc->amr_drive[i].al_disk, "still open, can't shutdown\n");
51651974Smsmith		error = EBUSY;
51751974Smsmith		goto out;
51851974Smsmith	    }
51951974Smsmith	}
52051974Smsmith    }
52151974Smsmith
52251974Smsmith    /* flush controller */
52351974Smsmith    device_printf(sc->amr_dev, "flushing cache...");
52451974Smsmith    if (amr_flush(sc)) {
52551974Smsmith	printf("failed\n");
52651974Smsmith    } else {
52751974Smsmith	printf("done\n");
52851974Smsmith    }
52951974Smsmith
53051974Smsmith    /* delete all our child devices */
53151974Smsmith    for (i = 0; i < AMR_MAXLD; i++) {
53251974Smsmith	if (sc->amr_drive[i].al_disk != 0) {
53351974Smsmith	    if ((error = device_delete_child(sc->amr_dev, sc->amr_drive[i].al_disk)) != 0)
53451974Smsmith		goto out;
53551974Smsmith	    sc->amr_drive[i].al_disk = 0;
53651974Smsmith	}
53751974Smsmith    }
53851974Smsmith    bus_generic_detach(sc->amr_dev);
53951974Smsmith
54051974Smsmith out:
54151974Smsmith    splx(s);
54251974Smsmith    return(error);
54351974Smsmith}
54451974Smsmith
54551974Smsmith/********************************************************************************
54651974Smsmith * Bring the controller to a quiescent state, ready for system suspend.
54751974Smsmith */
54851974Smsmithint
54951974Smsmithamr_suspend(device_t dev)
55051974Smsmith{
55151974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
55251974Smsmith
55351974Smsmith    debug("called");
55451974Smsmith
55551974Smsmith    sc->amr_state |= AMR_STATE_SUSPEND;
55651974Smsmith
55751974Smsmith    /* flush controller */
55851974Smsmith    device_printf(sc->amr_dev, "flushing cache...");
55951974Smsmith    printf("%s\n", amr_flush(sc) ? "failed" : "done");
56051974Smsmith
56151974Smsmith    return(0);
56251974Smsmith}
56351974Smsmith
56451974Smsmith/********************************************************************************
56551974Smsmith * Bring the controller back to a state ready for operation.
56651974Smsmith */
56751974Smsmithint
56851974Smsmithamr_resume(device_t dev)
56951974Smsmith{
57051974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
57151974Smsmith
57251974Smsmith    debug("called");
57351974Smsmith
57451974Smsmith    sc->amr_state &= ~AMR_STATE_SUSPEND;
57551974Smsmith
57651974Smsmith    return(0);
57751974Smsmith}
57851974Smsmith
57951974Smsmith/*******************************************************************************
58051974Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy
58151974Smsmith * status.
58251974Smsmith */
58351974Smsmithvoid
58451974Smsmithamr_intr(void *arg)
58551974Smsmith{
58651974Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
58751974Smsmith    int			worked;
58851974Smsmith
58951974Smsmith    debug("called on %p", sc);
59051974Smsmith
59151974Smsmith    /* spin collecting finished commands, process them if we find anything */
59251974Smsmith    worked = 0;
59351974Smsmith    while (amr_done(sc))
59451974Smsmith	worked = 1;
59551974Smsmith    if (worked)
59651974Smsmith	amr_complete(sc);
59751974Smsmith};
59851974Smsmith
59951974Smsmith/*******************************************************************************
60051974Smsmith * Receive a buf structure from a child device and queue it on a particular
60151974Smsmith * disk resource, then poke the disk resource to start as much work as it can.
60251974Smsmith */
60351974Smsmithint
60451974Smsmithamr_submit_buf(struct amr_softc *sc, struct buf *bp)
60551974Smsmith{
60652543Smsmith    int		s;
60752543Smsmith
60851974Smsmith    debug("called");
60951974Smsmith
61052543Smsmith    s = splbio();
61151974Smsmith    bufq_insert_tail(&sc->amr_bufq, bp);
61252543Smsmith    splx(s);
61351974Smsmith    sc->amr_waitbufs++;
61451974Smsmith    amr_startio(sc);
61551974Smsmith    return(0);
61651974Smsmith}
61751974Smsmith
61851974Smsmith/********************************************************************************
61951974Smsmith * Accept an open operation on the control device.
62051974Smsmith */
62151974Smsmithint
62251974Smsmithamr_open(dev_t dev, int flags, int fmt, struct proc *p)
62351974Smsmith{
62451974Smsmith    int			unit = minor(dev);
62551974Smsmith    struct amr_softc	*sc = devclass_get_softc(amr_devclass, unit);
62651974Smsmith
62751974Smsmith    sc->amr_state |= AMR_STATE_OPEN;
62851974Smsmith    return(0);
62951974Smsmith}
63051974Smsmith
63151974Smsmith/********************************************************************************
63251974Smsmith * Accept the last close on the control device.
63351974Smsmith */
63451974Smsmithint
63551974Smsmithamr_close(dev_t dev, int flags, int fmt, struct proc *p)
63651974Smsmith{
63751974Smsmith    int			unit = minor(dev);
63851974Smsmith    struct amr_softc	*sc = devclass_get_softc(amr_devclass, unit);
63951974Smsmith
64051974Smsmith    sc->amr_state &= ~AMR_STATE_OPEN;
64151974Smsmith    return (0);
64251974Smsmith}
64351974Smsmith
64451974Smsmith/********************************************************************************
64551974Smsmith * Handle controller-specific control operations.
64651974Smsmith */
64751974Smsmithint
64851974Smsmithamr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
64951974Smsmith{
65051974Smsmith
65151974Smsmith    switch(cmd) {
65251974Smsmith    default:
65351974Smsmith	return(ENOTTY);
65451974Smsmith    }
65551974Smsmith}
65651974Smsmith
65751974Smsmith/********************************************************************************
65851974Smsmith * Handle operations requested by a drive connected to this controller.
65951974Smsmith */
66051974Smsmithint
66151974Smsmithamr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd,
66251974Smsmith		 caddr_t addr, int32_t flag, struct proc *p)
66351974Smsmith{
66451974Smsmith    return(ENOTTY);
66551974Smsmith}
66651974Smsmith
66751974Smsmith/********************************************************************************
66851974Smsmith ********************************************************************************
66951974Smsmith                                                                 Command Wrappers
67051974Smsmith ********************************************************************************
67151974Smsmith ********************************************************************************/
67251974Smsmith
67351974Smsmith/********************************************************************************
67451974Smsmith * Interrogate the controller for the operational parameters we require.
67551974Smsmith */
67651974Smsmithstatic int
67751974Smsmithamr_query_controller(struct amr_softc *sc)
67851974Smsmith{
67951974Smsmith    void	*buf;
68051974Smsmith    int		i;
68151974Smsmith
68251974Smsmith    /* try to issue an ENQUIRY3 command */
68351974Smsmith    if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
68451974Smsmith			   AMR_CONFIG_ENQ3_SOLICITED_FULL)) == NULL) {
68551974Smsmith
68651974Smsmith	struct amr_enquiry	*ae;
68751974Smsmith
68851974Smsmith	/* failed, try the old ENQUIRY command */
68951974Smsmith	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
69051974Smsmith	    device_printf(sc->amr_dev, "could not obtain configuration data from controller\n");
69151974Smsmith	    return(1);
69251974Smsmith	}
69351974Smsmith	/* first-time enquiry? */
69451974Smsmith	if (sc->amr_maxdrives == 0) {
69551974Smsmith	    device_printf(sc->amr_dev, "firmware %.4s bios %.4s  %dMB memory, chipset %x\n",
69651974Smsmith			  ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
69751974Smsmith			  ae->ae_adapter.aa_memorysize, ae->ae_adapter.aa_chipsetvalue);
69851974Smsmith	}
69951974Smsmith	sc->amr_maxdrives = 8;
70051974Smsmith	sc->amr_maxio = ae->ae_adapter.aa_maxio;
70151974Smsmith	for (i = 0; i < ae->ae_ldrv.al_numdrives; i++) {
70251974Smsmith	    sc->amr_drive[i].al_size = ae->ae_ldrv.al_size[i];
70351974Smsmith	    sc->amr_drive[i].al_state = ae->ae_ldrv.al_state[i];
70451974Smsmith	    sc->amr_drive[i].al_properties = ae->ae_ldrv.al_properties[i];
70551974Smsmith	    debug("  drive %d: %d state %x properties %x\n", i, sc->amr_drive[i].al_size,
70651974Smsmith		  sc->amr_drive[i].al_state, sc->amr_drive[i].al_properties);
70751974Smsmith	}
70851974Smsmith	for (; i < AMR_MAXLD; i++)
70951974Smsmith	    sc->amr_drive[i].al_size = 0xffffffff;
71051974Smsmith	free(ae, M_DEVBUF);
71151974Smsmith    } else {
71251974Smsmith	free(buf, M_DEVBUF);
71351974Smsmith	sc->amr_maxdrives = 40;
71451974Smsmith
71551974Smsmith	/* get static product info */
71651974Smsmith	if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODINFO, 0)) == NULL) {
71751974Smsmith	    device_printf(sc->amr_dev, "controller supports 40ld but CONFIG_PRODINFO failed\n");
71851974Smsmith	    return(1);
71951974Smsmith	}
72051974Smsmith	free(buf, M_DEVBUF);
72151974Smsmith	device_printf(sc->amr_dev, "40LD firmware unsupported; send controller to msmith@freebsd.org\n");
72251974Smsmith	return(1);
72351974Smsmith    }
72451974Smsmith    return(0);
72551974Smsmith}
72651974Smsmith
72751974Smsmith/********************************************************************************
72851974Smsmith * Run a generic enquiry-style command.
72951974Smsmith */
73051974Smsmithstatic void *
73151974Smsmithamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
73251974Smsmith{
73351974Smsmith    struct amr_command	*ac;
73451974Smsmith    void		*result;
73551974Smsmith    u_int8_t		*mbox;
73651974Smsmith    int			error;
73751974Smsmith
73851974Smsmith    debug("called");
73951974Smsmith
74051974Smsmith    error = 1;
74151974Smsmith    result = NULL;
74251974Smsmith
74351974Smsmith    /* get ourselves a command buffer */
74451974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
74551974Smsmith	goto out;
74651974Smsmith    /* allocate the response structure */
74751974Smsmith    if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
74851974Smsmith	goto out;
74951974Smsmith    /* get a command slot */
75051974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
75151974Smsmith    if (amr_getslot(ac))
75251974Smsmith	goto out;
75351974Smsmith
75451974Smsmith    /* map the command so the controller can see it */
75551974Smsmith    ac->ac_data = result;
75651974Smsmith    ac->ac_length = bufsize;
75751974Smsmith    amr_mapcmd(ac);
75851974Smsmith
75951974Smsmith    /* build the command proper */
76051974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
76151974Smsmith    mbox[0] = cmd;
76251974Smsmith    mbox[2] = cmdsub;
76351974Smsmith    mbox[3] = cmdqual;
76451974Smsmith    ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
76551974Smsmith
76651974Smsmith    /* run the command in polled/wait mode as suits the current mode */
76751974Smsmith    if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac))
76851974Smsmith	goto out;
76951974Smsmith    error = ac->ac_status;
77051974Smsmith
77151974Smsmith out:
77251974Smsmith    if (ac != NULL)
77351974Smsmith	amr_releasecmd(ac);
77451974Smsmith    if ((error != 0) && (result != NULL)) {
77551974Smsmith	free(result, M_DEVBUF);
77651974Smsmith	result = NULL;
77751974Smsmith    }
77851974Smsmith    return(result);
77951974Smsmith}
78051974Smsmith
78151974Smsmith/********************************************************************************
78251974Smsmith * Flush the controller's internal cache, return status.
78351974Smsmith */
78451974Smsmithstatic int
78551974Smsmithamr_flush(struct amr_softc *sc)
78651974Smsmith{
78751974Smsmith    struct amr_command	*ac;
78851974Smsmith    int			error;
78951974Smsmith
79051974Smsmith    /* get ourselves a command buffer */
79151974Smsmith    error = 1;
79251974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
79351974Smsmith	goto out;
79451974Smsmith    /* get a command slot */
79551974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
79651974Smsmith    if (amr_getslot(ac))
79751974Smsmith	goto out;
79851974Smsmith
79951974Smsmith    /* build the command proper */
80051974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
80151974Smsmith
80251974Smsmith    /* run the command in polled/wait mode as suits the current mode */
80351974Smsmith    if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac))
80451974Smsmith	goto out;
80551974Smsmith    error = ac->ac_status;
80651974Smsmith
80751974Smsmith out:
80851974Smsmith    if (ac != NULL)
80951974Smsmith	amr_releasecmd(ac);
81051974Smsmith    return(error);
81151974Smsmith}
81251974Smsmith
81351974Smsmith/********************************************************************************
81451974Smsmith * Pull as much work off the softc's work queue as possible and give it to the
81551974Smsmith * controller.  Leave a couple of slots free for emergencies.
81651974Smsmith *
81752543Smsmith * We avoid running at splbio() whenever possible.
81851974Smsmith */
81951974Smsmithstatic void
82051974Smsmithamr_startio(struct amr_softc *sc)
82151974Smsmith{
82251974Smsmith    struct amr_command	*ac;
82351974Smsmith    struct amrd_softc	*amrd;
82451974Smsmith    struct buf		*bp;
82551974Smsmith    int			blkcount;
82651974Smsmith    int			driveno;
82751974Smsmith    int			cmd;
82852543Smsmith    int			s;
82951974Smsmith
83051974Smsmith    /* spin until something prevents us from doing any work */
83152543Smsmith    s = splbio();
83251974Smsmith    for (;;) {
83351974Smsmith
83451974Smsmith	/* see if there's work to be done */
83551974Smsmith	if ((bp = bufq_first(&sc->amr_bufq)) == NULL)
83651974Smsmith	    break;
83751974Smsmith	/* get a command */
83851974Smsmith	if ((ac = amr_alloccmd(sc)) == NULL)
83951974Smsmith	    break;
84051974Smsmith	/* get a slot for the command */
84151974Smsmith	if (amr_getslot(ac) != 0) {
84251974Smsmith	    amr_releasecmd(ac);
84351974Smsmith	    break;
84451974Smsmith	}
84551974Smsmith	/* get the buf containing our work */
84651974Smsmith	bufq_remove(&sc->amr_bufq, bp);
84751974Smsmith	sc->amr_waitbufs--;
84852543Smsmith	splx(s);
84951974Smsmith
85051974Smsmith	/* connect the buf to the command */
85151974Smsmith	ac->ac_complete = amr_completeio;
85251974Smsmith	ac->ac_private = bp;
85351974Smsmith	ac->ac_data = bp->b_data;
85451974Smsmith	ac->ac_length = bp->b_bcount;
85551974Smsmith	if (bp->b_flags & B_READ) {
85651974Smsmith	    ac->ac_flags |= AMR_CMD_DATAIN;
85751974Smsmith	    cmd = AMR_CMD_LREAD;
85851974Smsmith	} else {
85951974Smsmith	    ac->ac_flags |= AMR_CMD_DATAOUT;
86051974Smsmith	    cmd = AMR_CMD_LWRITE;
86151974Smsmith	}
86251974Smsmith
86351974Smsmith	/* map the command so the controller can work with it */
86451974Smsmith	amr_mapcmd(ac);
86551974Smsmith
86651974Smsmith	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
86751974Smsmith	amrd = (struct amrd_softc *)bp->b_driver1;
86851974Smsmith	driveno = amrd->amrd_drive - &sc->amr_drive[0];
86951974Smsmith	blkcount = bp->b_bcount / AMR_BLKSIZE;
87051974Smsmith
87152439Smsmith	if ((bp->b_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
87251974Smsmith	    device_printf(sc->amr_dev, "I/O beyond end of unit (%u,%d > %u)\n",
87352439Smsmith			  bp->b_pblkno, blkcount, sc->amr_drive[driveno].al_size);
87451974Smsmith
87551974Smsmith	/*
87651974Smsmith	 * Build the I/O command.
87751974Smsmith	 */
87851974Smsmith	ac->ac_mailbox.mb_command = cmd;
87951974Smsmith	ac->ac_mailbox.mb_blkcount = blkcount;
88052439Smsmith	ac->ac_mailbox.mb_lba = bp->b_pblkno;
88151974Smsmith	ac->ac_mailbox.mb_physaddr = ac->ac_sgphys;
88251974Smsmith	ac->ac_mailbox.mb_drive = driveno;
88351974Smsmith	ac->ac_mailbox.mb_nsgelem = ac->ac_nsgent;
88451974Smsmith
88551974Smsmith	/* try to give command to controller */
88651974Smsmith	if (amr_start(ac) != 0) {
88751974Smsmith	    /* fail the command */
88851974Smsmith	    ac->ac_status = AMR_STATUS_WEDGED;
88951974Smsmith	    amr_completeio(ac);
89051974Smsmith	}
89152543Smsmith	s = splbio();
89251974Smsmith    }
89352543Smsmith    splx(s);
89451974Smsmith}
89551974Smsmith
89651974Smsmith/********************************************************************************
89751974Smsmith * Handle completion of an I/O command.
89851974Smsmith */
89951974Smsmithstatic void
90051974Smsmithamr_completeio(struct amr_command *ac)
90151974Smsmith{
90251974Smsmith    struct amr_softc	*sc = ac->ac_sc;
90351974Smsmith    struct buf		*bp = (struct buf *)ac->ac_private;
90451974Smsmith
90551974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
90651974Smsmith	bp->b_error = EIO;
90751974Smsmith	bp->b_flags |= B_ERROR;
90851974Smsmith
90951974Smsmith	switch(ac->ac_status) {
91051974Smsmith	    /* XXX need more information on I/O error reasons */
91151974Smsmith	default:
91251974Smsmith	    device_printf(sc->amr_dev, "I/O error - %x\n", ac->ac_status);
91351974Smsmith	    amr_printcommand(ac);
91451974Smsmith	    break;
91551974Smsmith	}
91651974Smsmith    }
91751974Smsmith    amr_releasecmd(ac);
91851974Smsmith    amrd_intr(bp);
91951974Smsmith}
92051974Smsmith
92151974Smsmith/********************************************************************************
92251974Smsmith ********************************************************************************
92351974Smsmith                                                               Command Processing
92451974Smsmith ********************************************************************************
92551974Smsmith ********************************************************************************/
92651974Smsmith
92751974Smsmith/********************************************************************************
92851974Smsmith * Take a command, submit it to the controller and sleep until it completes
92951974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
93051974Smsmith */
93151974Smsmithstatic int
93251974Smsmithamr_wait_command(struct amr_command *ac)
93351974Smsmith{
93451974Smsmith    struct amr_softc	*sc = ac->ac_sc;
93551974Smsmith    int			error, count;
93651974Smsmith
93751974Smsmith    debug("called");
93851974Smsmith
93951974Smsmith    ac->ac_complete = NULL;
94051974Smsmith    ac->ac_private = ac;
94151974Smsmith    if ((error = amr_start(ac)) != 0)
94251974Smsmith	return(error);
94351974Smsmith
94451974Smsmith    count = 0;
94551974Smsmith    /* XXX better timeout? */
94651974Smsmith    while ((ac->ac_status == AMR_STATUS_BUSY) && (count < 30)) {
94751974Smsmith	tsleep(ac->ac_private, PRIBIO | PCATCH, "amrwcmd", hz);
94851974Smsmith    }
94951974Smsmith
95051974Smsmith    if (ac->ac_status != 0) {
95151974Smsmith	device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status);
95251974Smsmith	return(EIO);
95351974Smsmith    }
95451974Smsmith    return(0);
95551974Smsmith}
95651974Smsmith
95751974Smsmith/********************************************************************************
95851974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
95951974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
96051974Smsmith */
96151974Smsmithstatic int
96251974Smsmithamr_poll_command(struct amr_command *ac)
96351974Smsmith{
96451974Smsmith    struct amr_softc	*sc = ac->ac_sc;
96551974Smsmith    int			error, count, s;
96651974Smsmith
96751974Smsmith    debug("called");
96851974Smsmith
96951974Smsmith    ac->ac_complete = NULL;
97051974Smsmith    ac->ac_private = NULL;
97151974Smsmith    if ((error = amr_start(ac)) != 0)
97251974Smsmith	return(error);
97351974Smsmith
97451974Smsmith    count = 0;
97551974Smsmith    do {
97651974Smsmith	/*
97751974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
97851974Smsmith	 * Note that the timeout here is somewhat arbitrary.
97951974Smsmith	 */
98051974Smsmith	amr_done(sc);
98151974Smsmith    } while ((ac->ac_status == AMR_STATUS_BUSY) && (count++ < 100000));
98251974Smsmith    s = splbio();
98351974Smsmith    if (ac->ac_status != AMR_STATUS_BUSY) {
98452543Smsmith	TAILQ_REMOVE(&sc->amr_work, ac, ac_link);
98552543Smsmith	sc->amr_workcount--;
98651974Smsmith	error = 0;
98751974Smsmith    } else {
98851974Smsmith	/* take the command out of the busy list, mark slot as bogus */
98951974Smsmith	sc->amr_busycmd[ac->ac_slot] = (struct amr_command *)sc;
99051974Smsmith	error = EIO;
99151974Smsmith	device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status);
99251974Smsmith    }
99351974Smsmith    splx(s);
99451974Smsmith    return(error);
99551974Smsmith}
99651974Smsmith
99751974Smsmith/********************************************************************************
99851974Smsmith * Get a free command slot.
99951974Smsmith */
100051974Smsmithstatic int
100151974Smsmithamr_getslot(struct amr_command *ac)
100251974Smsmith{
100351974Smsmith    struct amr_softc	*sc = ac->ac_sc;
100451974Smsmith    int			s, slot, limit;
100551974Smsmith
100651974Smsmith    debug("called");
100751974Smsmith
100851974Smsmith    /* enforce slot usage limit */
100951974Smsmith    limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
101051974Smsmith    if (sc->amr_busycmdcount > limit)
101151974Smsmith	return(EBUSY);
101251974Smsmith
101351974Smsmith    /*
101451974Smsmith     * Allocate a slot
101551974Smsmith     */
101651974Smsmith    s = splbio();
101751974Smsmith    for (slot = 0; slot < sc->amr_maxio; slot++) {
101851974Smsmith	if (sc->amr_busycmd[slot] == NULL)
101951974Smsmith	    break;
102051974Smsmith    }
102151974Smsmith    if (slot < sc->amr_maxio) {
102251974Smsmith	sc->amr_busycmdcount++;
102351974Smsmith	sc->amr_busycmd[slot] = ac;
102451974Smsmith    }
102551974Smsmith    splx(s);
102651974Smsmith
102751974Smsmith    /* out of slots? */
102851974Smsmith    if (slot >= sc->amr_maxio)
102951974Smsmith	return(EBUSY);
103051974Smsmith
103151974Smsmith    ac->ac_slot = slot;
103251974Smsmith    return(0);
103351974Smsmith}
103451974Smsmith
103551974Smsmith/********************************************************************************
103651974Smsmith * Map/unmap (ac)'s data in the controller's addressable space.
103751974Smsmith */
103851974Smsmithstatic void
103951974Smsmithamr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
104051974Smsmith{
104151974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
104251974Smsmith    struct amr_softc	*sc = ac->ac_sc;
104351974Smsmith    struct amr_sgentry	*sg;
104451974Smsmith    int			i;
104551974Smsmith
104651974Smsmith    debug("called");
104751974Smsmith
104851974Smsmith    /* get base address of s/g table */
104951974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
105051974Smsmith
105151974Smsmith    /* save s/g table information in command */
105251974Smsmith    ac->ac_nsgent = nsegments;
105351974Smsmith    ac->ac_sgphys = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
105451974Smsmith    ac->ac_dataphys = segs[0].ds_addr;
105551974Smsmith
105651974Smsmith    /* populate s/g table */
105751974Smsmith    for (i = 0; i < nsegments; i++, sg++) {
105851974Smsmith	sg->sg_addr = segs[i].ds_addr;
105951974Smsmith	sg->sg_count = segs[i].ds_len;
106051974Smsmith    }
106151974Smsmith}
106251974Smsmith
106351974Smsmithstatic void
106451974Smsmithamr_mapcmd(struct amr_command *ac)
106551974Smsmith{
106651974Smsmith    struct amr_softc	*sc = ac->ac_sc;
106751974Smsmith
106851974Smsmith    debug("called");
106951974Smsmith
107051974Smsmith    /* if the command involves data at all */
107151974Smsmith    if (ac->ac_data != NULL) {
107251974Smsmith
107351974Smsmith	/* map the data buffer into bus space and build the s/g list */
107451974Smsmith	bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
107551974Smsmith			amr_setup_dmamap, ac, 0);
107651974Smsmith	if (ac->ac_flags & AMR_CMD_DATAIN)
107751974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
107851974Smsmith	if (ac->ac_flags & AMR_CMD_DATAOUT)
107951974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
108051974Smsmith    }
108151974Smsmith}
108251974Smsmith
108351974Smsmithstatic void
108451974Smsmithamr_unmapcmd(struct amr_command *ac)
108551974Smsmith{
108651974Smsmith    struct amr_softc	*sc = ac->ac_sc;
108751974Smsmith
108851974Smsmith    debug("called");
108951974Smsmith
109051974Smsmith    /* if the command involved data at all */
109151974Smsmith    if (ac->ac_data != NULL) {
109251974Smsmith
109351974Smsmith	if (ac->ac_flags & AMR_CMD_DATAIN)
109451974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
109551974Smsmith	if (ac->ac_flags & AMR_CMD_DATAOUT)
109651974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
109751974Smsmith
109851974Smsmith	bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
109951974Smsmith    }
110051974Smsmith}
110151974Smsmith
110251974Smsmith/********************************************************************************
110351974Smsmith * Take a command and give it to the controller.  Take care of any completed
110451974Smsmith * commands we encouter while waiting.
110551974Smsmith */
110651974Smsmithstatic int
110751974Smsmithamr_start(struct amr_command *ac)
110851974Smsmith{
110951974Smsmith    struct amr_softc	*sc = ac->ac_sc;
111051974Smsmith    int			worked, done, s, i;
111151974Smsmith
111251974Smsmith    debug("called");
111351974Smsmith
111451974Smsmith    /*
111551974Smsmith     * Save the slot number so that we can locate this command when complete.
111651974Smsmith     * Note that ident = 0 seems to be special, so we don't use it.
111751974Smsmith     */
111851974Smsmith    ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
111951974Smsmith
112051974Smsmith    /* set the busy flag when we copy the mailbox in */
112151974Smsmith    ac->ac_mailbox.mb_busy = 1;
112251974Smsmith
112351974Smsmith    /* set impossible status so that a woken sleeper can tell the command is busy */
112451974Smsmith    ac->ac_status = AMR_STATUS_BUSY;
112551974Smsmith
112651974Smsmith    /* spin waiting for the mailbox */
112751974Smsmith    debug("wait for mailbox");
112851974Smsmith    for (i = 10000, done = 0, worked = 0; (i > 0) && !done; i--) {
112951974Smsmith	s = splbio();
113051974Smsmith
113151974Smsmith	/* is the mailbox free? */
113251974Smsmith	if (sc->amr_mailbox->mb_busy == 0) {
113351974Smsmith	    debug("got mailbox");
113451974Smsmith	    sc->amr_mailbox64->mb64_segment = 0;
113551974Smsmith	    bcopy(&ac->ac_mailbox, sc->amr_mailbox, AMR_MBOX_CMDSIZE);
113651974Smsmith	    sc->amr_submit_command(sc);
113751974Smsmith	    done = 1;
113852543Smsmith	    sc->amr_workcount++;
113952543Smsmith	    TAILQ_INSERT_TAIL(&sc->amr_work, ac, ac_link);
114051974Smsmith
114151974Smsmith	    /* not free, try to clean up while we wait */
114251974Smsmith	} else {
114351974Smsmith	    debug("busy flag %x\n", sc->amr_mailbox->mb_busy);
114451974Smsmith	    worked = amr_done(sc);
114551974Smsmith	}
114651974Smsmith	splx(s);
114751974Smsmith    }
114851974Smsmith
114951974Smsmith    /* do completion processing if we picked anything up */
115051974Smsmith    if (worked)
115151974Smsmith	amr_complete(sc);
115251974Smsmith
115351974Smsmith    /* command is enqueued? */
115451974Smsmith    if (done) {
115551974Smsmith	ac->ac_stamp = time_second;
115651974Smsmith	debug("posted command");
115751974Smsmith	return(0);
115851974Smsmith    }
115951974Smsmith
116051974Smsmith    /*
116151974Smsmith     * The controller wouldn't take the command.  Revoke the slot
116251974Smsmith     * that the command was given and return with a bad status.
116351974Smsmith     */
116451974Smsmith    sc->amr_busycmd[ac->ac_slot] = NULL;
116551974Smsmith    device_printf(sc->amr_dev, "controller wedged (not taking commands)\n");
116651974Smsmith    ac->ac_status = AMR_STATUS_WEDGED;
116751974Smsmith    return(EIO);
116851974Smsmith}
116951974Smsmith
117051974Smsmith/********************************************************************************
117151974Smsmith * Extract one or more completed commands from the controller (sc)
117251974Smsmith *
117352543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
117451974Smsmith */
117551974Smsmithstatic int
117651974Smsmithamr_done(struct amr_softc *sc)
117751974Smsmith{
117851974Smsmith    struct amr_command	*ac;
117951974Smsmith    struct amr_mailbox	mbox;
118052543Smsmith    int			i, idx, result;
118151974Smsmith
118251974Smsmith    debug("called");
118351974Smsmith
118451974Smsmith    /* See if there's anything for us to do */
118551974Smsmith    result = 0;
118651974Smsmith    if (sc->amr_get_work(sc, &mbox)) {
118751974Smsmith	/* iterate over completed commands */
118851974Smsmith	for (i = 0; i < mbox.mb_nstatus; i++) {
118951974Smsmith	    /* get pointer to busy command */
119051974Smsmith	    idx = mbox.mb_completed[i] - 1;
119151974Smsmith	    ac = sc->amr_busycmd[idx];
119251974Smsmith
119351974Smsmith	    /* really a busy command? */
119451974Smsmith	    if (ac != NULL) {
119551974Smsmith
119651974Smsmith		/* pull the command from the busy index */
119751974Smsmith		sc->amr_busycmd[idx] = NULL;
119851974Smsmith		sc->amr_busycmdcount--;
119951974Smsmith
120052543Smsmith		/* unmap data buffer */
120152543Smsmith		amr_unmapcmd(ac);
120252543Smsmith
120351974Smsmith		/* aborted command? */
120451974Smsmith		if (ac == (struct amr_command *)sc) {
120551974Smsmith		    device_printf(sc->amr_dev, "aborted command completed (%d)\n", idx);
120651974Smsmith		    ac = NULL;
120752543Smsmith
120852543Smsmith		    /* completed normally, save status */
120951974Smsmith		} else {
121051974Smsmith		    ac->ac_status = mbox.mb_status;
121151974Smsmith		    debug("completed command with status %x", mbox.mb_status);
121251974Smsmith		}
121351974Smsmith		result = 1;
121451974Smsmith	    }
121551974Smsmith	}
121651974Smsmith    }
121751974Smsmith    return(result);
121851974Smsmith}
121951974Smsmith
122051974Smsmith/********************************************************************************
122151974Smsmith * Do completion processing on done commands on (sc)
122251974Smsmith */
122351974Smsmithstatic void
122451974Smsmithamr_complete(struct amr_softc *sc)
122551974Smsmith{
122651974Smsmith    struct amr_command	*ac, *nc;
122751974Smsmith    int			s, count;
122851974Smsmith
122951974Smsmith    debug("called");
123051974Smsmith
123151974Smsmith    count = 0;
123251974Smsmith
123351974Smsmith    s = splbio();
123452543Smsmith    ac = TAILQ_FIRST(&sc->amr_work);
123551974Smsmith    while (ac != NULL) {
123651974Smsmith	nc = TAILQ_NEXT(ac, ac_link);
123751974Smsmith
123852543Smsmith	/* Skip if command is still active */
123952543Smsmith	if (ac->ac_status != AMR_STATUS_BUSY) {
124051974Smsmith
124151974Smsmith	    /*
124252543Smsmith	     * Is there a completion handler?
124351974Smsmith	     */
124452543Smsmith	    if (ac->ac_complete != NULL) {
124551974Smsmith
124652543Smsmith		/* remove and give to completion handler */
124752543Smsmith		TAILQ_REMOVE(&sc->amr_work, ac, ac_link);
124852543Smsmith		sc->amr_workcount--;
124952543Smsmith		ac->ac_complete(ac);
125052543Smsmith
125152543Smsmith		/*
125252543Smsmith		 * Is someone sleeping on this one?
125352543Smsmith		 */
125452543Smsmith	    } else if (ac->ac_private != NULL) {
125551974Smsmith
125652543Smsmith		/* remove and wake up */
125752543Smsmith		TAILQ_REMOVE(&sc->amr_work, ac, ac_link);
125852543Smsmith		sc->amr_workcount--;
125952543Smsmith		wakeup_one(ac->ac_private);
126052543Smsmith
126152543Smsmith		/*
126252543Smsmith		 * Leave it for a polling caller.
126352543Smsmith		 */
126452543Smsmith	    } else {
126552543Smsmith	    }
126651974Smsmith	}
126751974Smsmith	ac = nc;
126851974Smsmith    }
126951974Smsmith    splx(s);
127051974Smsmith
127151974Smsmith    /* queue more work if we can */
127251974Smsmith    amr_startio(sc);
127351974Smsmith}
127451974Smsmith
127551974Smsmith/********************************************************************************
127651974Smsmith ********************************************************************************
127751974Smsmith                                                        Command Buffer Management
127851974Smsmith ********************************************************************************
127951974Smsmith ********************************************************************************/
128051974Smsmith
128151974Smsmith/********************************************************************************
128251974Smsmith * Get a new command buffer.
128351974Smsmith *
128451974Smsmith * This may return NULL in low-memory cases.
128551974Smsmith *
128651974Smsmith * Note that using malloc() is expensive (the command buffer is << 1 page) but
128751974Smsmith * necessary if we are to be a loadable module before the zone allocator is fixed.
128851974Smsmith *
128951974Smsmith * If possible, we recycle a command buffer that's been used before.
129051974Smsmith *
129151974Smsmith * XXX Note that command buffers are not cleaned out - it is the caller's
129251974Smsmith *     responsibility to ensure that all required fields are filled in before
129351974Smsmith *     using a buffer.
129451974Smsmith */
129551974Smsmithstatic struct amr_command *
129651974Smsmithamr_alloccmd(struct amr_softc *sc)
129751974Smsmith{
129851974Smsmith    struct amr_command	*ac;
129951974Smsmith    int			error;
130051974Smsmith    int			s;
130151974Smsmith
130251974Smsmith    debug("called");
130351974Smsmith
130451974Smsmith    s = splbio();
130551974Smsmith    if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL)
130651974Smsmith	TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
130751974Smsmith    splx(s);
130851974Smsmith
130951974Smsmith    /* allocate a new command buffer? */
131051974Smsmith    if (ac == NULL) {
131151974Smsmith	ac = (struct amr_command *)malloc(sizeof(*ac), M_DEVBUF, M_NOWAIT);
131251974Smsmith	if (ac != NULL) {
131351974Smsmith	    bzero(ac, sizeof(*ac));
131451974Smsmith	    ac->ac_sc = sc;
131551974Smsmith	    error = bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap);
131651974Smsmith	    if (error) {
131751974Smsmith		free(ac, M_DEVBUF);
131851974Smsmith		return(NULL);
131951974Smsmith	    }
132051974Smsmith	}
132151974Smsmith    }
132251974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
132351974Smsmith    return(ac);
132451974Smsmith}
132551974Smsmith
132651974Smsmith/********************************************************************************
132751974Smsmith * Release a command buffer for recycling.
132851974Smsmith *
132951974Smsmith * XXX It might be a good idea to limit the number of commands we save for reuse
133051974Smsmith *     if it's shown that this list bloats out massively.
133151974Smsmith */
133251974Smsmithstatic void
133351974Smsmithamr_releasecmd(struct amr_command *ac)
133451974Smsmith{
133551974Smsmith    int		s;
133651974Smsmith
133751974Smsmith    debug("called");
133851974Smsmith
133951974Smsmith    s = splbio();
134051974Smsmith    TAILQ_INSERT_HEAD(&ac->ac_sc->amr_freecmds, ac, ac_link);
134151974Smsmith    splx(s);
134251974Smsmith}
134351974Smsmith
134451974Smsmith/********************************************************************************
134551974Smsmith * Permanently discard a command buffer.
134651974Smsmith */
134751974Smsmithstatic void
134851974Smsmithamr_freecmd(struct amr_command *ac)
134951974Smsmith{
135051974Smsmith    struct amr_softc	*sc = ac->ac_sc;
135151974Smsmith
135251974Smsmith    debug("called");
135351974Smsmith
135451974Smsmith    bus_dmamap_destroy(sc->amr_buffer_dmat, ac->ac_dmamap);
135551974Smsmith    free(ac, M_DEVBUF);
135651974Smsmith}
135751974Smsmith
135851974Smsmith/********************************************************************************
135951974Smsmith ********************************************************************************
136051974Smsmith                                                         Interface-specific Shims
136151974Smsmith ********************************************************************************
136251974Smsmith ********************************************************************************/
136351974Smsmith
136451974Smsmith/********************************************************************************
136551974Smsmith * Tell the controller that the mailbox contains a valid command
136651974Smsmith */
136751974Smsmithstatic void
136851974Smsmithamr_quartz_submit_command(struct amr_softc *sc)
136951974Smsmith{
137051974Smsmith    debug("called");
137151974Smsmith
137251974Smsmith    sc->amr_mailbox->mb_poll = 0;
137351974Smsmith    sc->amr_mailbox->mb_ack = 0;
137451974Smsmith    /* XXX write barrier? */
137551974Smsmith    while(AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
137651974Smsmith	;				/* XXX aiee! what if it dies? */
137751974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
137851974Smsmith}
137951974Smsmith
138051974Smsmithstatic void
138151974Smsmithamr_std_submit_command(struct amr_softc *sc)
138251974Smsmith{
138351974Smsmith    debug("called");
138451974Smsmith
138551974Smsmith    /* XXX write barrier? */
138651974Smsmith    while (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
138751974Smsmith	;				/* XXX aiee! what if it dies? */
138851974Smsmith    AMR_SPOST_COMMAND(sc);
138951974Smsmith}
139051974Smsmith
139151974Smsmith/********************************************************************************
139251974Smsmith * Claim any work that the controller has completed; acknowledge completion,
139351974Smsmith * save details of the completion in (mbsave)
139451974Smsmith */
139551974Smsmithstatic int
139651974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
139751974Smsmith{
139851974Smsmith    int		s, worked;
139951974Smsmith    u_int32_t	outd;
140051974Smsmith
140151974Smsmith    debug("called");
140251974Smsmith
140351974Smsmith    worked = 0;
140451974Smsmith    s = splbio();
140551974Smsmith
140651974Smsmith    /* work waiting for us? */
140751974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
140851974Smsmith	AMR_QPUT_ODB(sc, AMR_QODB_READY);
140951974Smsmith
141051974Smsmith	/* save mailbox, which contains a list of completed commands */
141151974Smsmith	/* XXX read barrier? */
141251974Smsmith	bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave));
141351974Smsmith
141451974Smsmith	/* acknowledge that we have the commands */
141551974Smsmith	AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
141651974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
141751974Smsmith	    ;				/* XXX aiee! what if it dies? */
141851974Smsmith	worked = 1;			/* got some work */
141951974Smsmith    }
142051974Smsmith
142151974Smsmith    splx(s);
142251974Smsmith    return(worked);
142351974Smsmith}
142451974Smsmith
142551974Smsmithstatic int
142651974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
142751974Smsmith{
142851974Smsmith    int		s, worked;
142951974Smsmith    u_int8_t	istat;
143051974Smsmith
143151974Smsmith    debug("called");
143251974Smsmith
143351974Smsmith    worked = 0;
143451974Smsmith    s = splbio();
143551974Smsmith
143651974Smsmith    /* check for valid interrupt status */
143751974Smsmith    istat = AMR_SGET_ISTAT(sc);
143851974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
143951974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
144051974Smsmith
144151974Smsmith	/* save mailbox, which contains a list of completed commands */
144251974Smsmith	/* XXX read barrier? */
144351974Smsmith	bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave));
144451974Smsmith
144551974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
144651974Smsmith	worked = 1;
144751974Smsmith    }
144851974Smsmith
144951974Smsmith    splx(s);
145051974Smsmith    return(worked);
145151974Smsmith}
145251974Smsmith
145351974Smsmith/********************************************************************************
145451974Smsmith * Notify the controller of the mailbox location.
145551974Smsmith */
145651974Smsmithstatic void
145751974Smsmithamr_quartz_attach_mailbox(struct amr_softc *sc)
145851974Smsmith{
145951974Smsmith    /* Quartz is given the mailbox location when a command is submitted */
146051974Smsmith}
146151974Smsmith
146251974Smsmithstatic void
146351974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
146451974Smsmith{
146551974Smsmith
146651974Smsmith    /* program the mailbox physical address */
146751974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
146851974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
146951974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
147051974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
147151974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
147251974Smsmith
147351974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
147451974Smsmith    AMR_SACK_INTERRUPT(sc);
147551974Smsmith    AMR_SENABLE_INTR(sc);
147651974Smsmith}
147751974Smsmith
147851974Smsmith/********************************************************************************
147951974Smsmith ********************************************************************************
148051974Smsmith                                                                        Debugging
148151974Smsmith ********************************************************************************
148251974Smsmith ********************************************************************************/
148351974Smsmith
148451974Smsmith/********************************************************************************
148551974Smsmith * Print the command (ac) in human-readable format
148651974Smsmith */
148751974Smsmithstatic void
148851974Smsmithamr_printcommand(struct amr_command *ac)
148951974Smsmith{
149051974Smsmith    struct amr_softc	*sc = ac->ac_sc;
149151974Smsmith    struct amr_sgentry	*sg;
149251974Smsmith    int			i;
149351974Smsmith
149451974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
149551974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
149651974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
149751974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
149851974Smsmith    device_printf(sc->amr_dev, "virtaddr %p  length %d\n", ac->ac_data, ac->ac_length);
149951974Smsmith    device_printf(sc->amr_dev, "physaddr %08x  nsg %d\n",
150051974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
150151974Smsmith
150251974Smsmith    /* get base address of s/g table */
150351974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
150451974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
150551974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
150651974Smsmith}
1507