amr.c revision 59249
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 59249 2000-04-15 05:54:02Z phk $
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/*
11158883Smsmith * Status monitoring
11258883Smsmith */
11358883Smsmithstatic void			amr_periodic(void *data);
11458883Smsmith
11558883Smsmith/*
11651974Smsmith * Interface-specific shims
11751974Smsmith */
11851974Smsmithstatic void			amr_quartz_submit_command(struct amr_softc *sc);
11951974Smsmithstatic int			amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
12051974Smsmithstatic void			amr_quartz_attach_mailbox(struct amr_softc *sc);
12151974Smsmith
12251974Smsmithstatic void			amr_std_submit_command(struct amr_softc *sc);
12351974Smsmithstatic int			amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
12451974Smsmithstatic void			amr_std_attach_mailbox(struct amr_softc *sc);
12551974Smsmith
12651974Smsmith/*
12751974Smsmith * Debugging
12851974Smsmith */
12951974Smsmithstatic void			amr_printcommand(struct amr_command *ac);
13051974Smsmith
13151974Smsmith/********************************************************************************
13251974Smsmith ********************************************************************************
13351974Smsmith                                                                Public Interfaces
13451974Smsmith ********************************************************************************
13551974Smsmith ********************************************************************************/
13651974Smsmith
13751974Smsmith/********************************************************************************
13851974Smsmith * Free all of the resources associated with (sc)
13951974Smsmith *
14051974Smsmith * Should not be called if the controller is active.
14151974Smsmith */
14251974Smsmithvoid
14351974Smsmithamr_free(struct amr_softc *sc)
14451974Smsmith{
14551974Smsmith    struct amr_command	*ac;
14651974Smsmith    u_int8_t		*p;
14751974Smsmith
14851974Smsmith    debug("called");
14951974Smsmith
15058883Smsmith    /* cancel status timeout */
15158883Smsmith    untimeout(amr_periodic, sc, sc->amr_timeout);
15251974Smsmith
15351974Smsmith    /* throw away any command buffers */
15451974Smsmith    while ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL) {
15551974Smsmith	TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
15651974Smsmith	amr_freecmd(ac);
15751974Smsmith    }
15851974Smsmith
15951974Smsmith    /* destroy data-transfer DMA tag */
16051974Smsmith    if (sc->amr_buffer_dmat)
16151974Smsmith	bus_dma_tag_destroy(sc->amr_buffer_dmat);
16251974Smsmith
16351974Smsmith    /* free and destroy DMA memory and tag for s/g lists */
16451974Smsmith    if (sc->amr_sgtable)
16551974Smsmith	bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
16651974Smsmith    if (sc->amr_sg_dmat)
16751974Smsmith	bus_dma_tag_destroy(sc->amr_sg_dmat);
16851974Smsmith
16951974Smsmith    /* free and destroy DMA memory and tag for mailbox */
17051974Smsmith    if (sc->amr_mailbox) {
17151974Smsmith	p = (u_int8_t *)sc->amr_mailbox;
17251974Smsmith	bus_dmamem_free(sc->amr_sg_dmat, p - 16, sc->amr_sg_dmamap);
17351974Smsmith    }
17451974Smsmith    if (sc->amr_sg_dmat)
17551974Smsmith	bus_dma_tag_destroy(sc->amr_sg_dmat);
17651974Smsmith
17751974Smsmith    /* disconnect the interrupt handler */
17851974Smsmith    if (sc->amr_intr)
17951974Smsmith	bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr);
18051974Smsmith    if (sc->amr_irq != NULL)
18151974Smsmith	bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq);
18251974Smsmith
18351974Smsmith    /* destroy the parent DMA tag */
18451974Smsmith    if (sc->amr_parent_dmat)
18551974Smsmith	bus_dma_tag_destroy(sc->amr_parent_dmat);
18651974Smsmith
18751974Smsmith    /* release the register window mapping */
18851974Smsmith    if (sc->amr_reg != NULL)
18951974Smsmith	bus_release_resource(sc->amr_dev,
19051974Smsmith			     (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT,
19151974Smsmith			     AMR_CFG_BASE, sc->amr_reg);
19251974Smsmith}
19351974Smsmith
19451974Smsmith/********************************************************************************
19551974Smsmith * Allocate and map the scatter/gather table in bus space.
19651974Smsmith */
19751974Smsmithstatic void
19851974Smsmithamr_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
19951974Smsmith{
20051974Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
20151974Smsmith
20251974Smsmith    debug("called");
20351974Smsmith
20451974Smsmith    /* save base of s/g table's address in bus space */
20551974Smsmith    sc->amr_sgbusaddr = segs->ds_addr;
20651974Smsmith}
20751974Smsmith
20851974Smsmithstatic int
20951974Smsmithamr_sglist_map(struct amr_softc *sc)
21051974Smsmith{
21151974Smsmith    size_t	segsize;
21251974Smsmith    int		error;
21351974Smsmith
21451974Smsmith    debug("called");
21551974Smsmith
21651974Smsmith    /* destroy any existing mappings */
21751974Smsmith    if (sc->amr_sgtable)
21851974Smsmith	bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap);
21951974Smsmith    if (sc->amr_sg_dmat)
22051974Smsmith	bus_dma_tag_destroy(sc->amr_sg_dmat);
22151974Smsmith
22251974Smsmith    /*
22351974Smsmith     * Create a single tag describing a region large enough to hold all of
22451974Smsmith     * the s/g lists we will need.
22551974Smsmith     */
22651974Smsmith    segsize = sizeof(struct amr_sgentry) * AMR_NSEG * sc->amr_maxio;
22751974Smsmith    error = bus_dma_tag_create(sc->amr_parent_dmat, 	/* parent */
22851974Smsmith			       1, 0, 			/* alignment, boundary */
22951974Smsmith			       BUS_SPACE_MAXADDR,	/* lowaddr */
23051974Smsmith			       BUS_SPACE_MAXADDR, 	/* highaddr */
23151974Smsmith			       NULL, NULL, 		/* filter, filterarg */
23251974Smsmith			       segsize, 1,		/* maxsize, nsegments */
23351974Smsmith			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
23451974Smsmith			       0,			/* flags */
23551974Smsmith			       &sc->amr_sg_dmat);
23651974Smsmith    if (error != 0) {
23751974Smsmith	device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n");
23851974Smsmith	return(ENOMEM);
23951974Smsmith    }
24051974Smsmith
24151974Smsmith    /*
24251974Smsmith     * Allocate enough s/g maps for all commands and permanently map them into
24351974Smsmith     * controller-visible space.
24451974Smsmith     *
24551974Smsmith     * XXX this assumes we can get enough space for all the s/g maps in one
24651974Smsmith     * contiguous slab.  We may need to switch to a more complex arrangement where
24751974Smsmith     * we allocate in smaller chunks and keep a lookup table from slot to bus address.
24851974Smsmith     */
24951974Smsmith    error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap);
25051974Smsmith    if (error) {
25151974Smsmith	device_printf(sc->amr_dev, "can't allocate s/g table\n");
25251974Smsmith	return(ENOMEM);
25351974Smsmith    }
25451974Smsmith    bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_dma_map_sg, sc, 0);
25551974Smsmith    return(0);
25651974Smsmith}
25751974Smsmith
25851974Smsmith/********************************************************************************
25951974Smsmith * Allocate and set up mailbox areas for the controller (sc)
26051974Smsmith *
26151974Smsmith * The basic mailbox structure should be 16-byte aligned.  This means that the
26251974Smsmith * mailbox64 structure has 4 bytes hanging off the bottom.
26351974Smsmith */
26451974Smsmithstatic void
26551974Smsmithamr_map_mailbox(void *arg, bus_dma_segment_t *segs, int nseg, int error)
26651974Smsmith{
26751974Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
26851974Smsmith
26951974Smsmith    debug("called");
27051974Smsmith
27151974Smsmith    /* save phsyical base of the basic mailbox structure */
27251974Smsmith    sc->amr_mailboxphys = segs->ds_addr + 16;
27351974Smsmith}
27451974Smsmith
27551974Smsmithstatic int
27651974Smsmithamr_setup_mbox(struct amr_softc *sc)
27751974Smsmith{
27851974Smsmith    int		error;
27951974Smsmith    u_int8_t	*p;
28051974Smsmith
28151974Smsmith    debug("called");
28251974Smsmith
28351974Smsmith    /*
28451974Smsmith     * Create a single tag describing a region large enough to hold the entire
28551974Smsmith     * mailbox.
28651974Smsmith     */
28751974Smsmith    error = bus_dma_tag_create(sc->amr_parent_dmat,	/* parent */
28851974Smsmith			       16, 0,			/* alignment, boundary */
28951974Smsmith			       BUS_SPACE_MAXADDR,	/* lowaddr */
29051974Smsmith			       BUS_SPACE_MAXADDR,	/* highaddr */
29151974Smsmith			       NULL, NULL,		/* filter, filterarg */
29251974Smsmith			       sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */
29351974Smsmith			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
29451974Smsmith			       0,			/* flags */
29551974Smsmith			       &sc->amr_mailbox_dmat);
29651974Smsmith    if (error != 0) {
29751974Smsmith	device_printf(sc->amr_dev, "can't allocate mailbox tag\n");
29851974Smsmith	return(ENOMEM);
29951974Smsmith    }
30051974Smsmith
30151974Smsmith    /*
30251974Smsmith     * Allocate the mailbox structure and permanently map it into
30351974Smsmith     * controller-visible space.
30451974Smsmith     */
30551974Smsmith    error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT,
30651974Smsmith			     &sc->amr_mailbox_dmamap);
30751974Smsmith    if (error) {
30851974Smsmith	device_printf(sc->amr_dev, "can't allocate mailbox memory\n");
30951974Smsmith	return(ENOMEM);
31051974Smsmith    }
31151974Smsmith    bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p,
31251974Smsmith		    sizeof(struct amr_mailbox64), amr_map_mailbox, sc, 0);
31351974Smsmith    /*
31451974Smsmith     * Conventional mailbox is inside the mailbox64 region.
31551974Smsmith     */
31651974Smsmith    bzero(p, sizeof(struct amr_mailbox64));
31751974Smsmith    sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12);
31851974Smsmith    sc->amr_mailbox = (struct amr_mailbox *)(p + 16);
31951974Smsmith
32051974Smsmith    if (sc->amr_type == AMR_TYPE_STD) {
32151974Smsmith	/* XXX we have to tell the controller where we put it */
32251974Smsmith    }
32351974Smsmith    return(0);
32451974Smsmith}
32551974Smsmith
32651974Smsmith
32751974Smsmith/********************************************************************************
32851974Smsmith * Initialise the controller and softc.
32951974Smsmith */
33051974Smsmithint
33151974Smsmithamr_attach(struct amr_softc *sc)
33251974Smsmith{
33351974Smsmith    int			rid, error;
33451974Smsmith
33551974Smsmith    /*
33651974Smsmith     * Initialise per-controller queues.
33751974Smsmith     */
33852543Smsmith    TAILQ_INIT(&sc->amr_work);
33951974Smsmith    TAILQ_INIT(&sc->amr_freecmds);
34059249Sphk    bioq_init(&sc->amr_bioq);
34151974Smsmith
34251974Smsmith    /*
34351974Smsmith     * Configure for this controller type.
34451974Smsmith     */
34551974Smsmith    if (sc->amr_type == AMR_TYPE_QUARTZ) {
34651974Smsmith	sc->amr_submit_command = amr_quartz_submit_command;
34751974Smsmith	sc->amr_get_work       = amr_quartz_get_work;
34851974Smsmith	sc->amr_attach_mailbox = amr_quartz_attach_mailbox;
34951974Smsmith    } else {
35051974Smsmith	sc->amr_submit_command = amr_std_submit_command;
35151974Smsmith	sc->amr_get_work       = amr_std_get_work;
35251974Smsmith	sc->amr_attach_mailbox = amr_std_attach_mailbox;
35351974Smsmith    }
35451974Smsmith
35551974Smsmith    /*
35651974Smsmith     * Allocate and connect our interrupt.
35751974Smsmith     */
35851974Smsmith    rid = 0;
35951974Smsmith    sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
36051974Smsmith    if (sc->amr_irq == NULL) {
36151974Smsmith        device_printf(sc->amr_dev, "couldn't allocate interrupt\n");
36251974Smsmith        amr_free(sc);
36351974Smsmith        return(ENXIO);
36451974Smsmith    }
36551974Smsmith    error = bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO,  amr_intr, sc, &sc->amr_intr);
36651974Smsmith    if (error) {
36751974Smsmith        device_printf(sc->amr_dev, "couldn't set up interrupt\n");
36851974Smsmith        amr_free(sc);
36951974Smsmith        return(ENXIO);
37051974Smsmith    }
37151974Smsmith
37251974Smsmith    /*
37351974Smsmith     * Create DMA tag for mapping buffers into controller-addressable space.
37451974Smsmith     */
37551974Smsmith    error = bus_dma_tag_create(sc->amr_parent_dmat,     /* parent */
37651974Smsmith                               1, 0,                    /* alignment, boundary */
37751974Smsmith                               BUS_SPACE_MAXADDR,       /* lowaddr */
37851974Smsmith                               BUS_SPACE_MAXADDR,       /* highaddr */
37951974Smsmith                               NULL, NULL,              /* filter, filterarg */
38051974Smsmith                               MAXBSIZE, AMR_NSEG,      /* maxsize, nsegments */
38151974Smsmith                               BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
38251974Smsmith                               0,                       /* flags */
38351974Smsmith                               &sc->amr_buffer_dmat);
38451974Smsmith    if (error != 0) {
38551974Smsmith        device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
38651974Smsmith        return(ENOMEM);
38751974Smsmith    }
38851974Smsmith
38951974Smsmith    /*
39051974Smsmith     * Allocate and set up mailbox in a bus-visible fashion, attach to controller.
39151974Smsmith     */
39251974Smsmith    if ((error = amr_setup_mbox(sc)) != 0)
39351974Smsmith	return(error);
39451974Smsmith    sc->amr_attach_mailbox(sc);
39551974Smsmith
39651974Smsmith    /*
39751974Smsmith     * Build a temporary set of scatter/gather buffers.
39851974Smsmith     */
39951974Smsmith    sc->amr_maxio = 2;
40051974Smsmith    if (amr_sglist_map(sc))
40151974Smsmith	return(ENXIO);
40251974Smsmith
40351974Smsmith    /*
40451974Smsmith     * Quiz controller for features and limits.
40551974Smsmith     */
40651974Smsmith    if (amr_query_controller(sc))
40751974Smsmith	return(ENXIO);
40851974Smsmith
40951974Smsmith    /*
41051974Smsmith     * Rebuild the scatter/gather buffers now we know how many we need.
41151974Smsmith     */
41251974Smsmith    if (amr_sglist_map(sc))
41351974Smsmith	return(ENXIO);
41451974Smsmith
41558883Smsmith    /*
41658883Smsmith     * Start the timeout routine.
41758883Smsmith     */
41858883Smsmith    sc->amr_timeout = timeout(amr_periodic, sc, hz);
41958883Smsmith
42051974Smsmith    return(0);
42151974Smsmith}
42251974Smsmith
42351974Smsmith/********************************************************************************
42451974Smsmith * Locate disk resources and attach children to them.
42551974Smsmith */
42651974Smsmithvoid
42751974Smsmithamr_startup(struct amr_softc *sc)
42851974Smsmith{
42951974Smsmith    struct amr_logdrive	*dr;
43051974Smsmith    int			i, error;
43151974Smsmith
43251974Smsmith    debug("called");
43351974Smsmith
43451974Smsmith    /* get up-to-date drive information */
43551974Smsmith    if (amr_query_controller(sc)) {
43651974Smsmith	device_printf(sc->amr_dev, "couldn't scan controller for drives\n");
43751974Smsmith	return;
43851974Smsmith    }
43951974Smsmith
44051974Smsmith    /* iterate over available drives */
44151974Smsmith    for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
44251974Smsmith	/* are we already attached to this drive? */
44351974Smsmith	if (dr->al_disk == 0) {
44451974Smsmith	    /* generate geometry information */
44551974Smsmith	    if (dr->al_size > 0x200000) {	/* extended translation? */
44651974Smsmith		dr->al_heads = 255;
44751974Smsmith		dr->al_sectors = 63;
44851974Smsmith	    } else {
44951974Smsmith		dr->al_heads = 64;
45051974Smsmith		dr->al_sectors = 32;
45151974Smsmith	    }
45251974Smsmith	    dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
45351974Smsmith
45454073Smdodd	    dr->al_disk = device_add_child(sc->amr_dev, NULL, -1);
45551974Smsmith	    if (dr->al_disk == 0)
45651974Smsmith		device_printf(sc->amr_dev, "device_add_child failed\n");
45754073Smdodd	    device_set_ivars(dr->al_disk, dr);
45851974Smsmith	}
45951974Smsmith    }
46051974Smsmith
46151974Smsmith    if ((error = bus_generic_attach(sc->amr_dev)) != 0)
46251974Smsmith	device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
46351974Smsmith
46451974Smsmith    /* mark controller back up */
46551974Smsmith    sc->amr_state &= ~AMR_STATE_SHUTDOWN;
46651974Smsmith
46751974Smsmith    /* interrupts will be enabled before we do anything more */
46851974Smsmith    sc->amr_state |= AMR_STATE_INTEN;
46951974Smsmith}
47051974Smsmith
47151974Smsmith/********************************************************************************
47251974Smsmith * Disconnect from the controller completely, in preparation for unload.
47351974Smsmith */
47451974Smsmithint
47551974Smsmithamr_detach(device_t dev)
47651974Smsmith{
47751974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
47851974Smsmith    int			error;
47951974Smsmith
48051974Smsmith    debug("called");
48151974Smsmith
48251974Smsmith    if (sc->amr_state & AMR_STATE_OPEN)
48351974Smsmith	return(EBUSY);
48451974Smsmith
48551974Smsmith    if ((error = amr_shutdown(dev)))
48651974Smsmith	return(error);
48751974Smsmith
48851974Smsmith    amr_free(sc);
48951974Smsmith
49051974Smsmith    /*
49151974Smsmith     * Deregister the control device on last detach.
49251974Smsmith     */
49351974Smsmith    if (--cdev_registered == 0)
49451974Smsmith	cdevsw_remove(&amr_cdevsw);
49551974Smsmith
49651974Smsmith    return(0);
49751974Smsmith}
49851974Smsmith
49951974Smsmith/********************************************************************************
50051974Smsmith * Bring the controller down to a dormant state and detach all child devices.
50151974Smsmith *
50251974Smsmith * This function is called before detach, system shutdown, or before performing
50351974Smsmith * an operation which may add or delete system disks.  (Call amr_startup to
50451974Smsmith * resume normal operation.)
50551974Smsmith *
50659249Sphk * Note that we can assume that the bioq on the controller is empty, as we won't
50751974Smsmith * allow shutdown if any device is open.
50851974Smsmith */
50951974Smsmithint
51051974Smsmithamr_shutdown(device_t dev)
51151974Smsmith{
51251974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
51351974Smsmith    struct amrd_softc	*ad;
51451974Smsmith    int			i, s, error;
51551974Smsmith
51651974Smsmith    debug("called");
51751974Smsmith
51851974Smsmith    s = splbio();
51951974Smsmith    error = 0;
52051974Smsmith
52151974Smsmith    /* assume we're going to shut down */
52251974Smsmith    sc->amr_state |= AMR_STATE_SHUTDOWN;
52351974Smsmith    for (i = 0; i < AMR_MAXLD; i++) {
52451974Smsmith	if (sc->amr_drive[i].al_disk != 0) {
52551974Smsmith	    ad = device_get_softc(sc->amr_drive[i].al_disk);
52651974Smsmith	    if (ad->amrd_flags & AMRD_OPEN) {		/* drive is mounted, abort shutdown */
52751974Smsmith		sc->amr_state &= ~AMR_STATE_SHUTDOWN;
52851974Smsmith		device_printf(sc->amr_drive[i].al_disk, "still open, can't shutdown\n");
52951974Smsmith		error = EBUSY;
53051974Smsmith		goto out;
53151974Smsmith	    }
53251974Smsmith	}
53351974Smsmith    }
53451974Smsmith
53551974Smsmith    /* flush controller */
53651974Smsmith    device_printf(sc->amr_dev, "flushing cache...");
53751974Smsmith    if (amr_flush(sc)) {
53851974Smsmith	printf("failed\n");
53951974Smsmith    } else {
54051974Smsmith	printf("done\n");
54151974Smsmith    }
54251974Smsmith
54351974Smsmith    /* delete all our child devices */
54451974Smsmith    for (i = 0; i < AMR_MAXLD; i++) {
54551974Smsmith	if (sc->amr_drive[i].al_disk != 0) {
54651974Smsmith	    if ((error = device_delete_child(sc->amr_dev, sc->amr_drive[i].al_disk)) != 0)
54751974Smsmith		goto out;
54851974Smsmith	    sc->amr_drive[i].al_disk = 0;
54951974Smsmith	}
55051974Smsmith    }
55151974Smsmith
55251974Smsmith out:
55351974Smsmith    splx(s);
55451974Smsmith    return(error);
55551974Smsmith}
55651974Smsmith
55751974Smsmith/********************************************************************************
55851974Smsmith * Bring the controller to a quiescent state, ready for system suspend.
55951974Smsmith */
56051974Smsmithint
56151974Smsmithamr_suspend(device_t dev)
56251974Smsmith{
56351974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
56451974Smsmith
56551974Smsmith    debug("called");
56651974Smsmith
56751974Smsmith    sc->amr_state |= AMR_STATE_SUSPEND;
56851974Smsmith
56951974Smsmith    /* flush controller */
57051974Smsmith    device_printf(sc->amr_dev, "flushing cache...");
57151974Smsmith    printf("%s\n", amr_flush(sc) ? "failed" : "done");
57251974Smsmith
57351974Smsmith    return(0);
57451974Smsmith}
57551974Smsmith
57651974Smsmith/********************************************************************************
57751974Smsmith * Bring the controller back to a state ready for operation.
57851974Smsmith */
57951974Smsmithint
58051974Smsmithamr_resume(device_t dev)
58151974Smsmith{
58251974Smsmith    struct amr_softc	*sc = device_get_softc(dev);
58351974Smsmith
58451974Smsmith    debug("called");
58551974Smsmith
58651974Smsmith    sc->amr_state &= ~AMR_STATE_SUSPEND;
58751974Smsmith
58851974Smsmith    return(0);
58951974Smsmith}
59051974Smsmith
59151974Smsmith/*******************************************************************************
59251974Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy
59351974Smsmith * status.
59451974Smsmith */
59551974Smsmithvoid
59651974Smsmithamr_intr(void *arg)
59751974Smsmith{
59851974Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
59951974Smsmith
60058883Smsmith    debug("called");
60151974Smsmith
60258883Smsmith    /* collect finished commands, queue anything waiting */
60358883Smsmith    amr_done(sc);
60451974Smsmith};
60551974Smsmith
60651974Smsmith/*******************************************************************************
60751974Smsmith * Receive a buf structure from a child device and queue it on a particular
60851974Smsmith * disk resource, then poke the disk resource to start as much work as it can.
60951974Smsmith */
61051974Smsmithint
61159249Sphkamr_submit_buf(struct amr_softc *sc, struct bio *bp)
61251974Smsmith{
61352543Smsmith    int		s;
61452543Smsmith
61551974Smsmith    debug("called");
61651974Smsmith
61752543Smsmith    s = splbio();
61859249Sphk    bioq_insert_tail(&sc->amr_bioq, bp);
61952543Smsmith    splx(s);
62051974Smsmith    sc->amr_waitbufs++;
62151974Smsmith    amr_startio(sc);
62251974Smsmith    return(0);
62351974Smsmith}
62451974Smsmith
62551974Smsmith/********************************************************************************
62651974Smsmith * Accept an open operation on the control device.
62751974Smsmith */
62851974Smsmithint
62951974Smsmithamr_open(dev_t dev, int flags, int fmt, struct proc *p)
63051974Smsmith{
63151974Smsmith    int			unit = minor(dev);
63251974Smsmith    struct amr_softc	*sc = devclass_get_softc(amr_devclass, unit);
63351974Smsmith
63451974Smsmith    sc->amr_state |= AMR_STATE_OPEN;
63551974Smsmith    return(0);
63651974Smsmith}
63751974Smsmith
63851974Smsmith/********************************************************************************
63951974Smsmith * Accept the last close on the control device.
64051974Smsmith */
64151974Smsmithint
64251974Smsmithamr_close(dev_t dev, int flags, int fmt, struct proc *p)
64351974Smsmith{
64451974Smsmith    int			unit = minor(dev);
64551974Smsmith    struct amr_softc	*sc = devclass_get_softc(amr_devclass, unit);
64651974Smsmith
64751974Smsmith    sc->amr_state &= ~AMR_STATE_OPEN;
64851974Smsmith    return (0);
64951974Smsmith}
65051974Smsmith
65151974Smsmith/********************************************************************************
65251974Smsmith * Handle controller-specific control operations.
65351974Smsmith */
65451974Smsmithint
65551974Smsmithamr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
65651974Smsmith{
65751974Smsmith
65851974Smsmith    switch(cmd) {
65951974Smsmith    default:
66051974Smsmith	return(ENOTTY);
66151974Smsmith    }
66251974Smsmith}
66351974Smsmith
66451974Smsmith/********************************************************************************
66551974Smsmith * Handle operations requested by a drive connected to this controller.
66651974Smsmith */
66751974Smsmithint
66851974Smsmithamr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd,
66951974Smsmith		 caddr_t addr, int32_t flag, struct proc *p)
67051974Smsmith{
67151974Smsmith    return(ENOTTY);
67251974Smsmith}
67351974Smsmith
67451974Smsmith/********************************************************************************
67551974Smsmith ********************************************************************************
67658883Smsmith                                                                Status Monitoring
67758883Smsmith ********************************************************************************
67858883Smsmith ********************************************************************************/
67958883Smsmith
68058883Smsmith/********************************************************************************
68158883Smsmith * Perform a periodic check of the controller status
68258883Smsmith */
68358883Smsmithstatic void
68458883Smsmithamr_periodic(void *data)
68558883Smsmith{
68658883Smsmith    struct amr_softc	*sc = (struct amr_softc *)data;
68758883Smsmith    int			s, i;
68858883Smsmith
68958883Smsmith    debug("called");
69058883Smsmith
69158883Smsmith
69258883Smsmith    /*
69358883Smsmith     * Check for commands that are massively late.  This will need to be
69458883Smsmith     * revisited if/when we deal with eg. device format commands.
69558883Smsmith     * The 30 second value is entirely arbitrary.
69658883Smsmith     */
69758883Smsmith    s = splbio();
69858883Smsmith    if (sc->amr_busycmdcount > 0) {
69958883Smsmith	for (i = 0; i < AMR_MAXCMD; i++) {
70058883Smsmith	    /*
70158883Smsmith	     * If the command has been busy for more than 30 seconds, declare it
70258883Smsmith	     * wedged and retire it with an error.
70358883Smsmith	     */
70458883Smsmith	    if ((sc->amr_busycmd[i] != NULL) &&
70558883Smsmith		(sc->amr_busycmd[i]->ac_status == AMR_STATUS_BUSY) &&
70658883Smsmith		((sc->amr_busycmd[i]->ac_stamp + 30) < time_second)) {
70758883Smsmith		device_printf(sc->amr_dev, "command %d wedged after 30 seconds\n", i);
70858883Smsmith		sc->amr_busycmd[i]->ac_status = AMR_STATUS_WEDGED;
70958883Smsmith		amr_completeio(sc->amr_busycmd[i]);
71058883Smsmith	    }
71158883Smsmith	}
71258883Smsmith    }
71358883Smsmith    splx(s);
71458883Smsmith
71558883Smsmith    /* reschedule */
71658883Smsmith    sc->amr_timeout = timeout(amr_periodic, sc, hz);
71758883Smsmith}
71858883Smsmith
71958883Smsmith
72058883Smsmith/********************************************************************************
72158883Smsmith ********************************************************************************
72251974Smsmith                                                                 Command Wrappers
72351974Smsmith ********************************************************************************
72451974Smsmith ********************************************************************************/
72551974Smsmith
72651974Smsmith/********************************************************************************
72751974Smsmith * Interrogate the controller for the operational parameters we require.
72851974Smsmith */
72951974Smsmithstatic int
73051974Smsmithamr_query_controller(struct amr_softc *sc)
73151974Smsmith{
73251974Smsmith    void	*buf;
73351974Smsmith    int		i;
73451974Smsmith
73551974Smsmith    /* try to issue an ENQUIRY3 command */
73651974Smsmith    if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
73751974Smsmith			   AMR_CONFIG_ENQ3_SOLICITED_FULL)) == NULL) {
73851974Smsmith
73951974Smsmith	struct amr_enquiry	*ae;
74051974Smsmith
74151974Smsmith	/* failed, try the old ENQUIRY command */
74251974Smsmith	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
74351974Smsmith	    device_printf(sc->amr_dev, "could not obtain configuration data from controller\n");
74451974Smsmith	    return(1);
74551974Smsmith	}
74651974Smsmith	/* first-time enquiry? */
74751974Smsmith	if (sc->amr_maxdrives == 0) {
74852784Smsmith	    device_printf(sc->amr_dev, "firmware %.4s bios %.4s  %dMB memory\n",
74951974Smsmith			  ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
75052784Smsmith			  ae->ae_adapter.aa_memorysize);
75151974Smsmith	}
75251974Smsmith	sc->amr_maxdrives = 8;
75358883Smsmith
75458883Smsmith	/*
75558883Smsmith	 * Cap the maximum number of outstanding I/Os.  AMI's Linux driver doesn't trust
75658883Smsmith	 * the controller's reported value, and lockups have been seen when we do.
75758883Smsmith	 */
75858883Smsmith	sc->amr_maxio = imin(ae->ae_adapter.aa_maxio, AMR_LIMITCMD);
75958883Smsmith
76051974Smsmith	for (i = 0; i < ae->ae_ldrv.al_numdrives; i++) {
76151974Smsmith	    sc->amr_drive[i].al_size = ae->ae_ldrv.al_size[i];
76251974Smsmith	    sc->amr_drive[i].al_state = ae->ae_ldrv.al_state[i];
76351974Smsmith	    sc->amr_drive[i].al_properties = ae->ae_ldrv.al_properties[i];
76451974Smsmith	    debug("  drive %d: %d state %x properties %x\n", i, sc->amr_drive[i].al_size,
76551974Smsmith		  sc->amr_drive[i].al_state, sc->amr_drive[i].al_properties);
76651974Smsmith	}
76751974Smsmith	for (; i < AMR_MAXLD; i++)
76851974Smsmith	    sc->amr_drive[i].al_size = 0xffffffff;
76951974Smsmith	free(ae, M_DEVBUF);
77051974Smsmith    } else {
77158883Smsmith	/*
77258883Smsmith	 * The "40LD" (40 logical drive support) firmware is mentioned in the Linux
77358883Smsmith	 * driver, but no adapters from AMI appear to support it.
77458883Smsmith	 */
77551974Smsmith	free(buf, M_DEVBUF);
77651974Smsmith	sc->amr_maxdrives = 40;
77751974Smsmith
77851974Smsmith	/* get static product info */
77951974Smsmith	if ((buf = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODINFO, 0)) == NULL) {
78051974Smsmith	    device_printf(sc->amr_dev, "controller supports 40ld but CONFIG_PRODINFO failed\n");
78151974Smsmith	    return(1);
78251974Smsmith	}
78351974Smsmith	free(buf, M_DEVBUF);
78451974Smsmith	device_printf(sc->amr_dev, "40LD firmware unsupported; send controller to msmith@freebsd.org\n");
78551974Smsmith	return(1);
78651974Smsmith    }
78751974Smsmith    return(0);
78851974Smsmith}
78951974Smsmith
79051974Smsmith/********************************************************************************
79151974Smsmith * Run a generic enquiry-style command.
79251974Smsmith */
79351974Smsmithstatic void *
79451974Smsmithamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
79551974Smsmith{
79651974Smsmith    struct amr_command	*ac;
79751974Smsmith    void		*result;
79851974Smsmith    u_int8_t		*mbox;
79951974Smsmith    int			error;
80051974Smsmith
80151974Smsmith    debug("called");
80251974Smsmith
80351974Smsmith    error = 1;
80451974Smsmith    result = NULL;
80551974Smsmith
80651974Smsmith    /* get ourselves a command buffer */
80751974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
80851974Smsmith	goto out;
80951974Smsmith    /* allocate the response structure */
81051974Smsmith    if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
81151974Smsmith	goto out;
81251974Smsmith    /* get a command slot */
81351974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
81451974Smsmith    if (amr_getslot(ac))
81551974Smsmith	goto out;
81651974Smsmith
81751974Smsmith    /* map the command so the controller can see it */
81851974Smsmith    ac->ac_data = result;
81951974Smsmith    ac->ac_length = bufsize;
82051974Smsmith    amr_mapcmd(ac);
82151974Smsmith
82251974Smsmith    /* build the command proper */
82351974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
82451974Smsmith    mbox[0] = cmd;
82551974Smsmith    mbox[2] = cmdsub;
82651974Smsmith    mbox[3] = cmdqual;
82751974Smsmith    ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
82851974Smsmith
82958883Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
83058883Smsmith    if (amr_poll_command(ac))
83151974Smsmith	goto out;
83251974Smsmith    error = ac->ac_status;
83351974Smsmith
83451974Smsmith out:
83551974Smsmith    if (ac != NULL)
83651974Smsmith	amr_releasecmd(ac);
83751974Smsmith    if ((error != 0) && (result != NULL)) {
83851974Smsmith	free(result, M_DEVBUF);
83951974Smsmith	result = NULL;
84051974Smsmith    }
84151974Smsmith    return(result);
84251974Smsmith}
84351974Smsmith
84451974Smsmith/********************************************************************************
84551974Smsmith * Flush the controller's internal cache, return status.
84651974Smsmith */
84751974Smsmithstatic int
84851974Smsmithamr_flush(struct amr_softc *sc)
84951974Smsmith{
85051974Smsmith    struct amr_command	*ac;
85151974Smsmith    int			error;
85251974Smsmith
85351974Smsmith    /* get ourselves a command buffer */
85451974Smsmith    error = 1;
85551974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
85651974Smsmith	goto out;
85751974Smsmith    /* get a command slot */
85851974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
85951974Smsmith    if (amr_getslot(ac))
86051974Smsmith	goto out;
86151974Smsmith
86251974Smsmith    /* build the command proper */
86351974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
86451974Smsmith
86558883Smsmith    /* we have to poll, as the system may be going down or otherwise damaged */
86658883Smsmith    if (amr_poll_command(ac))
86751974Smsmith	goto out;
86851974Smsmith    error = ac->ac_status;
86951974Smsmith
87051974Smsmith out:
87151974Smsmith    if (ac != NULL)
87251974Smsmith	amr_releasecmd(ac);
87351974Smsmith    return(error);
87451974Smsmith}
87551974Smsmith
87651974Smsmith/********************************************************************************
87751974Smsmith * Pull as much work off the softc's work queue as possible and give it to the
87851974Smsmith * controller.  Leave a couple of slots free for emergencies.
87951974Smsmith *
88052543Smsmith * We avoid running at splbio() whenever possible.
88151974Smsmith */
88251974Smsmithstatic void
88351974Smsmithamr_startio(struct amr_softc *sc)
88451974Smsmith{
88551974Smsmith    struct amr_command	*ac;
88651974Smsmith    struct amrd_softc	*amrd;
88759249Sphk    struct bio		*bp;
88851974Smsmith    int			blkcount;
88951974Smsmith    int			driveno;
89051974Smsmith    int			cmd;
89152543Smsmith    int			s;
89251974Smsmith
89358883Smsmith    /* avoid reentrancy */
89458883Smsmith    if (amr_lock_tas(sc, AMR_LOCK_STARTING))
89558883Smsmith	return;
89658883Smsmith
89751974Smsmith    /* spin until something prevents us from doing any work */
89852543Smsmith    s = splbio();
89951974Smsmith    for (;;) {
90051974Smsmith
90151974Smsmith	/* see if there's work to be done */
90259249Sphk	if ((bp = bioq_first(&sc->amr_bioq)) == NULL)
90351974Smsmith	    break;
90451974Smsmith	/* get a command */
90551974Smsmith	if ((ac = amr_alloccmd(sc)) == NULL)
90651974Smsmith	    break;
90751974Smsmith	/* get a slot for the command */
90851974Smsmith	if (amr_getslot(ac) != 0) {
90951974Smsmith	    amr_releasecmd(ac);
91051974Smsmith	    break;
91151974Smsmith	}
91251974Smsmith	/* get the buf containing our work */
91359249Sphk	bioq_remove(&sc->amr_bioq, bp);
91451974Smsmith	sc->amr_waitbufs--;
91552543Smsmith	splx(s);
91651974Smsmith
91751974Smsmith	/* connect the buf to the command */
91851974Smsmith	ac->ac_complete = amr_completeio;
91951974Smsmith	ac->ac_private = bp;
92059249Sphk	ac->ac_data = bp->bio_data;
92159249Sphk	ac->ac_length = bp->bio_bcount;
92259249Sphk	if (bp->bio_cmd == BIO_READ) {
92351974Smsmith	    ac->ac_flags |= AMR_CMD_DATAIN;
92451974Smsmith	    cmd = AMR_CMD_LREAD;
92551974Smsmith	} else {
92651974Smsmith	    ac->ac_flags |= AMR_CMD_DATAOUT;
92751974Smsmith	    cmd = AMR_CMD_LWRITE;
92851974Smsmith	}
92951974Smsmith
93051974Smsmith	/* map the command so the controller can work with it */
93151974Smsmith	amr_mapcmd(ac);
93251974Smsmith
93351974Smsmith	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
93459249Sphk	amrd = (struct amrd_softc *)bp->bio_dev->si_drv1;
93558883Smsmith	driveno = amrd->amrd_drive - sc->amr_drive;
93659249Sphk	blkcount = (bp->bio_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
93751974Smsmith
93859249Sphk	if ((bp->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
93951974Smsmith	    device_printf(sc->amr_dev, "I/O beyond end of unit (%u,%d > %u)\n",
94059249Sphk			  bp->bio_pblkno, blkcount, sc->amr_drive[driveno].al_size);
94151974Smsmith
94251974Smsmith	/*
94351974Smsmith	 * Build the I/O command.
94451974Smsmith	 */
94551974Smsmith	ac->ac_mailbox.mb_command = cmd;
94651974Smsmith	ac->ac_mailbox.mb_blkcount = blkcount;
94759249Sphk	ac->ac_mailbox.mb_lba = bp->bio_pblkno;
94851974Smsmith	ac->ac_mailbox.mb_physaddr = ac->ac_sgphys;
94951974Smsmith	ac->ac_mailbox.mb_drive = driveno;
95051974Smsmith	ac->ac_mailbox.mb_nsgelem = ac->ac_nsgent;
95151974Smsmith
95251974Smsmith	/* try to give command to controller */
95351974Smsmith	if (amr_start(ac) != 0) {
95451974Smsmith	    /* fail the command */
95551974Smsmith	    ac->ac_status = AMR_STATUS_WEDGED;
95651974Smsmith	    amr_completeio(ac);
95751974Smsmith	}
95852543Smsmith	s = splbio();
95951974Smsmith    }
96052543Smsmith    splx(s);
96158883Smsmith    amr_lock_clr(sc, AMR_LOCK_STARTING);
96251974Smsmith}
96351974Smsmith
96451974Smsmith/********************************************************************************
96551974Smsmith * Handle completion of an I/O command.
96651974Smsmith */
96751974Smsmithstatic void
96851974Smsmithamr_completeio(struct amr_command *ac)
96951974Smsmith{
97051974Smsmith    struct amr_softc	*sc = ac->ac_sc;
97159249Sphk    struct bio		*bp = (struct bio *)ac->ac_private;
97258883Smsmith    int			notify, release;
97358883Smsmith
97458883Smsmith    notify = 1;
97558883Smsmith    release = 1;
97651974Smsmith
97751974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
97859249Sphk	bp->bio_error = EIO;
97959249Sphk	bp->bio_flags |= BIO_ERROR;
98051974Smsmith
98151974Smsmith	switch(ac->ac_status) {
98251974Smsmith	    /* XXX need more information on I/O error reasons */
98358883Smsmith	case AMR_STATUS_LATE:
98458883Smsmith	    notify = 0;				/* we've already notified the parent */
98558883Smsmith	    break;
98658883Smsmith
98758883Smsmith	case AMR_STATUS_WEDGED:
98858883Smsmith	    release = 0;			/* the command is still outstanding, we can't release */
98958883Smsmith	    break;
99058883Smsmith
99151974Smsmith	default:
99251974Smsmith	    device_printf(sc->amr_dev, "I/O error - %x\n", ac->ac_status);
99351974Smsmith	    amr_printcommand(ac);
99451974Smsmith	    break;
99551974Smsmith	}
99651974Smsmith    }
99758883Smsmith    if (release)
99858883Smsmith	amr_releasecmd(ac);
99958883Smsmith    if (notify)
100058883Smsmith	amrd_intr(bp);
100151974Smsmith}
100251974Smsmith
100351974Smsmith/********************************************************************************
100451974Smsmith ********************************************************************************
100551974Smsmith                                                               Command Processing
100651974Smsmith ********************************************************************************
100751974Smsmith ********************************************************************************/
100851974Smsmith
100951974Smsmith/********************************************************************************
101051974Smsmith * Take a command, submit it to the controller and sleep until it completes
101151974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
101251974Smsmith */
101351974Smsmithstatic int
101451974Smsmithamr_wait_command(struct amr_command *ac)
101551974Smsmith{
101651974Smsmith    struct amr_softc	*sc = ac->ac_sc;
101751974Smsmith    int			error, count;
101851974Smsmith
101951974Smsmith    debug("called");
102051974Smsmith
102151974Smsmith    ac->ac_complete = NULL;
102251974Smsmith    ac->ac_private = ac;
102351974Smsmith    if ((error = amr_start(ac)) != 0)
102451974Smsmith	return(error);
102551974Smsmith
102651974Smsmith    count = 0;
102751974Smsmith    /* XXX better timeout? */
102851974Smsmith    while ((ac->ac_status == AMR_STATUS_BUSY) && (count < 30)) {
102951974Smsmith	tsleep(ac->ac_private, PRIBIO | PCATCH, "amrwcmd", hz);
103051974Smsmith    }
103151974Smsmith
103251974Smsmith    if (ac->ac_status != 0) {
103351974Smsmith	device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status);
103451974Smsmith	return(EIO);
103551974Smsmith    }
103651974Smsmith    return(0);
103751974Smsmith}
103851974Smsmith
103951974Smsmith/********************************************************************************
104051974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
104151974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
104251974Smsmith */
104351974Smsmithstatic int
104451974Smsmithamr_poll_command(struct amr_command *ac)
104551974Smsmith{
104651974Smsmith    struct amr_softc	*sc = ac->ac_sc;
104751974Smsmith    int			error, count, s;
104851974Smsmith
104951974Smsmith    debug("called");
105051974Smsmith
105151974Smsmith    ac->ac_complete = NULL;
105251974Smsmith    ac->ac_private = NULL;
105351974Smsmith    if ((error = amr_start(ac)) != 0)
105451974Smsmith	return(error);
105551974Smsmith
105651974Smsmith    count = 0;
105751974Smsmith    do {
105851974Smsmith	/*
105951974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
106051974Smsmith	 * Note that the timeout here is somewhat arbitrary.
106151974Smsmith	 */
106251974Smsmith	amr_done(sc);
106351974Smsmith    } while ((ac->ac_status == AMR_STATUS_BUSY) && (count++ < 100000));
106451974Smsmith    s = splbio();
106551974Smsmith    if (ac->ac_status != AMR_STATUS_BUSY) {
106652543Smsmith	TAILQ_REMOVE(&sc->amr_work, ac, ac_link);
106752543Smsmith	sc->amr_workcount--;
106851974Smsmith	error = 0;
106951974Smsmith    } else {
107051974Smsmith	/* take the command out of the busy list, mark slot as bogus */
107151974Smsmith	sc->amr_busycmd[ac->ac_slot] = (struct amr_command *)sc;
107251974Smsmith	error = EIO;
107351974Smsmith	device_printf(sc->amr_dev, "I/O error 0x%x\n", ac->ac_status);
107451974Smsmith    }
107551974Smsmith    splx(s);
107651974Smsmith    return(error);
107751974Smsmith}
107851974Smsmith
107951974Smsmith/********************************************************************************
108051974Smsmith * Get a free command slot.
108151974Smsmith */
108251974Smsmithstatic int
108351974Smsmithamr_getslot(struct amr_command *ac)
108451974Smsmith{
108551974Smsmith    struct amr_softc	*sc = ac->ac_sc;
108651974Smsmith    int			s, slot, limit;
108751974Smsmith
108851974Smsmith    debug("called");
108951974Smsmith
109051974Smsmith    /* enforce slot usage limit */
109151974Smsmith    limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
109251974Smsmith    if (sc->amr_busycmdcount > limit)
109351974Smsmith	return(EBUSY);
109451974Smsmith
109551974Smsmith    /*
109651974Smsmith     * Allocate a slot
109751974Smsmith     */
109851974Smsmith    s = splbio();
109951974Smsmith    for (slot = 0; slot < sc->amr_maxio; slot++) {
110051974Smsmith	if (sc->amr_busycmd[slot] == NULL)
110151974Smsmith	    break;
110251974Smsmith    }
110351974Smsmith    if (slot < sc->amr_maxio) {
110451974Smsmith	sc->amr_busycmdcount++;
110551974Smsmith	sc->amr_busycmd[slot] = ac;
110651974Smsmith    }
110751974Smsmith    splx(s);
110851974Smsmith
110951974Smsmith    /* out of slots? */
111051974Smsmith    if (slot >= sc->amr_maxio)
111151974Smsmith	return(EBUSY);
111251974Smsmith
111351974Smsmith    ac->ac_slot = slot;
111451974Smsmith    return(0);
111551974Smsmith}
111651974Smsmith
111751974Smsmith/********************************************************************************
111851974Smsmith * Map/unmap (ac)'s data in the controller's addressable space.
111951974Smsmith */
112051974Smsmithstatic void
112151974Smsmithamr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
112251974Smsmith{
112351974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
112451974Smsmith    struct amr_softc	*sc = ac->ac_sc;
112551974Smsmith    struct amr_sgentry	*sg;
112651974Smsmith    int			i;
112751974Smsmith
112851974Smsmith    debug("called");
112951974Smsmith
113051974Smsmith    /* get base address of s/g table */
113151974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
113251974Smsmith
113351974Smsmith    /* save s/g table information in command */
113451974Smsmith    ac->ac_nsgent = nsegments;
113551974Smsmith    ac->ac_sgphys = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
113651974Smsmith    ac->ac_dataphys = segs[0].ds_addr;
113751974Smsmith
113851974Smsmith    /* populate s/g table */
113951974Smsmith    for (i = 0; i < nsegments; i++, sg++) {
114051974Smsmith	sg->sg_addr = segs[i].ds_addr;
114151974Smsmith	sg->sg_count = segs[i].ds_len;
114251974Smsmith    }
114351974Smsmith}
114451974Smsmith
114551974Smsmithstatic void
114651974Smsmithamr_mapcmd(struct amr_command *ac)
114751974Smsmith{
114851974Smsmith    struct amr_softc	*sc = ac->ac_sc;
114951974Smsmith
115051974Smsmith    debug("called");
115151974Smsmith
115251974Smsmith    /* if the command involves data at all */
115351974Smsmith    if (ac->ac_data != NULL) {
115451974Smsmith
115551974Smsmith	/* map the data buffer into bus space and build the s/g list */
115651974Smsmith	bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
115751974Smsmith			amr_setup_dmamap, ac, 0);
115851974Smsmith	if (ac->ac_flags & AMR_CMD_DATAIN)
115951974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
116051974Smsmith	if (ac->ac_flags & AMR_CMD_DATAOUT)
116151974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
116251974Smsmith    }
116351974Smsmith}
116451974Smsmith
116551974Smsmithstatic void
116651974Smsmithamr_unmapcmd(struct amr_command *ac)
116751974Smsmith{
116851974Smsmith    struct amr_softc	*sc = ac->ac_sc;
116951974Smsmith
117051974Smsmith    debug("called");
117151974Smsmith
117251974Smsmith    /* if the command involved data at all */
117351974Smsmith    if (ac->ac_data != NULL) {
117451974Smsmith
117551974Smsmith	if (ac->ac_flags & AMR_CMD_DATAIN)
117651974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
117751974Smsmith	if (ac->ac_flags & AMR_CMD_DATAOUT)
117851974Smsmith	    bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
117951974Smsmith
118051974Smsmith	bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
118151974Smsmith    }
118251974Smsmith}
118351974Smsmith
118451974Smsmith/********************************************************************************
118558883Smsmith * Take a command and give it to the controller.
118651974Smsmith */
118751974Smsmithstatic int
118851974Smsmithamr_start(struct amr_command *ac)
118951974Smsmith{
119051974Smsmith    struct amr_softc	*sc = ac->ac_sc;
119158883Smsmith    int			done, s, i;
119251974Smsmith
119351974Smsmith    debug("called");
119451974Smsmith
119551974Smsmith    /*
119651974Smsmith     * Save the slot number so that we can locate this command when complete.
119751974Smsmith     * Note that ident = 0 seems to be special, so we don't use it.
119851974Smsmith     */
119951974Smsmith    ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
120051974Smsmith
120151974Smsmith    /* set the busy flag when we copy the mailbox in */
120251974Smsmith    ac->ac_mailbox.mb_busy = 1;
120351974Smsmith
120451974Smsmith    /* set impossible status so that a woken sleeper can tell the command is busy */
120551974Smsmith    ac->ac_status = AMR_STATUS_BUSY;
120651974Smsmith
120758883Smsmith    /*
120858883Smsmith     * Spin waiting for the mailbox, give up after ~1 second.
120958883Smsmith     */
121051974Smsmith    debug("wait for mailbox");
121158883Smsmith    for (i = 10000, done = 0; (i > 0) && !done; i--) {
121251974Smsmith	s = splbio();
121351974Smsmith
121451974Smsmith	/* is the mailbox free? */
121551974Smsmith	if (sc->amr_mailbox->mb_busy == 0) {
121651974Smsmith	    debug("got mailbox");
121751974Smsmith	    sc->amr_mailbox64->mb64_segment = 0;
121851974Smsmith	    bcopy(&ac->ac_mailbox, sc->amr_mailbox, AMR_MBOX_CMDSIZE);
121951974Smsmith	    sc->amr_submit_command(sc);
122051974Smsmith	    done = 1;
122152543Smsmith	    sc->amr_workcount++;
122252543Smsmith	    TAILQ_INSERT_TAIL(&sc->amr_work, ac, ac_link);
122351974Smsmith
122451974Smsmith	    /* not free, try to clean up while we wait */
122551974Smsmith	} else {
122651974Smsmith	    debug("busy flag %x\n", sc->amr_mailbox->mb_busy);
122758883Smsmith	    /* this is somewhat ugly */
122858883Smsmith	    DELAY(100);
122951974Smsmith	}
123058883Smsmith	splx(s);	/* drop spl to allow completion interrupts */
123151974Smsmith    }
123251974Smsmith
123351974Smsmith    /* command is enqueued? */
123451974Smsmith    if (done) {
123551974Smsmith	ac->ac_stamp = time_second;
123651974Smsmith	debug("posted command");
123751974Smsmith	return(0);
123851974Smsmith    }
123951974Smsmith
124051974Smsmith    /*
124151974Smsmith     * The controller wouldn't take the command.  Revoke the slot
124251974Smsmith     * that the command was given and return with a bad status.
124351974Smsmith     */
124451974Smsmith    sc->amr_busycmd[ac->ac_slot] = NULL;
124551974Smsmith    device_printf(sc->amr_dev, "controller wedged (not taking commands)\n");
124651974Smsmith    ac->ac_status = AMR_STATUS_WEDGED;
124758883Smsmith    amr_complete(sc);
124851974Smsmith    return(EIO);
124951974Smsmith}
125051974Smsmith
125151974Smsmith/********************************************************************************
125251974Smsmith * Extract one or more completed commands from the controller (sc)
125351974Smsmith *
125452543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
125551974Smsmith */
125651974Smsmithstatic int
125751974Smsmithamr_done(struct amr_softc *sc)
125851974Smsmith{
125951974Smsmith    struct amr_command	*ac;
126051974Smsmith    struct amr_mailbox	mbox;
126158883Smsmith    int			i, idx, s, result;
126251974Smsmith
126351974Smsmith    debug("called");
126451974Smsmith
126551974Smsmith    /* See if there's anything for us to do */
126651974Smsmith    result = 0;
126751974Smsmith
126858883Smsmith    /* loop collecting completed commands */
126958883Smsmith    s = splbio();
127058883Smsmith    for (;;) {
127158883Smsmith	/* poll for a completed command's identifier and status */
127258883Smsmith	if (sc->amr_get_work(sc, &mbox)) {
127358883Smsmith	    result = 1;
127458883Smsmith
127558883Smsmith	    /* iterate over completed commands in this result */
127658883Smsmith	    for (i = 0; i < mbox.mb_nstatus; i++) {
127758883Smsmith		/* get pointer to busy command */
127858883Smsmith		idx = mbox.mb_completed[i] - 1;
127958883Smsmith		ac = sc->amr_busycmd[idx];
128051974Smsmith
128158883Smsmith		/* really a busy command? */
128258883Smsmith		if (ac != NULL) {
128358883Smsmith
128458883Smsmith		    /* pull the command from the busy index */
128558883Smsmith		    sc->amr_busycmd[idx] = NULL;
128658883Smsmith		    sc->amr_busycmdcount--;
128751974Smsmith
128858883Smsmith		    /* aborted command? */
128958883Smsmith		    if (ac == (struct amr_command *)sc) {
129058883Smsmith			device_printf(sc->amr_dev, "aborted command completed (%d)\n", idx);
129158883Smsmith			sc->amr_busycmd[idx] = NULL;	/* free the slot again */
129258883Smsmith			ac = NULL;
129352543Smsmith
129458883Smsmith			/* wedged command? */
129558883Smsmith		    } else if (ac->ac_status == AMR_STATUS_WEDGED) {
129658883Smsmith			device_printf(sc->amr_dev, "wedged command completed (%d)\n", idx);
129758883Smsmith			ac->ac_status = AMR_STATUS_LATE;
129852543Smsmith
129958883Smsmith			/* completed normally, save status */
130058883Smsmith		    } else {
130158883Smsmith			ac->ac_status = mbox.mb_status;
130258883Smsmith			debug("completed command with status %x", mbox.mb_status);
130358883Smsmith		    }
130451974Smsmith		}
130551974Smsmith	    }
130658883Smsmith	} else {
130758883Smsmith	    break;
130851974Smsmith	}
130951974Smsmith    }
131058883Smsmith
131158883Smsmith    /* if we've completed any commands, try posting some more */
131258883Smsmith    if (result)
131358883Smsmith	amr_startio(sc);
131458883Smsmith
131558883Smsmith    /* handle completion and timeouts */
131658883Smsmith    amr_complete(sc);
131758883Smsmith
131851974Smsmith    return(result);
131951974Smsmith}
132051974Smsmith
132151974Smsmith/********************************************************************************
132251974Smsmith * Do completion processing on done commands on (sc)
132351974Smsmith */
132451974Smsmithstatic void
132551974Smsmithamr_complete(struct amr_softc *sc)
132651974Smsmith{
132751974Smsmith    struct amr_command	*ac, *nc;
132851974Smsmith    int			s, count;
132951974Smsmith
133051974Smsmith    debug("called");
133151974Smsmith
133258883Smsmith    if (amr_lock_tas(sc, AMR_LOCK_COMPLETING))
133358883Smsmith	return;
133458883Smsmith
133558883Smsmith    s = splbio();
133651974Smsmith    count = 0;
133751974Smsmith
133858883Smsmith    /* scan the list of busy/done commands */
133952543Smsmith    ac = TAILQ_FIRST(&sc->amr_work);
134051974Smsmith    while (ac != NULL) {
134151974Smsmith	nc = TAILQ_NEXT(ac, ac_link);
134251974Smsmith
134358883Smsmith	/* Command has been completed in some fashion */
134452543Smsmith	if (ac->ac_status != AMR_STATUS_BUSY) {
134558883Smsmith
134658883Smsmith	    /* unmap the command's data buffer */
134758883Smsmith	    amr_unmapcmd(ac);
134851974Smsmith
134951974Smsmith	    /*
135052543Smsmith	     * Is there a completion handler?
135151974Smsmith	     */
135252543Smsmith	    if (ac->ac_complete != NULL) {
135351974Smsmith
135452543Smsmith		/* remove and give to completion handler */
135552543Smsmith		TAILQ_REMOVE(&sc->amr_work, ac, ac_link);
135652543Smsmith		sc->amr_workcount--;
135752543Smsmith		ac->ac_complete(ac);
135852543Smsmith
135952543Smsmith		/*
136052543Smsmith		 * Is someone sleeping on this one?
136152543Smsmith		 */
136252543Smsmith	    } else if (ac->ac_private != NULL) {
136351974Smsmith
136452543Smsmith		/* remove and wake up */
136552543Smsmith		TAILQ_REMOVE(&sc->amr_work, ac, ac_link);
136652543Smsmith		sc->amr_workcount--;
136752543Smsmith		wakeup_one(ac->ac_private);
136852543Smsmith
136952543Smsmith		/*
137052543Smsmith		 * Leave it for a polling caller.
137152543Smsmith		 */
137252543Smsmith	    } else {
137352543Smsmith	    }
137451974Smsmith	}
137551974Smsmith	ac = nc;
137651974Smsmith    }
137751974Smsmith    splx(s);
137858883Smsmith
137958883Smsmith    amr_lock_clr(sc, AMR_LOCK_COMPLETING);
138051974Smsmith}
138151974Smsmith
138251974Smsmith/********************************************************************************
138351974Smsmith ********************************************************************************
138451974Smsmith                                                        Command Buffer Management
138551974Smsmith ********************************************************************************
138651974Smsmith ********************************************************************************/
138751974Smsmith
138851974Smsmith/********************************************************************************
138951974Smsmith * Get a new command buffer.
139051974Smsmith *
139151974Smsmith * This may return NULL in low-memory cases.
139251974Smsmith *
139351974Smsmith * Note that using malloc() is expensive (the command buffer is << 1 page) but
139451974Smsmith * necessary if we are to be a loadable module before the zone allocator is fixed.
139551974Smsmith *
139651974Smsmith * If possible, we recycle a command buffer that's been used before.
139751974Smsmith *
139851974Smsmith * XXX Note that command buffers are not cleaned out - it is the caller's
139951974Smsmith *     responsibility to ensure that all required fields are filled in before
140051974Smsmith *     using a buffer.
140151974Smsmith */
140251974Smsmithstatic struct amr_command *
140351974Smsmithamr_alloccmd(struct amr_softc *sc)
140451974Smsmith{
140551974Smsmith    struct amr_command	*ac;
140651974Smsmith    int			error;
140751974Smsmith    int			s;
140851974Smsmith
140951974Smsmith    debug("called");
141051974Smsmith
141151974Smsmith    s = splbio();
141251974Smsmith    if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL)
141351974Smsmith	TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
141451974Smsmith    splx(s);
141551974Smsmith
141651974Smsmith    /* allocate a new command buffer? */
141751974Smsmith    if (ac == NULL) {
141851974Smsmith	ac = (struct amr_command *)malloc(sizeof(*ac), M_DEVBUF, M_NOWAIT);
141951974Smsmith	if (ac != NULL) {
142051974Smsmith	    bzero(ac, sizeof(*ac));
142151974Smsmith	    ac->ac_sc = sc;
142251974Smsmith	    error = bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap);
142351974Smsmith	    if (error) {
142451974Smsmith		free(ac, M_DEVBUF);
142551974Smsmith		return(NULL);
142651974Smsmith	    }
142751974Smsmith	}
142851974Smsmith    }
142951974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
143051974Smsmith    return(ac);
143151974Smsmith}
143251974Smsmith
143351974Smsmith/********************************************************************************
143451974Smsmith * Release a command buffer for recycling.
143551974Smsmith *
143651974Smsmith * XXX It might be a good idea to limit the number of commands we save for reuse
143751974Smsmith *     if it's shown that this list bloats out massively.
143851974Smsmith */
143951974Smsmithstatic void
144051974Smsmithamr_releasecmd(struct amr_command *ac)
144151974Smsmith{
144251974Smsmith    int		s;
144351974Smsmith
144451974Smsmith    debug("called");
144551974Smsmith
144651974Smsmith    s = splbio();
144751974Smsmith    TAILQ_INSERT_HEAD(&ac->ac_sc->amr_freecmds, ac, ac_link);
144851974Smsmith    splx(s);
144951974Smsmith}
145051974Smsmith
145151974Smsmith/********************************************************************************
145251974Smsmith * Permanently discard a command buffer.
145351974Smsmith */
145451974Smsmithstatic void
145551974Smsmithamr_freecmd(struct amr_command *ac)
145651974Smsmith{
145751974Smsmith    struct amr_softc	*sc = ac->ac_sc;
145851974Smsmith
145951974Smsmith    debug("called");
146051974Smsmith
146151974Smsmith    bus_dmamap_destroy(sc->amr_buffer_dmat, ac->ac_dmamap);
146251974Smsmith    free(ac, M_DEVBUF);
146351974Smsmith}
146451974Smsmith
146551974Smsmith/********************************************************************************
146651974Smsmith ********************************************************************************
146751974Smsmith                                                         Interface-specific Shims
146851974Smsmith ********************************************************************************
146951974Smsmith ********************************************************************************/
147051974Smsmith
147151974Smsmith/********************************************************************************
147251974Smsmith * Tell the controller that the mailbox contains a valid command
147351974Smsmith */
147451974Smsmithstatic void
147551974Smsmithamr_quartz_submit_command(struct amr_softc *sc)
147651974Smsmith{
147751974Smsmith    debug("called");
147851974Smsmith
147951974Smsmith    sc->amr_mailbox->mb_poll = 0;
148051974Smsmith    sc->amr_mailbox->mb_ack = 0;
148151974Smsmith    while(AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
148251974Smsmith	;				/* XXX aiee! what if it dies? */
148351974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
148451974Smsmith}
148551974Smsmith
148651974Smsmithstatic void
148751974Smsmithamr_std_submit_command(struct amr_softc *sc)
148851974Smsmith{
148951974Smsmith    debug("called");
149051974Smsmith
149151974Smsmith    /* XXX write barrier? */
149251974Smsmith    while (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
149351974Smsmith	;				/* XXX aiee! what if it dies? */
149451974Smsmith    AMR_SPOST_COMMAND(sc);
149551974Smsmith}
149651974Smsmith
149751974Smsmith/********************************************************************************
149851974Smsmith * Claim any work that the controller has completed; acknowledge completion,
149951974Smsmith * save details of the completion in (mbsave)
150051974Smsmith */
150151974Smsmithstatic int
150251974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
150351974Smsmith{
150451974Smsmith    int		s, worked;
150551974Smsmith    u_int32_t	outd;
150651974Smsmith
150758883Smsmith/*    debug("called"); */
150851974Smsmith
150951974Smsmith    worked = 0;
151051974Smsmith    s = splbio();
151151974Smsmith
151251974Smsmith    /* work waiting for us? */
151351974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
151451974Smsmith	AMR_QPUT_ODB(sc, AMR_QODB_READY);
151551974Smsmith
151651974Smsmith	/* save mailbox, which contains a list of completed commands */
151751974Smsmith	/* XXX read barrier? */
151851974Smsmith	bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave));
151951974Smsmith
152051974Smsmith	/* acknowledge that we have the commands */
152151974Smsmith	AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
152251974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
152351974Smsmith	    ;				/* XXX aiee! what if it dies? */
152451974Smsmith	worked = 1;			/* got some work */
152551974Smsmith    }
152651974Smsmith
152751974Smsmith    splx(s);
152851974Smsmith    return(worked);
152951974Smsmith}
153051974Smsmith
153151974Smsmithstatic int
153251974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
153351974Smsmith{
153451974Smsmith    int		s, worked;
153551974Smsmith    u_int8_t	istat;
153651974Smsmith
153751974Smsmith    debug("called");
153851974Smsmith
153951974Smsmith    worked = 0;
154051974Smsmith    s = splbio();
154151974Smsmith
154251974Smsmith    /* check for valid interrupt status */
154351974Smsmith    istat = AMR_SGET_ISTAT(sc);
154451974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
154551974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
154651974Smsmith
154751974Smsmith	/* save mailbox, which contains a list of completed commands */
154851974Smsmith	/* XXX read barrier? */
154951974Smsmith	bcopy(sc->amr_mailbox, mbsave, sizeof(*mbsave));
155051974Smsmith
155151974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
155251974Smsmith	worked = 1;
155351974Smsmith    }
155451974Smsmith
155551974Smsmith    splx(s);
155651974Smsmith    return(worked);
155751974Smsmith}
155851974Smsmith
155951974Smsmith/********************************************************************************
156051974Smsmith * Notify the controller of the mailbox location.
156151974Smsmith */
156251974Smsmithstatic void
156351974Smsmithamr_quartz_attach_mailbox(struct amr_softc *sc)
156451974Smsmith{
156551974Smsmith    /* Quartz is given the mailbox location when a command is submitted */
156651974Smsmith}
156751974Smsmith
156851974Smsmithstatic void
156951974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
157051974Smsmith{
157151974Smsmith
157251974Smsmith    /* program the mailbox physical address */
157351974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
157451974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
157551974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
157651974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
157751974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
157851974Smsmith
157951974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
158051974Smsmith    AMR_SACK_INTERRUPT(sc);
158151974Smsmith    AMR_SENABLE_INTR(sc);
158251974Smsmith}
158351974Smsmith
158451974Smsmith/********************************************************************************
158551974Smsmith ********************************************************************************
158651974Smsmith                                                                        Debugging
158751974Smsmith ********************************************************************************
158851974Smsmith ********************************************************************************/
158951974Smsmith
159051974Smsmith/********************************************************************************
159151974Smsmith * Print the command (ac) in human-readable format
159251974Smsmith */
159351974Smsmithstatic void
159451974Smsmithamr_printcommand(struct amr_command *ac)
159551974Smsmith{
159651974Smsmith    struct amr_softc	*sc = ac->ac_sc;
159751974Smsmith    struct amr_sgentry	*sg;
159851974Smsmith    int			i;
159951974Smsmith
160051974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
160151974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
160251974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
160351974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
160454512Speter    device_printf(sc->amr_dev, "virtaddr %p  length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
160558883Smsmith    device_printf(sc->amr_dev, "sg physaddr %08x  nsg %d\n",
160651974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
160751974Smsmith
160851974Smsmith    /* get base address of s/g table */
160951974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
161051974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
161151974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
161251974Smsmith}
161358883Smsmith
161458883Smsmith/********************************************************************************
161558883Smsmith * Print information on all the controllers in the system, useful mostly
161658883Smsmith * for calling from DDB.
161758883Smsmith */
161858883Smsmithvoid
161958883Smsmithamr_report(void)
162058883Smsmith{
162158883Smsmith    struct amr_softc	*sc;
162258883Smsmith    int			i, s;
162358883Smsmith
162458883Smsmith    s = splbio();
162558883Smsmith    for (i = 0; (sc = devclass_get_softc(amr_devclass, i)) != NULL; i++) {
162658883Smsmith	device_printf(sc->amr_dev, "amr_waitbufs %d  amr_busycmdcount %d  amr_workcount %d\n",
162758883Smsmith		      sc->amr_waitbufs, sc->amr_busycmdcount, sc->amr_workcount);
162858883Smsmith    }
162958883Smsmith}
1630