amr.c revision 89055
151974Smsmith/*-
265245Smsmith * Copyright (c) 1999,2000 Michael Smith
365245Smsmith * Copyright (c) 2000 BSDi
451974Smsmith * All rights reserved.
551974Smsmith *
651974Smsmith * Redistribution and use in source and binary forms, with or without
751974Smsmith * modification, are permitted provided that the following conditions
851974Smsmith * are met:
951974Smsmith * 1. Redistributions of source code must retain the above copyright
1051974Smsmith *    notice, this list of conditions and the following disclaimer.
1151974Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1251974Smsmith *    notice, this list of conditions and the following disclaimer in the
1351974Smsmith *    documentation and/or other materials provided with the distribution.
1451974Smsmith *
1551974Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1651974Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1751974Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1851974Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1951974Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2051974Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2151974Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2251974Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2351974Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2451974Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2551974Smsmith * SUCH DAMAGE.
2651974Smsmith *
2751974Smsmith *	$FreeBSD: head/sys/dev/amr/amr.c 89055 2002-01-08 06:47:02Z msmith $
2851974Smsmith */
2951974Smsmith
3051974Smsmith/*
3165245Smsmith * Driver for the AMI MegaRaid family of controllers.
3251974Smsmith */
3351974Smsmith
3451974Smsmith#include <sys/param.h>
3551974Smsmith#include <sys/systm.h>
3651974Smsmith#include <sys/malloc.h>
3751974Smsmith#include <sys/kernel.h>
3851974Smsmith
3965245Smsmith#include <dev/amr/amr_compat.h>
4051974Smsmith#include <sys/bus.h>
4151974Smsmith#include <sys/conf.h>
4251974Smsmith#include <sys/devicestat.h>
4351974Smsmith#include <sys/disk.h>
4465245Smsmith#include <sys/stat.h>
4551974Smsmith
4665245Smsmith#include <machine/bus_memio.h>
4765245Smsmith#include <machine/bus_pio.h>
4865245Smsmith#include <machine/bus.h>
4951974Smsmith#include <machine/resource.h>
5051974Smsmith#include <sys/rman.h>
5151974Smsmith
5265245Smsmith#include <pci/pcireg.h>
5365245Smsmith#include <pci/pcivar.h>
5465245Smsmith
5551974Smsmith#include <dev/amr/amrio.h>
5651974Smsmith#include <dev/amr/amrreg.h>
5751974Smsmith#include <dev/amr/amrvar.h>
5865245Smsmith#define AMR_DEFINE_TABLES
5965245Smsmith#include <dev/amr/amr_tables.h>
6051974Smsmith
6151974Smsmith#define AMR_CDEV_MAJOR	132
6251974Smsmith
6365245Smsmithstatic d_open_t         amr_open;
6465245Smsmithstatic d_close_t        amr_close;
6565245Smsmithstatic d_ioctl_t        amr_ioctl;
6665245Smsmith
6751974Smsmithstatic struct cdevsw amr_cdevsw = {
6851974Smsmith		/* open */	amr_open,
6951974Smsmith		/* close */	amr_close,
7051974Smsmith		/* read */	noread,
7151974Smsmith		/* write */	nowrite,
7251974Smsmith		/* ioctl */	amr_ioctl,
7351974Smsmith		/* poll */	nopoll,
7451974Smsmith		/* mmap */	nommap,
7551974Smsmith		/* strategy */	nostrategy,
7651974Smsmith		/* name */ 	"amr",
7751974Smsmith		/* maj */	AMR_CDEV_MAJOR,
7851974Smsmith		/* dump */	nodump,
7951974Smsmith		/* psize */ 	nopsize,
8051974Smsmith		/* flags */	0,
8151974Smsmith};
8251974Smsmith
8365245Smsmith/*
8465245Smsmith * Initialisation, bus interface.
8565245Smsmith */
8665245Smsmithstatic void	amr_startup(void *arg);
8751974Smsmith
8851974Smsmith/*
8951974Smsmith * Command wrappers
9051974Smsmith */
9165245Smsmithstatic int	amr_query_controller(struct amr_softc *sc);
9265245Smsmithstatic void	*amr_enquiry(struct amr_softc *sc, size_t bufsize,
9365245Smsmith			     u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual);
9465245Smsmithstatic void	amr_completeio(struct amr_command *ac);
9551974Smsmith
9651974Smsmith/*
9765245Smsmith * Command buffer allocation.
9851974Smsmith */
9965245Smsmithstatic void	amr_alloccmd_cluster(struct amr_softc *sc);
10065245Smsmithstatic void	amr_freecmd_cluster(struct amr_command_cluster *acc);
10151974Smsmith
10251974Smsmith/*
10365245Smsmith * Command processing.
10451974Smsmith */
10565245Smsmithstatic int	amr_bio_command(struct amr_softc *sc, struct amr_command **acp);
10665245Smsmithstatic int	amr_wait_command(struct amr_command *ac);
10765245Smsmithstatic int	amr_poll_command(struct amr_command *ac);
10865245Smsmithstatic int	amr_getslot(struct amr_command *ac);
10965245Smsmithstatic void	amr_mapcmd(struct amr_command *ac);
11065245Smsmithstatic void	amr_unmapcmd(struct amr_command *ac);
11165245Smsmithstatic int	amr_start(struct amr_command *ac);
11265245Smsmithstatic void	amr_complete(void *context, int pending);
11351974Smsmith
11451974Smsmith/*
11558883Smsmith * Status monitoring
11658883Smsmith */
11765245Smsmithstatic void	amr_periodic(void *data);
11858883Smsmith
11958883Smsmith/*
12051974Smsmith * Interface-specific shims
12151974Smsmith */
12265245Smsmithstatic int	amr_quartz_submit_command(struct amr_softc *sc);
12365245Smsmithstatic int	amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
12451974Smsmith
12565245Smsmithstatic int	amr_std_submit_command(struct amr_softc *sc);
12665245Smsmithstatic int	amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
12765245Smsmithstatic void	amr_std_attach_mailbox(struct amr_softc *sc);
12851974Smsmith
12965245Smsmith#ifdef AMR_BOARD_INIT
13065245Smsmithstatic int	amr_quartz_init(struct amr_softc *sc);
13165245Smsmithstatic int	amr_std_init(struct amr_softc *sc);
13265245Smsmith#endif
13365245Smsmith
13451974Smsmith/*
13551974Smsmith * Debugging
13651974Smsmith */
13765245Smsmithstatic void	amr_describe_controller(struct amr_softc *sc);
13865245Smsmith#ifdef AMR_DEBUG
13965245Smsmithstatic void	amr_printcommand(struct amr_command *ac);
14065245Smsmith#endif
14151974Smsmith
14251974Smsmith/********************************************************************************
14351974Smsmith ********************************************************************************
14465245Smsmith                                                                      Inline Glue
14551974Smsmith ********************************************************************************
14651974Smsmith ********************************************************************************/
14751974Smsmith
14851974Smsmith/********************************************************************************
14965245Smsmith ********************************************************************************
15065245Smsmith                                                                Public Interfaces
15165245Smsmith ********************************************************************************
15265245Smsmith ********************************************************************************/
15351974Smsmith
15451974Smsmith/********************************************************************************
15551974Smsmith * Initialise the controller and softc.
15651974Smsmith */
15751974Smsmithint
15851974Smsmithamr_attach(struct amr_softc *sc)
15951974Smsmith{
16051974Smsmith
16165245Smsmith    debug_called(1);
16265245Smsmith
16351974Smsmith    /*
16451974Smsmith     * Initialise per-controller queues.
16551974Smsmith     */
16665245Smsmith    TAILQ_INIT(&sc->amr_completed);
16751974Smsmith    TAILQ_INIT(&sc->amr_freecmds);
16865245Smsmith    TAILQ_INIT(&sc->amr_cmd_clusters);
16965245Smsmith    TAILQ_INIT(&sc->amr_ready);
17059249Sphk    bioq_init(&sc->amr_bioq);
17151974Smsmith
17265245Smsmith#if __FreeBSD_version >= 500005
17351974Smsmith    /*
17465245Smsmith     * Initialise command-completion task.
17565245Smsmith     */
17665245Smsmith    TASK_INIT(&sc->amr_task_complete, 0, amr_complete, sc);
17765245Smsmith#endif
17865245Smsmith
17965245Smsmith    debug(2, "queue init done");
18065245Smsmith
18165245Smsmith    /*
18251974Smsmith     * Configure for this controller type.
18351974Smsmith     */
18465245Smsmith    if (AMR_IS_QUARTZ(sc)) {
18551974Smsmith	sc->amr_submit_command = amr_quartz_submit_command;
18651974Smsmith	sc->amr_get_work       = amr_quartz_get_work;
18751974Smsmith    } else {
18851974Smsmith	sc->amr_submit_command = amr_std_submit_command;
18951974Smsmith	sc->amr_get_work       = amr_std_get_work;
19065245Smsmith	amr_std_attach_mailbox(sc);;
19151974Smsmith    }
19251974Smsmith
19365245Smsmith#ifdef AMR_BOARD_INIT
19465245Smsmith    if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc))))
19565245Smsmith	return(ENXIO);
19665245Smsmith#endif
19751974Smsmith
19851974Smsmith    /*
19965245Smsmith     * Quiz controller for features and limits.
20051974Smsmith     */
20165245Smsmith    if (amr_query_controller(sc))
20265245Smsmith	return(ENXIO);
20351974Smsmith
20465245Smsmith    debug(2, "controller query complete");
20551974Smsmith
20665245Smsmith#ifdef AMR_SCSI_PASSTHROUGH
20751974Smsmith    /*
20865245Smsmith     * Attach our 'real' SCSI channels to CAM.
20951974Smsmith     */
21065245Smsmith    if (amr_cam_attach(sc))
21151974Smsmith	return(ENXIO);
21265245Smsmith    debug(2, "CAM attach done");
21365245Smsmith#endif
21451974Smsmith
21551974Smsmith    /*
21665245Smsmith     * Create the control device.
21751974Smsmith     */
21865245Smsmith    sc->amr_dev_t = make_dev(&amr_cdevsw, device_get_unit(sc->amr_dev), UID_ROOT, GID_OPERATOR,
21965245Smsmith			     S_IRUSR | S_IWUSR, "amr%d", device_get_unit(sc->amr_dev));
22065245Smsmith    sc->amr_dev_t->si_drv1 = sc;
22151974Smsmith
22251974Smsmith    /*
22365245Smsmith     * Schedule ourselves to bring the controller up once interrupts are
22465245Smsmith     * available.
22551974Smsmith     */
22665245Smsmith    bzero(&sc->amr_ich, sizeof(struct intr_config_hook));
22765245Smsmith    sc->amr_ich.ich_func = amr_startup;
22865245Smsmith    sc->amr_ich.ich_arg = sc;
22965245Smsmith    if (config_intrhook_establish(&sc->amr_ich) != 0) {
23065245Smsmith	device_printf(sc->amr_dev, "can't establish configuration hook\n");
23165245Smsmith	return(ENOMEM);
23265245Smsmith    }
23351974Smsmith
23458883Smsmith    /*
23565245Smsmith     * Print a little information about the controller.
23658883Smsmith     */
23765245Smsmith    amr_describe_controller(sc);
23858883Smsmith
23965245Smsmith    debug(2, "attach complete");
24051974Smsmith    return(0);
24151974Smsmith}
24251974Smsmith
24351974Smsmith/********************************************************************************
24451974Smsmith * Locate disk resources and attach children to them.
24551974Smsmith */
24665245Smsmithstatic void
24765245Smsmithamr_startup(void *arg)
24851974Smsmith{
24965245Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
25051974Smsmith    struct amr_logdrive	*dr;
25151974Smsmith    int			i, error;
25251974Smsmith
25365245Smsmith    debug_called(1);
25451974Smsmith
25565245Smsmith    /* pull ourselves off the intrhook chain */
25665245Smsmith    config_intrhook_disestablish(&sc->amr_ich);
25765245Smsmith
25851974Smsmith    /* get up-to-date drive information */
25951974Smsmith    if (amr_query_controller(sc)) {
26065245Smsmith	device_printf(sc->amr_dev, "can't scan controller for drives\n");
26151974Smsmith	return;
26251974Smsmith    }
26351974Smsmith
26451974Smsmith    /* iterate over available drives */
26551974Smsmith    for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
26651974Smsmith	/* are we already attached to this drive? */
26751974Smsmith	if (dr->al_disk == 0) {
26851974Smsmith	    /* generate geometry information */
26951974Smsmith	    if (dr->al_size > 0x200000) {	/* extended translation? */
27051974Smsmith		dr->al_heads = 255;
27151974Smsmith		dr->al_sectors = 63;
27251974Smsmith	    } else {
27351974Smsmith		dr->al_heads = 64;
27451974Smsmith		dr->al_sectors = 32;
27551974Smsmith	    }
27651974Smsmith	    dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
27751974Smsmith
27854073Smdodd	    dr->al_disk = device_add_child(sc->amr_dev, NULL, -1);
27951974Smsmith	    if (dr->al_disk == 0)
28051974Smsmith		device_printf(sc->amr_dev, "device_add_child failed\n");
28154073Smdodd	    device_set_ivars(dr->al_disk, dr);
28251974Smsmith	}
28351974Smsmith    }
28451974Smsmith
28551974Smsmith    if ((error = bus_generic_attach(sc->amr_dev)) != 0)
28651974Smsmith	device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
28751974Smsmith
28851974Smsmith    /* mark controller back up */
28951974Smsmith    sc->amr_state &= ~AMR_STATE_SHUTDOWN;
29051974Smsmith
29151974Smsmith    /* interrupts will be enabled before we do anything more */
29251974Smsmith    sc->amr_state |= AMR_STATE_INTEN;
29351974Smsmith
29451974Smsmith    /*
29565245Smsmith     * Start the timeout routine.
29651974Smsmith     */
29765245Smsmith/*    sc->amr_timeout = timeout(amr_periodic, sc, hz);*/
29851974Smsmith
29965245Smsmith    return;
30051974Smsmith}
30151974Smsmith
30265245Smsmith/*******************************************************************************
30365245Smsmith * Free resources associated with a controller instance
30451974Smsmith */
30565245Smsmithvoid
30665245Smsmithamr_free(struct amr_softc *sc)
30751974Smsmith{
30865245Smsmith    struct amr_command_cluster	*acc;
30951974Smsmith
31065245Smsmith#ifdef AMR_SCSI_PASSTHROUGH
31165245Smsmith    /* detach from CAM */
31265245Smsmith    amr_cam_detach(sc);
31365245Smsmith#endif
31451974Smsmith
31565245Smsmith    /* cancel status timeout */
31665245Smsmith    untimeout(amr_periodic, sc, sc->amr_timeout);
31751974Smsmith
31865245Smsmith    /* throw away any command buffers */
31965245Smsmith    while ((acc = TAILQ_FIRST(&sc->amr_cmd_clusters)) != NULL) {
32065245Smsmith	TAILQ_REMOVE(&sc->amr_cmd_clusters, acc, acc_link);
32165245Smsmith	amr_freecmd_cluster(acc);
32251974Smsmith    }
32351974Smsmith}
32451974Smsmith
32551974Smsmith/*******************************************************************************
32665245Smsmith * Receive a bio structure from a child device and queue it on a particular
32751974Smsmith * disk resource, then poke the disk resource to start as much work as it can.
32851974Smsmith */
32951974Smsmithint
33065245Smsmithamr_submit_bio(struct amr_softc *sc, struct bio *bio)
33151974Smsmith{
33265245Smsmith    debug_called(2);
33352543Smsmith
33465245Smsmith    amr_enqueue_bio(sc, bio);
33551974Smsmith    amr_startio(sc);
33651974Smsmith    return(0);
33751974Smsmith}
33851974Smsmith
33951974Smsmith/********************************************************************************
34051974Smsmith * Accept an open operation on the control device.
34151974Smsmith */
34251974Smsmithint
34383366Sjulianamr_open(dev_t dev, int flags, int fmt, struct thread *td)
34451974Smsmith{
34551974Smsmith    int			unit = minor(dev);
34689055Smsmith    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
34751974Smsmith
34865245Smsmith    debug_called(1);
34965245Smsmith
35051974Smsmith    sc->amr_state |= AMR_STATE_OPEN;
35151974Smsmith    return(0);
35251974Smsmith}
35351974Smsmith
35451974Smsmith/********************************************************************************
35551974Smsmith * Accept the last close on the control device.
35651974Smsmith */
35751974Smsmithint
35883366Sjulianamr_close(dev_t dev, int flags, int fmt, struct thread *td)
35951974Smsmith{
36051974Smsmith    int			unit = minor(dev);
36189055Smsmith    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
36251974Smsmith
36365245Smsmith    debug_called(1);
36465245Smsmith
36551974Smsmith    sc->amr_state &= ~AMR_STATE_OPEN;
36651974Smsmith    return (0);
36751974Smsmith}
36851974Smsmith
36951974Smsmith/********************************************************************************
37051974Smsmith * Handle controller-specific control operations.
37151974Smsmith */
37251974Smsmithint
37383366Sjulianamr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
37451974Smsmith{
37565245Smsmith    struct amr_softc		*sc = (struct amr_softc *)dev->si_drv1;
37665245Smsmith    int				*arg = (int *)addr;
37765245Smsmith    struct amr_user_ioctl	*au = (struct amr_user_ioctl *)addr;
37865245Smsmith    struct amr_command		*ac;
37965245Smsmith    struct amr_mailbox_ioctl	*mbi;
38065245Smsmith    struct amr_passthrough	*ap;
38165245Smsmith    void			*dp;
38265245Smsmith    int				error;
38365245Smsmith
38465245Smsmith    debug_called(1);
38565245Smsmith
38665245Smsmith    error = 0;
38765245Smsmith    dp = NULL;
38865245Smsmith    ap = NULL;
38965245Smsmith    ac = NULL;
39051974Smsmith    switch(cmd) {
39165245Smsmith
39265245Smsmith    case AMR_IO_VERSION:
39365245Smsmith	debug(1, "AMR_IO_VERSION");
39465245Smsmith	*arg = AMR_IO_VERSION_NUMBER;
39565245Smsmith	break;
39665245Smsmith
39765245Smsmith    case AMR_IO_COMMAND:
39869319Smsmith	debug(1, "AMR_IO_COMMAND  0x%x", au->au_cmd[0]);
39965245Smsmith	/* handle inbound data buffer */
40065245Smsmith	if (au->au_length != 0) {
40165245Smsmith	    if ((dp = malloc(au->au_length, M_DEVBUF, M_WAITOK)) == NULL) {
40265245Smsmith		error = ENOMEM;
40365245Smsmith		break;
40465245Smsmith	    }
40565245Smsmith	    if ((error = copyin(au->au_buffer, dp, au->au_length)) != 0)
40665245Smsmith		break;
40769319Smsmith	    debug(2, "copyin %ld bytes from %p -> %p", au->au_length, au->au_buffer, dp);
40865245Smsmith	}
40965245Smsmith
41065245Smsmith	if ((ac = amr_alloccmd(sc)) == NULL) {
41165245Smsmith	    error = ENOMEM;
41265245Smsmith	    break;
41365245Smsmith	}
41465245Smsmith
41565245Smsmith	/* handle SCSI passthrough command */
41665245Smsmith	if (au->au_cmd[0] == AMR_CMD_PASS) {
41768877Sdwmalone	    if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) {
41865245Smsmith		error = ENOMEM;
41965245Smsmith		break;
42065245Smsmith	    }
42165245Smsmith
42265245Smsmith	    /* copy cdb */
42365245Smsmith	    ap->ap_cdb_length = au->au_cmd[2];
42465245Smsmith	    bcopy(&au->au_cmd[3], &ap->ap_cdb[0], ap->ap_cdb_length);
42565245Smsmith
42665245Smsmith	    /* build passthrough */
42765245Smsmith	    ap->ap_timeout		= au->au_cmd[ap->ap_cdb_length + 3] & 0x07;
42865245Smsmith	    ap->ap_ars			= (au->au_cmd[ap->ap_cdb_length + 3] & 0x08) ? 1 : 0;
42965245Smsmith	    ap->ap_islogical		= (au->au_cmd[ap->ap_cdb_length + 3] & 0x80) ? 1 : 0;
43065245Smsmith	    ap->ap_logical_drive_no	= au->au_cmd[ap->ap_cdb_length + 4];
43165245Smsmith	    ap->ap_channel		= au->au_cmd[ap->ap_cdb_length + 5];
43265245Smsmith	    ap->ap_scsi_id 		= au->au_cmd[ap->ap_cdb_length + 6];
43365245Smsmith	    ap->ap_request_sense_length	= 14;
43465245Smsmith	    /* XXX what about the request-sense area? does the caller want it? */
43565245Smsmith
43665245Smsmith	    /* build command */
43765245Smsmith	    ac->ac_data = ap;
43865245Smsmith	    ac->ac_length = sizeof(*ap);
43965245Smsmith	    ac->ac_flags |= AMR_CMD_DATAOUT;
44065245Smsmith	    ac->ac_ccb_data = dp;
44165245Smsmith	    ac->ac_ccb_length = au->au_length;
44265245Smsmith	    if (au->au_direction & AMR_IO_READ)
44365245Smsmith		ac->ac_flags |= AMR_CMD_CCB_DATAIN;
44465245Smsmith	    if (au->au_direction & AMR_IO_WRITE)
44565245Smsmith		ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
44665245Smsmith
44765245Smsmith	    ac->ac_mailbox.mb_command = AMR_CMD_PASS;
44865245Smsmith
44965245Smsmith	} else {
45065245Smsmith	    /* direct command to controller */
45165245Smsmith	    mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
45265245Smsmith
45365245Smsmith	    /* copy pertinent mailbox items */
45465245Smsmith	    mbi->mb_command = au->au_cmd[0];
45565245Smsmith	    mbi->mb_channel = au->au_cmd[1];
45665245Smsmith	    mbi->mb_param = au->au_cmd[2];
45765245Smsmith	    mbi->mb_pad[0] = au->au_cmd[3];
45865245Smsmith	    mbi->mb_drive = au->au_cmd[4];
45965245Smsmith
46065245Smsmith	    /* build the command */
46165245Smsmith	    ac->ac_data = dp;
46265245Smsmith	    ac->ac_length = au->au_length;
46365245Smsmith	    if (au->au_direction & AMR_IO_READ)
46465245Smsmith		ac->ac_flags |= AMR_CMD_DATAIN;
46565245Smsmith	    if (au->au_direction & AMR_IO_WRITE)
46665245Smsmith		ac->ac_flags |= AMR_CMD_DATAOUT;
46765245Smsmith	}
46865245Smsmith
46965245Smsmith	/* run the command */
47065245Smsmith	if ((error = amr_wait_command(ac)) != 0)
47165245Smsmith	    break;
47265245Smsmith
47365245Smsmith	/* copy out data and set status */
47465245Smsmith	if (au->au_length != 0)
47565245Smsmith	    error = copyout(dp, au->au_buffer, au->au_length);
47669319Smsmith	debug(2, "copyout %ld bytes from %p -> %p", au->au_length, dp, au->au_buffer);
47769319Smsmith	if (dp != NULL)
47869319Smsmith	    debug(2, "%16D", dp, " ");
47965245Smsmith	au->au_status = ac->ac_status;
48065245Smsmith	break;
48165245Smsmith
48265245Smsmith    default:
48365245Smsmith	debug(1, "unknown ioctl 0x%lx", cmd);
48465245Smsmith	error = ENOIOCTL;
48565245Smsmith	break;
48651974Smsmith    }
48751974Smsmith
48865245Smsmith    if (dp != NULL)
48965245Smsmith	free(dp, M_DEVBUF);
49065245Smsmith    if (ap != NULL)
49165245Smsmith	free(ap, M_DEVBUF);
49265245Smsmith    if (ac != NULL)
49365245Smsmith	amr_releasecmd(ac);
49465245Smsmith    return(error);
49551974Smsmith}
49651974Smsmith
49751974Smsmith/********************************************************************************
49851974Smsmith ********************************************************************************
49958883Smsmith                                                                Status Monitoring
50058883Smsmith ********************************************************************************
50158883Smsmith ********************************************************************************/
50258883Smsmith
50358883Smsmith/********************************************************************************
50458883Smsmith * Perform a periodic check of the controller status
50558883Smsmith */
50658883Smsmithstatic void
50758883Smsmithamr_periodic(void *data)
50858883Smsmith{
50958883Smsmith    struct amr_softc	*sc = (struct amr_softc *)data;
51058883Smsmith
51165245Smsmith    debug_called(2);
51258883Smsmith
51365245Smsmith    /* XXX perform periodic status checks here */
51458883Smsmith
51565245Smsmith    /* compensate for missed interrupts */
51665245Smsmith    amr_done(sc);
51765245Smsmith
51858883Smsmith    /* reschedule */
51958883Smsmith    sc->amr_timeout = timeout(amr_periodic, sc, hz);
52058883Smsmith}
52158883Smsmith
52258883Smsmith/********************************************************************************
52358883Smsmith ********************************************************************************
52451974Smsmith                                                                 Command Wrappers
52551974Smsmith ********************************************************************************
52651974Smsmith ********************************************************************************/
52751974Smsmith
52851974Smsmith/********************************************************************************
52951974Smsmith * Interrogate the controller for the operational parameters we require.
53051974Smsmith */
53151974Smsmithstatic int
53251974Smsmithamr_query_controller(struct amr_softc *sc)
53351974Smsmith{
53465245Smsmith    struct amr_enquiry3	*aex;
53565245Smsmith    struct amr_prodinfo	*ap;
53665245Smsmith    struct amr_enquiry	*ae;
53765245Smsmith    int			ldrv;
53851974Smsmith
53965245Smsmith    /*
54065245Smsmith     * If we haven't found the real limit yet, let us have a couple of commands in
54165245Smsmith     * order to be able to probe.
54265245Smsmith     */
54365245Smsmith    if (sc->amr_maxio == 0)
54465245Smsmith	sc->amr_maxio = 2;
54551974Smsmith
54665245Smsmith    /*
54765245Smsmith     * Try to issue an ENQUIRY3 command
54865245Smsmith     */
54965245Smsmith    if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
55065245Smsmith			   AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) {
55151974Smsmith
55265245Smsmith	/*
55365245Smsmith	 * Fetch current state of logical drives.
55465245Smsmith	 */
55565245Smsmith	for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) {
55665245Smsmith	    sc->amr_drive[ldrv].al_size       = aex->ae_drivesize[ldrv];
55765245Smsmith	    sc->amr_drive[ldrv].al_state      = aex->ae_drivestate[ldrv];
55865245Smsmith	    sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[ldrv];
55965245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
56065245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
56151974Smsmith	}
56265245Smsmith	free(aex, M_DEVBUF);
56358883Smsmith
56465245Smsmith	/*
56565245Smsmith	 * Get product info for channel count.
56658883Smsmith	 */
56765245Smsmith	if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) {
56865245Smsmith	    device_printf(sc->amr_dev, "can't obtain product data from controller\n");
56965245Smsmith	    return(1);
57065245Smsmith	}
57165245Smsmith	sc->amr_maxdrives = 40;
57265245Smsmith	sc->amr_maxchan = ap->ap_nschan;
57365245Smsmith	sc->amr_maxio = ap->ap_maxio;
57465245Smsmith	sc->amr_type |= AMR_TYPE_40LD;
57565245Smsmith	free(ap, M_DEVBUF);
57658883Smsmith
57765245Smsmith    } else {
57865245Smsmith
57965245Smsmith	/* failed, try the 8LD ENQUIRY commands */
58065245Smsmith	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) {
58165245Smsmith	    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
58265245Smsmith		device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
58365245Smsmith		return(1);
58465245Smsmith	    }
58565245Smsmith	    ae->ae_signature = 0;
58651974Smsmith	}
58765245Smsmith
58858883Smsmith	/*
58965245Smsmith	 * Fetch current state of logical drives.
59058883Smsmith	 */
59165245Smsmith	for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) {
59265245Smsmith	    sc->amr_drive[ldrv].al_size       = ae->ae_ldrv.al_size[ldrv];
59365245Smsmith	    sc->amr_drive[ldrv].al_state      = ae->ae_ldrv.al_state[ldrv];
59465245Smsmith	    sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv];
59565245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
59665245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
59751974Smsmith	}
59865245Smsmith
59965245Smsmith	sc->amr_maxdrives = 8;
60065245Smsmith	sc->amr_maxchan = ae->ae_adapter.aa_channels;
60165245Smsmith	sc->amr_maxio = ae->ae_adapter.aa_maxio;
60265245Smsmith	free(ae, M_DEVBUF);
60351974Smsmith    }
60465245Smsmith
60565245Smsmith    /*
60665245Smsmith     * Mark remaining drives as unused.
60765245Smsmith     */
60865245Smsmith    for (; ldrv < AMR_MAXLD; ldrv++)
60965245Smsmith	sc->amr_drive[ldrv].al_size = 0xffffffff;
61065245Smsmith
61165245Smsmith    /*
61265245Smsmith     * Cap the maximum number of outstanding I/Os.  AMI's Linux driver doesn't trust
61365245Smsmith     * the controller's reported value, and lockups have been seen when we do.
61465245Smsmith     */
61565245Smsmith    sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD);
61665245Smsmith
61751974Smsmith    return(0);
61851974Smsmith}
61951974Smsmith
62051974Smsmith/********************************************************************************
62151974Smsmith * Run a generic enquiry-style command.
62251974Smsmith */
62351974Smsmithstatic void *
62451974Smsmithamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
62551974Smsmith{
62651974Smsmith    struct amr_command	*ac;
62751974Smsmith    void		*result;
62851974Smsmith    u_int8_t		*mbox;
62951974Smsmith    int			error;
63051974Smsmith
63165245Smsmith    debug_called(1);
63251974Smsmith
63351974Smsmith    error = 1;
63451974Smsmith    result = NULL;
63551974Smsmith
63651974Smsmith    /* get ourselves a command buffer */
63751974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
63851974Smsmith	goto out;
63951974Smsmith    /* allocate the response structure */
64051974Smsmith    if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
64151974Smsmith	goto out;
64265245Smsmith    /* set command flags */
64351974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
64451974Smsmith
64565245Smsmith    /* point the command at our data */
64651974Smsmith    ac->ac_data = result;
64751974Smsmith    ac->ac_length = bufsize;
64851974Smsmith
64951974Smsmith    /* build the command proper */
65051974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
65151974Smsmith    mbox[0] = cmd;
65251974Smsmith    mbox[2] = cmdsub;
65351974Smsmith    mbox[3] = cmdqual;
65451974Smsmith
65558883Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
65658883Smsmith    if (amr_poll_command(ac))
65751974Smsmith	goto out;
65851974Smsmith    error = ac->ac_status;
65951974Smsmith
66051974Smsmith out:
66151974Smsmith    if (ac != NULL)
66251974Smsmith	amr_releasecmd(ac);
66351974Smsmith    if ((error != 0) && (result != NULL)) {
66451974Smsmith	free(result, M_DEVBUF);
66551974Smsmith	result = NULL;
66651974Smsmith    }
66751974Smsmith    return(result);
66851974Smsmith}
66951974Smsmith
67051974Smsmith/********************************************************************************
67151974Smsmith * Flush the controller's internal cache, return status.
67251974Smsmith */
67365245Smsmithint
67451974Smsmithamr_flush(struct amr_softc *sc)
67551974Smsmith{
67651974Smsmith    struct amr_command	*ac;
67751974Smsmith    int			error;
67851974Smsmith
67951974Smsmith    /* get ourselves a command buffer */
68051974Smsmith    error = 1;
68151974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
68251974Smsmith	goto out;
68365245Smsmith    /* set command flags */
68451974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
68551974Smsmith
68651974Smsmith    /* build the command proper */
68751974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
68851974Smsmith
68958883Smsmith    /* we have to poll, as the system may be going down or otherwise damaged */
69058883Smsmith    if (amr_poll_command(ac))
69151974Smsmith	goto out;
69251974Smsmith    error = ac->ac_status;
69351974Smsmith
69451974Smsmith out:
69551974Smsmith    if (ac != NULL)
69651974Smsmith	amr_releasecmd(ac);
69751974Smsmith    return(error);
69851974Smsmith}
69951974Smsmith
70051974Smsmith/********************************************************************************
70165245Smsmith * Try to find I/O work for the controller from one or more of the work queues.
70251974Smsmith *
70365245Smsmith * We make the assumption that if the controller is not ready to take a command
70465245Smsmith * at some given time, it will generate an interrupt at some later time when
70565245Smsmith * it is.
70651974Smsmith */
70765245Smsmithvoid
70851974Smsmithamr_startio(struct amr_softc *sc)
70951974Smsmith{
71051974Smsmith    struct amr_command	*ac;
71151974Smsmith
71251974Smsmith    /* spin until something prevents us from doing any work */
71351974Smsmith    for (;;) {
71451974Smsmith
71565245Smsmith	/* try to get a ready command */
71665245Smsmith	ac = amr_dequeue_ready(sc);
71751974Smsmith
71865245Smsmith	/* if that failed, build a command from a bio */
71965245Smsmith	if (ac == NULL)
72065245Smsmith	    (void)amr_bio_command(sc, &ac);
72151974Smsmith
72265245Smsmith#ifdef AMR_SCSI_PASSTHROUGH
72365245Smsmith	/* if that failed, build a command from a ccb */
72465245Smsmith	if (ac == NULL)
72565245Smsmith	    (void)amr_cam_command(sc, &ac);
72665245Smsmith#endif
72765245Smsmith
72865245Smsmith	/* if we don't have anything to do, give up */
72965245Smsmith	if (ac == NULL)
73065245Smsmith	    break;
73151974Smsmith
73265245Smsmith	/* try to give the command to the controller; if this fails save it for later and give up */
73365245Smsmith	if (amr_start(ac)) {
73465245Smsmith	    debug(2, "controller busy, command deferred");
73565245Smsmith	    amr_requeue_ready(ac);	/* XXX schedule retry very soon? */
73665245Smsmith	    break;
73751974Smsmith	}
73851974Smsmith    }
73951974Smsmith}
74051974Smsmith
74151974Smsmith/********************************************************************************
74251974Smsmith * Handle completion of an I/O command.
74351974Smsmith */
74451974Smsmithstatic void
74551974Smsmithamr_completeio(struct amr_command *ac)
74651974Smsmith{
74751974Smsmith    struct amr_softc	*sc = ac->ac_sc;
74851974Smsmith
74951974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
75065245Smsmith	ac->ac_bio->bio_error = EIO;
75165245Smsmith	ac->ac_bio->bio_flags |= BIO_ERROR;
75251974Smsmith
75365245Smsmith	device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status);
75465245Smsmith/*	amr_printcommand(ac);*/
75551974Smsmith    }
75665245Smsmith    amrd_intr(ac->ac_bio);
75765245Smsmith    amr_releasecmd(ac);
75851974Smsmith}
75951974Smsmith
76051974Smsmith/********************************************************************************
76151974Smsmith ********************************************************************************
76251974Smsmith                                                               Command Processing
76351974Smsmith ********************************************************************************
76451974Smsmith ********************************************************************************/
76551974Smsmith
76651974Smsmith/********************************************************************************
76765245Smsmith * Convert a bio off the top of the bio queue into a command.
76865245Smsmith */
76965245Smsmithstatic int
77065245Smsmithamr_bio_command(struct amr_softc *sc, struct amr_command **acp)
77165245Smsmith{
77265245Smsmith    struct amr_command	*ac;
77365245Smsmith    struct amrd_softc	*amrd;
77465245Smsmith    struct bio		*bio;
77565245Smsmith    int			error;
77665245Smsmith    int			blkcount;
77765245Smsmith    int			driveno;
77865245Smsmith    int			cmd;
77965245Smsmith
78065245Smsmith    ac = NULL;
78165245Smsmith    error = 0;
78265245Smsmith
78365245Smsmith    /* get a bio to work on */
78465245Smsmith    if ((bio = amr_dequeue_bio(sc)) == NULL)
78565245Smsmith	goto out;
78665245Smsmith
78765245Smsmith    /* get a command */
78865245Smsmith    if ((ac = amr_alloccmd(sc)) == NULL) {
78965245Smsmith	error = ENOMEM;
79065245Smsmith	goto out;
79165245Smsmith    }
79265245Smsmith
79365245Smsmith    /* connect the bio to the command */
79465245Smsmith    ac->ac_complete = amr_completeio;
79565245Smsmith    ac->ac_bio = bio;
79665245Smsmith    ac->ac_data = bio->bio_data;
79765245Smsmith    ac->ac_length = bio->bio_bcount;
79865245Smsmith    if (BIO_IS_READ(bio)) {
79965245Smsmith	ac->ac_flags |= AMR_CMD_DATAIN;
80065245Smsmith	cmd = AMR_CMD_LREAD;
80165245Smsmith    } else {
80265245Smsmith	ac->ac_flags |= AMR_CMD_DATAOUT;
80365245Smsmith	cmd = AMR_CMD_LWRITE;
80465245Smsmith    }
80565245Smsmith    amrd = (struct amrd_softc *)bio->bio_dev->si_drv1;
80665245Smsmith    driveno = amrd->amrd_drive - sc->amr_drive;
80765245Smsmith    blkcount = (bio->bio_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
80865245Smsmith
80965245Smsmith    ac->ac_mailbox.mb_command = cmd;
81065245Smsmith    ac->ac_mailbox.mb_blkcount = blkcount;
81165245Smsmith    ac->ac_mailbox.mb_lba = bio->bio_pblkno;
81265245Smsmith    ac->ac_mailbox.mb_drive = driveno;
81365245Smsmith    /* we fill in the s/g related data when the command is mapped */
81465245Smsmith
81565245Smsmith    if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
81665245Smsmith	device_printf(sc->amr_dev, "I/O beyond end of unit (%u,%d > %u)\n",
81765245Smsmith		      bio->bio_pblkno, blkcount, sc->amr_drive[driveno].al_size);
81865245Smsmith
81965245Smsmithout:
82065245Smsmith    if (error != 0) {
82165245Smsmith	if (ac != NULL)
82265245Smsmith	    amr_releasecmd(ac);
82365245Smsmith	if (bio != NULL)			/* this breaks ordering... */
82465245Smsmith	    amr_enqueue_bio(sc, bio);
82565245Smsmith    }
82665245Smsmith    *acp = ac;
82765245Smsmith    return(error);
82865245Smsmith}
82965245Smsmith
83065245Smsmith/********************************************************************************
83151974Smsmith * Take a command, submit it to the controller and sleep until it completes
83251974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
83351974Smsmith */
83451974Smsmithstatic int
83551974Smsmithamr_wait_command(struct amr_command *ac)
83651974Smsmith{
83751974Smsmith    int			error, count;
83851974Smsmith
83965245Smsmith    debug_called(1);
84051974Smsmith
84151974Smsmith    ac->ac_complete = NULL;
84265245Smsmith    ac->ac_flags |= AMR_CMD_SLEEP;
84351974Smsmith    if ((error = amr_start(ac)) != 0)
84451974Smsmith	return(error);
84551974Smsmith
84651974Smsmith    count = 0;
84751974Smsmith    /* XXX better timeout? */
84865245Smsmith    while ((ac->ac_flags & AMR_CMD_BUSY) && (count < 30)) {
84965245Smsmith	tsleep(ac, PRIBIO | PCATCH, "amrwcmd", hz);
85051974Smsmith    }
85151974Smsmith    return(0);
85251974Smsmith}
85351974Smsmith
85451974Smsmith/********************************************************************************
85551974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
85651974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
85751974Smsmith */
85851974Smsmithstatic int
85951974Smsmithamr_poll_command(struct amr_command *ac)
86051974Smsmith{
86151974Smsmith    struct amr_softc	*sc = ac->ac_sc;
86265245Smsmith    int			error, count;
86351974Smsmith
86465245Smsmith    debug_called(2);
86551974Smsmith
86651974Smsmith    ac->ac_complete = NULL;
86751974Smsmith    if ((error = amr_start(ac)) != 0)
86851974Smsmith	return(error);
86951974Smsmith
87051974Smsmith    count = 0;
87151974Smsmith    do {
87251974Smsmith	/*
87351974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
87451974Smsmith	 * Note that the timeout here is somewhat arbitrary.
87551974Smsmith	 */
87651974Smsmith	amr_done(sc);
87765245Smsmith	DELAY(1000);
87865245Smsmith    } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000));
87965245Smsmith    if (!(ac->ac_flags & AMR_CMD_BUSY)) {
88051974Smsmith	error = 0;
88151974Smsmith    } else {
88265245Smsmith	/* XXX the slot is now marked permanently busy */
88351974Smsmith	error = EIO;
88465245Smsmith	device_printf(sc->amr_dev, "polled command timeout\n");
88551974Smsmith    }
88651974Smsmith    return(error);
88751974Smsmith}
88851974Smsmith
88951974Smsmith/********************************************************************************
89065245Smsmith * Get a free command slot for a command if it doesn't already have one.
89165245Smsmith *
89265245Smsmith * May be safely called multiple times for a given command.
89351974Smsmith */
89451974Smsmithstatic int
89551974Smsmithamr_getslot(struct amr_command *ac)
89651974Smsmith{
89751974Smsmith    struct amr_softc	*sc = ac->ac_sc;
89865245Smsmith    int			s, slot, limit, error;
89951974Smsmith
90065245Smsmith    debug_called(3);
90165245Smsmith
90265245Smsmith    /* if the command already has a slot, don't try to give it another one */
90365245Smsmith    if (ac->ac_slot != 0)
90465245Smsmith	return(0);
90565245Smsmith
90651974Smsmith    /* enforce slot usage limit */
90751974Smsmith    limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
90865245Smsmith    if (sc->amr_busyslots > limit)
90951974Smsmith	return(EBUSY);
91051974Smsmith
91151974Smsmith    /*
91265245Smsmith     * Allocate a slot.  XXX linear scan is slow
91351974Smsmith     */
91465245Smsmith    error = EBUSY;
91551974Smsmith    s = splbio();
91651974Smsmith    for (slot = 0; slot < sc->amr_maxio; slot++) {
91765245Smsmith	if (sc->amr_busycmd[slot] == NULL) {
91865245Smsmith	    sc->amr_busycmd[slot] = ac;
91965245Smsmith	    sc->amr_busyslots++;
92065245Smsmith	    ac->ac_slot = slot;
92165245Smsmith	    error = 0;
92251974Smsmith	    break;
92365245Smsmith	}
92451974Smsmith    }
92551974Smsmith    splx(s);
92651974Smsmith
92765245Smsmith    return(error);
92851974Smsmith}
92951974Smsmith
93051974Smsmith/********************************************************************************
93165245Smsmith * Map/unmap (ac)'s data in the controller's addressable space as required.
93265245Smsmith *
93365245Smsmith * These functions may be safely called multiple times on a given command.
93451974Smsmith */
93551974Smsmithstatic void
93651974Smsmithamr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
93751974Smsmith{
93851974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
93951974Smsmith    struct amr_softc	*sc = ac->ac_sc;
94051974Smsmith    struct amr_sgentry	*sg;
94151974Smsmith    int			i;
94269319Smsmith    u_int8_t		*sgc;
94351974Smsmith
94465245Smsmith    debug_called(3);
94551974Smsmith
94651974Smsmith    /* get base address of s/g table */
94751974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
94851974Smsmith
94965245Smsmith    /* save data physical address */
95051974Smsmith    ac->ac_dataphys = segs[0].ds_addr;
95151974Smsmith
95269319Smsmith    /* for AMR_CMD_CONFIG the s/g count goes elsewhere */
95369319Smsmith    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) {
95469319Smsmith	sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param);
95569319Smsmith    } else {
95669319Smsmith	sgc = &ac->ac_mailbox.mb_nsgelem;
95769319Smsmith    }
95869319Smsmith
95965245Smsmith    /* decide whether we need to populate the s/g table */
96065245Smsmith    if (nsegments < 2) {
96169319Smsmith	*sgc = 0;
96265245Smsmith	ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
96365245Smsmith    } else {
96469319Smsmith	*sgc = nsegments;
96565245Smsmith	ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
96665245Smsmith	for (i = 0; i < nsegments; i++, sg++) {
96765245Smsmith	    sg->sg_addr = segs[i].ds_addr;
96865245Smsmith	    sg->sg_count = segs[i].ds_len;
96965245Smsmith	}
97065245Smsmith    }
97165245Smsmith}
97265245Smsmith
97365245Smsmithstatic void
97465245Smsmithamr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
97565245Smsmith{
97665245Smsmith    struct amr_command		*ac = (struct amr_command *)arg;
97765245Smsmith    struct amr_softc		*sc = ac->ac_sc;
97865245Smsmith    struct amr_sgentry		*sg;
97965245Smsmith    struct amr_passthrough	*ap = (struct amr_passthrough *)ac->ac_data;
98065245Smsmith    int				i;
98165245Smsmith
98265245Smsmith    /* get base address of s/g table */
98365245Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
98465245Smsmith
98565245Smsmith    /* save s/g table information in passthrough */
98665245Smsmith    ap->ap_no_sg_elements = nsegments;
98765245Smsmith    ap->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
98865245Smsmith
98965245Smsmith    /* save pointer to passthrough in command   XXX is this already done above? */
99065245Smsmith    ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
99165245Smsmith
99269319Smsmith    debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
99365245Smsmith	   ap->ap_no_sg_elements, ap->ap_data_transfer_address, ac->ac_dataphys);
99465245Smsmith
99565245Smsmith    /* populate s/g table (overwrites previous call which mapped the passthrough) */
99651974Smsmith    for (i = 0; i < nsegments; i++, sg++) {
99751974Smsmith	sg->sg_addr = segs[i].ds_addr;
99851974Smsmith	sg->sg_count = segs[i].ds_len;
99969319Smsmith	debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
100051974Smsmith    }
100151974Smsmith}
100251974Smsmith
100351974Smsmithstatic void
100451974Smsmithamr_mapcmd(struct amr_command *ac)
100551974Smsmith{
100651974Smsmith    struct amr_softc	*sc = ac->ac_sc;
100751974Smsmith
100869319Smsmith    debug_called(3);
100951974Smsmith
101065245Smsmith    /* if the command involves data at all, and hasn't been mapped */
101165245Smsmith    if (!(ac->ac_flags & AMR_CMD_MAPPED)) {
101265245Smsmith
101365245Smsmith	if (ac->ac_data != NULL) {
101465245Smsmith	    /* map the data buffers into bus space and build the s/g list */
101565245Smsmith	    bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
101665245Smsmith			    amr_setup_dmamap, ac, 0);
101765245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
101865245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
101965245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
102065245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
102165245Smsmith	}
102265245Smsmith
102365245Smsmith	if (ac->ac_ccb_data != NULL) {
102465245Smsmith	    bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, ac->ac_ccb_data, ac->ac_ccb_length,
102565245Smsmith			    amr_setup_ccbmap, ac, 0);
102665245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
102765245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREREAD);
102865245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
102965245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREWRITE);
103065245Smsmith	}
103165245Smsmith	ac->ac_flags |= AMR_CMD_MAPPED;
103251974Smsmith    }
103351974Smsmith}
103451974Smsmith
103551974Smsmithstatic void
103651974Smsmithamr_unmapcmd(struct amr_command *ac)
103751974Smsmith{
103851974Smsmith    struct amr_softc	*sc = ac->ac_sc;
103951974Smsmith
104069319Smsmith    debug_called(3);
104151974Smsmith
104265245Smsmith    /* if the command involved data at all and was mapped */
104365245Smsmith    if (ac->ac_flags & AMR_CMD_MAPPED) {
104451974Smsmith
104565245Smsmith	if (ac->ac_data != NULL) {
104665245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
104765245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
104865245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
104965245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
105065245Smsmith	    bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
105165245Smsmith	}
105265245Smsmith
105365245Smsmith	if (ac->ac_ccb_data != NULL) {
105465245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
105565245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTREAD);
105665245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
105765245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTWRITE);
105865245Smsmith	    bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap);
105965245Smsmith	}
106065245Smsmith	ac->ac_flags &= ~AMR_CMD_MAPPED;
106151974Smsmith    }
106251974Smsmith}
106351974Smsmith
106451974Smsmith/********************************************************************************
106565245Smsmith * Take a command and give it to the controller, returns 0 if successful, or
106665245Smsmith * EBUSY if the command should be retried later.
106751974Smsmith */
106851974Smsmithstatic int
106951974Smsmithamr_start(struct amr_command *ac)
107051974Smsmith{
107151974Smsmith    struct amr_softc	*sc = ac->ac_sc;
107258883Smsmith    int			done, s, i;
107351974Smsmith
107469319Smsmith    debug_called(3);
107551974Smsmith
107665245Smsmith    /* mark command as busy so that polling consumer can tell */
107765245Smsmith    ac->ac_flags |= AMR_CMD_BUSY;
107865245Smsmith
107965245Smsmith    /* get a command slot (freed in amr_done) */
108065245Smsmith    if (amr_getslot(ac))
108165245Smsmith	return(EBUSY);
108265245Smsmith
108365245Smsmith    /* now we have a slot, we can map the command (unmapped in amr_complete) */
108465245Smsmith    amr_mapcmd(ac);
108565245Smsmith
108665245Smsmith    /* mark the new mailbox we are going to copy in as busy */
108765245Smsmith    ac->ac_mailbox.mb_busy = 1;
108865245Smsmith
108965245Smsmith    /* clear the poll/ack fields in the mailbox */
109065245Smsmith    sc->amr_mailbox->mb_poll = 0;
109165245Smsmith    sc->amr_mailbox->mb_ack = 0;
109265245Smsmith
109351974Smsmith    /*
109451974Smsmith     * Save the slot number so that we can locate this command when complete.
109551974Smsmith     * Note that ident = 0 seems to be special, so we don't use it.
109651974Smsmith     */
109751974Smsmith    ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
109851974Smsmith
109958883Smsmith    /*
110065245Smsmith     * Spin waiting for the mailbox, give up after ~1 second.  We expect the
110165245Smsmith     * controller to be able to handle our I/O.
110265245Smsmith     *
110365245Smsmith     * XXX perhaps we should wait for less time, and count on the deferred command
110465245Smsmith     * handling to deal with retries?
110558883Smsmith     */
110669319Smsmith    debug(4, "wait for mailbox");
110758883Smsmith    for (i = 10000, done = 0; (i > 0) && !done; i--) {
110851974Smsmith	s = splbio();
110951974Smsmith
111051974Smsmith	/* is the mailbox free? */
111151974Smsmith	if (sc->amr_mailbox->mb_busy == 0) {
111269319Smsmith	    debug(4, "got mailbox");
111351974Smsmith	    sc->amr_mailbox64->mb64_segment = 0;
111465245Smsmith	    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
111551974Smsmith	    done = 1;
111651974Smsmith
111765245Smsmith	    /* not free, spin waiting */
111851974Smsmith	} else {
111969319Smsmith	    debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy);
112058883Smsmith	    /* this is somewhat ugly */
112158883Smsmith	    DELAY(100);
112251974Smsmith	}
112358883Smsmith	splx(s);	/* drop spl to allow completion interrupts */
112451974Smsmith    }
112565245Smsmith
112665245Smsmith    /*
112765245Smsmith     * Now give the command to the controller
112865245Smsmith     */
112951974Smsmith    if (done) {
113065245Smsmith	if (sc->amr_submit_command(sc)) {
113165245Smsmith	    /* the controller wasn't ready to take the command, forget that we tried to post it */
113265245Smsmith	    sc->amr_mailbox->mb_busy = 0;
113365245Smsmith	    return(EBUSY);
113465245Smsmith	}
113569319Smsmith	debug(3, "posted command");
113651974Smsmith	return(0);
113751974Smsmith    }
113851974Smsmith
113951974Smsmith    /*
114065245Smsmith     * The controller wouldn't take the command.  Return the command as busy
114165245Smsmith     * so that it is retried later.
114251974Smsmith     */
114365245Smsmith    return(EBUSY);
114451974Smsmith}
114551974Smsmith
114651974Smsmith/********************************************************************************
114751974Smsmith * Extract one or more completed commands from the controller (sc)
114851974Smsmith *
114952543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
115051974Smsmith */
115165245Smsmithint
115251974Smsmithamr_done(struct amr_softc *sc)
115351974Smsmith{
115451974Smsmith    struct amr_command	*ac;
115551974Smsmith    struct amr_mailbox	mbox;
115665245Smsmith    int			i, idx, result;
115751974Smsmith
115869319Smsmith    debug_called(3);
115951974Smsmith
116051974Smsmith    /* See if there's anything for us to do */
116151974Smsmith    result = 0;
116251974Smsmith
116358883Smsmith    /* loop collecting completed commands */
116458883Smsmith    for (;;) {
116558883Smsmith	/* poll for a completed command's identifier and status */
116658883Smsmith	if (sc->amr_get_work(sc, &mbox)) {
116758883Smsmith	    result = 1;
116858883Smsmith
116958883Smsmith	    /* iterate over completed commands in this result */
117058883Smsmith	    for (i = 0; i < mbox.mb_nstatus; i++) {
117158883Smsmith		/* get pointer to busy command */
117258883Smsmith		idx = mbox.mb_completed[i] - 1;
117358883Smsmith		ac = sc->amr_busycmd[idx];
117451974Smsmith
117558883Smsmith		/* really a busy command? */
117658883Smsmith		if (ac != NULL) {
117758883Smsmith
117858883Smsmith		    /* pull the command from the busy index */
117958883Smsmith		    sc->amr_busycmd[idx] = NULL;
118065245Smsmith		    sc->amr_busyslots--;
118151974Smsmith
118265245Smsmith		    /* save status for later use */
118365245Smsmith		    ac->ac_status = mbox.mb_status;
118465245Smsmith		    amr_enqueue_completed(ac);
118565245Smsmith		    debug(3, "completed command with status %x", mbox.mb_status);
118665245Smsmith		} else {
118765245Smsmith		    device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
118851974Smsmith		}
118951974Smsmith	    }
119058883Smsmith	} else {
119165245Smsmith	    break;	/* no work */
119251974Smsmith	}
119351974Smsmith    }
119458883Smsmith
119558883Smsmith    /* if we've completed any commands, try posting some more */
119658883Smsmith    if (result)
119758883Smsmith	amr_startio(sc);
119858883Smsmith
119958883Smsmith    /* handle completion and timeouts */
120065245Smsmith#if __FreeBSD_version >= 500005
120165245Smsmith    if (sc->amr_state & AMR_STATE_INTEN)
120265245Smsmith	taskqueue_enqueue(taskqueue_swi, &sc->amr_task_complete);
120365245Smsmith    else
120465245Smsmith#endif
120565245Smsmith	amr_complete(sc, 0);
120658883Smsmith
120751974Smsmith    return(result);
120851974Smsmith}
120951974Smsmith
121051974Smsmith/********************************************************************************
121151974Smsmith * Do completion processing on done commands on (sc)
121251974Smsmith */
121351974Smsmithstatic void
121465245Smsmithamr_complete(void *context, int pending)
121551974Smsmith{
121665245Smsmith    struct amr_softc	*sc = (struct amr_softc *)context;
121765245Smsmith    struct amr_command	*ac;
121851974Smsmith
121969319Smsmith    debug_called(3);
122051974Smsmith
122165245Smsmith    /* pull completed commands off the queue */
122265245Smsmith    for (;;) {
122365245Smsmith	ac = amr_dequeue_completed(sc);
122465245Smsmith	if (ac == NULL)
122565245Smsmith	    break;
122658883Smsmith
122765245Smsmith	/* unmap the command's data buffer */
122865245Smsmith	amr_unmapcmd(ac);
122951974Smsmith
123065245Smsmith	/* unbusy the command */
123165245Smsmith	ac->ac_flags &= ~AMR_CMD_BUSY;
123251974Smsmith
123365245Smsmith	/*
123465245Smsmith	 * Is there a completion handler?
123565245Smsmith	 */
123665245Smsmith	if (ac->ac_complete != NULL) {
123765245Smsmith	    ac->ac_complete(ac);
123865245Smsmith
123951974Smsmith	    /*
124065245Smsmith	     * Is someone sleeping on this one?
124151974Smsmith	     */
124265245Smsmith	} else if (ac->ac_flags & AMR_CMD_SLEEP) {
124365245Smsmith	    wakeup(ac);
124451974Smsmith	}
124551974Smsmith    }
124651974Smsmith}
124751974Smsmith
124851974Smsmith/********************************************************************************
124951974Smsmith ********************************************************************************
125051974Smsmith                                                        Command Buffer Management
125151974Smsmith ********************************************************************************
125251974Smsmith ********************************************************************************/
125351974Smsmith
125451974Smsmith/********************************************************************************
125551974Smsmith * Get a new command buffer.
125651974Smsmith *
125751974Smsmith * This may return NULL in low-memory cases.
125851974Smsmith *
125951974Smsmith * If possible, we recycle a command buffer that's been used before.
126051974Smsmith */
126165245Smsmithstruct amr_command *
126251974Smsmithamr_alloccmd(struct amr_softc *sc)
126351974Smsmith{
126451974Smsmith    struct amr_command	*ac;
126551974Smsmith
126665245Smsmith    debug_called(3);
126751974Smsmith
126865245Smsmith    ac = amr_dequeue_free(sc);
126951974Smsmith    if (ac == NULL) {
127065245Smsmith	amr_alloccmd_cluster(sc);
127165245Smsmith	ac = amr_dequeue_free(sc);
127251974Smsmith    }
127365245Smsmith    if (ac == NULL)
127465245Smsmith	return(NULL);
127565245Smsmith
127665245Smsmith    /* clear out significant fields */
127765245Smsmith    ac->ac_slot = 0;
127865245Smsmith    ac->ac_status = 0;
127951974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
128065245Smsmith    ac->ac_flags = 0;
128165245Smsmith    ac->ac_bio = NULL;
128265245Smsmith    ac->ac_data = NULL;
128365245Smsmith    ac->ac_ccb_data = NULL;
128465245Smsmith    ac->ac_complete = NULL;
128551974Smsmith    return(ac);
128651974Smsmith}
128751974Smsmith
128851974Smsmith/********************************************************************************
128951974Smsmith * Release a command buffer for recycling.
129051974Smsmith */
129165245Smsmithvoid
129251974Smsmithamr_releasecmd(struct amr_command *ac)
129351974Smsmith{
129465245Smsmith    debug_called(3);
129551974Smsmith
129665245Smsmith    amr_enqueue_free(ac);
129751974Smsmith}
129851974Smsmith
129951974Smsmith/********************************************************************************
130065245Smsmith * Allocate a new command cluster and initialise it.
130151974Smsmith */
130265245Smsmithvoid
130365245Smsmithamr_alloccmd_cluster(struct amr_softc *sc)
130451974Smsmith{
130565245Smsmith    struct amr_command_cluster	*acc;
130665245Smsmith    struct amr_command		*ac;
130765245Smsmith    int				s, i;
130851974Smsmith
130965245Smsmith    acc = malloc(AMR_CMD_CLUSTERSIZE, M_DEVBUF, M_NOWAIT);
131065245Smsmith    if (acc != NULL) {
131165245Smsmith	s = splbio();
131265245Smsmith	TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
131365245Smsmith	splx(s);
131465245Smsmith	for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
131565245Smsmith	    ac = &acc->acc_command[i];
131665245Smsmith	    bzero(ac, sizeof(*ac));
131765245Smsmith	    ac->ac_sc = sc;
131865245Smsmith	    if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) &&
131965245Smsmith		!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap))
132065245Smsmith		amr_releasecmd(ac);
132165245Smsmith	}
132265245Smsmith    }
132351974Smsmith}
132451974Smsmith
132551974Smsmith/********************************************************************************
132665245Smsmith * Free a command cluster
132765245Smsmith */
132865245Smsmithvoid
132965245Smsmithamr_freecmd_cluster(struct amr_command_cluster *acc)
133065245Smsmith{
133165245Smsmith    struct amr_softc	*sc = acc->acc_command[0].ac_sc;
133265245Smsmith    int			i;
133365245Smsmith
133465245Smsmith    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++)
133565245Smsmith	bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
133665245Smsmith    free(acc, M_DEVBUF);
133765245Smsmith}
133865245Smsmith
133965245Smsmith/********************************************************************************
134051974Smsmith ********************************************************************************
134151974Smsmith                                                         Interface-specific Shims
134251974Smsmith ********************************************************************************
134351974Smsmith ********************************************************************************/
134451974Smsmith
134551974Smsmith/********************************************************************************
134651974Smsmith * Tell the controller that the mailbox contains a valid command
134751974Smsmith */
134865245Smsmithstatic int
134951974Smsmithamr_quartz_submit_command(struct amr_softc *sc)
135051974Smsmith{
135165245Smsmith    debug_called(3);
135251974Smsmith
135365245Smsmith    if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
135465245Smsmith	return(EBUSY);
135551974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
135665245Smsmith    return(0);
135751974Smsmith}
135851974Smsmith
135965245Smsmithstatic int
136051974Smsmithamr_std_submit_command(struct amr_softc *sc)
136151974Smsmith{
136265245Smsmith    debug_called(3);
136351974Smsmith
136465245Smsmith    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
136565245Smsmith	return(EBUSY);
136651974Smsmith    AMR_SPOST_COMMAND(sc);
136765245Smsmith    return(0);
136851974Smsmith}
136951974Smsmith
137051974Smsmith/********************************************************************************
137151974Smsmith * Claim any work that the controller has completed; acknowledge completion,
137251974Smsmith * save details of the completion in (mbsave)
137351974Smsmith */
137451974Smsmithstatic int
137551974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
137651974Smsmith{
137751974Smsmith    int		s, worked;
137851974Smsmith    u_int32_t	outd;
137951974Smsmith
138065245Smsmith    debug_called(3);
138165245Smsmith
138251974Smsmith    worked = 0;
138351974Smsmith    s = splbio();
138451974Smsmith
138551974Smsmith    /* work waiting for us? */
138651974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
138751974Smsmith
138851974Smsmith	/* save mailbox, which contains a list of completed commands */
138965245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
139051974Smsmith
139165245Smsmith	/* acknowledge interrupt */
139265245Smsmith	AMR_QPUT_ODB(sc, AMR_QODB_READY);
139365245Smsmith
139451974Smsmith	/* acknowledge that we have the commands */
139551974Smsmith	AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
139665245Smsmith
139765763Smsmith#ifndef AMR_QUARTZ_GOFASTER
139865245Smsmith	/*
139965245Smsmith	 * This waits for the controller to notice that we've taken the
140065245Smsmith	 * command from it.  It's very inefficient, and we shouldn't do it,
140165245Smsmith	 * but if we remove this code, we stop completing commands under
140265245Smsmith	 * load.
140365245Smsmith	 *
140465245Smsmith	 * Peter J says we shouldn't do this.  The documentation says we
140565245Smsmith	 * should.  Who is right?
140665245Smsmith	 */
140751974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
140851974Smsmith	    ;				/* XXX aiee! what if it dies? */
140965245Smsmith#endif
141065245Smsmith
141151974Smsmith	worked = 1;			/* got some work */
141251974Smsmith    }
141351974Smsmith
141451974Smsmith    splx(s);
141551974Smsmith    return(worked);
141651974Smsmith}
141751974Smsmith
141851974Smsmithstatic int
141951974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
142051974Smsmith{
142151974Smsmith    int		s, worked;
142251974Smsmith    u_int8_t	istat;
142351974Smsmith
142465245Smsmith    debug_called(3);
142551974Smsmith
142651974Smsmith    worked = 0;
142751974Smsmith    s = splbio();
142851974Smsmith
142951974Smsmith    /* check for valid interrupt status */
143051974Smsmith    istat = AMR_SGET_ISTAT(sc);
143151974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
143251974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
143351974Smsmith
143451974Smsmith	/* save mailbox, which contains a list of completed commands */
143565245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
143651974Smsmith
143751974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
143851974Smsmith	worked = 1;
143951974Smsmith    }
144051974Smsmith
144151974Smsmith    splx(s);
144251974Smsmith    return(worked);
144351974Smsmith}
144451974Smsmith
144551974Smsmith/********************************************************************************
144651974Smsmith * Notify the controller of the mailbox location.
144751974Smsmith */
144851974Smsmithstatic void
144951974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
145051974Smsmith{
145151974Smsmith
145251974Smsmith    /* program the mailbox physical address */
145351974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
145451974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
145551974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
145651974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
145751974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
145851974Smsmith
145951974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
146051974Smsmith    AMR_SACK_INTERRUPT(sc);
146151974Smsmith    AMR_SENABLE_INTR(sc);
146251974Smsmith}
146351974Smsmith
146465245Smsmith#ifdef AMR_BOARD_INIT
146551974Smsmith/********************************************************************************
146665245Smsmith * Initialise the controller
146765245Smsmith */
146865245Smsmithstatic int
146965245Smsmithamr_quartz_init(struct amr_softc *sc)
147065245Smsmith{
147165245Smsmith    int		status, ostatus;
147265245Smsmith
147365245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc));
147465245Smsmith
147565245Smsmith    AMR_QRESET(sc);
147665245Smsmith
147765245Smsmith    ostatus = 0xff;
147865245Smsmith    while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) {
147965245Smsmith	if (status != ostatus) {
148065245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status));
148165245Smsmith	    ostatus = status;
148265245Smsmith	}
148365245Smsmith	switch (status) {
148465245Smsmith	case AMR_QINIT_NOMEM:
148565245Smsmith	    return(ENOMEM);
148665245Smsmith
148765245Smsmith	case AMR_QINIT_SCAN:
148865245Smsmith	    /* XXX we could print channel/target here */
148965245Smsmith	    break;
149065245Smsmith	}
149165245Smsmith    }
149265245Smsmith    return(0);
149365245Smsmith}
149465245Smsmith
149565245Smsmithstatic int
149665245Smsmithamr_std_init(struct amr_softc *sc)
149765245Smsmith{
149865245Smsmith    int		status, ostatus;
149965245Smsmith
150065245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc));
150165245Smsmith
150265245Smsmith    AMR_SRESET(sc);
150365245Smsmith
150465245Smsmith    ostatus = 0xff;
150565245Smsmith    while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) {
150665245Smsmith	if (status != ostatus) {
150765245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status));
150865245Smsmith	    ostatus = status;
150965245Smsmith	}
151065245Smsmith	switch (status) {
151165245Smsmith	case AMR_SINIT_NOMEM:
151265245Smsmith	    return(ENOMEM);
151365245Smsmith
151465245Smsmith	case AMR_SINIT_INPROG:
151565245Smsmith	    /* XXX we could print channel/target here? */
151665245Smsmith	    break;
151765245Smsmith	}
151865245Smsmith    }
151965245Smsmith    return(0);
152065245Smsmith}
152165245Smsmith#endif
152265245Smsmith
152365245Smsmith/********************************************************************************
152451974Smsmith ********************************************************************************
152551974Smsmith                                                                        Debugging
152651974Smsmith ********************************************************************************
152751974Smsmith ********************************************************************************/
152851974Smsmith
152951974Smsmith/********************************************************************************
153065245Smsmith * Identify the controller and print some information about it.
153165245Smsmith */
153265245Smsmithstatic void
153365245Smsmithamr_describe_controller(struct amr_softc *sc)
153465245Smsmith{
153565245Smsmith    struct amr_prodinfo	*ap;
153665245Smsmith    struct amr_enquiry	*ae;
153765245Smsmith    char		*prod;
153865245Smsmith
153965245Smsmith    /*
154065245Smsmith     * Try to get 40LD product info, which tells us what the card is labelled as.
154165245Smsmith     */
154265245Smsmith    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) {
154365245Smsmith	device_printf(sc->amr_dev, "<%.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
154465245Smsmith		      ap->ap_product, ap->ap_firmware, ap->ap_bios,
154565245Smsmith		      ap->ap_memsize);
154665245Smsmith
154765245Smsmith	free(ap, M_DEVBUF);
154865245Smsmith	return;
154965245Smsmith    }
155065245Smsmith
155165245Smsmith    /*
155265245Smsmith     * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
155365245Smsmith     */
155465245Smsmith    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) {
155565245Smsmith	prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
155665245Smsmith
155765245Smsmith    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) {
155865245Smsmith
155965245Smsmith	/*
156065245Smsmith	 * Try to work it out based on the PCI signatures.
156165245Smsmith	 */
156265245Smsmith	switch (pci_get_device(sc->amr_dev)) {
156365245Smsmith	case 0x9010:
156465245Smsmith	    prod = "Series 428";
156565245Smsmith	    break;
156665245Smsmith	case 0x9060:
156765245Smsmith	    prod = "Series 434";
156865245Smsmith	    break;
156965245Smsmith	default:
157065245Smsmith	    prod = "unknown controller";
157165245Smsmith	    break;
157265245Smsmith	}
157365245Smsmith    } else {
157465245Smsmith	prod = "unsupported controller";
157565245Smsmith    }
157674936Shm
157774936Shm    /*
157874936Shm     * HP NetRaid controllers have a special encoding of the firmware and
157974936Shm     * BIOS versions. The AMI version seems to have it as strings whereas
158074936Shm     * the HP version does it with a leading uppercase character and two
158174936Shm     * binary numbers.
158274936Shm     */
158374936Shm
158474936Shm    if(ae->ae_adapter.aa_firmware[2] >= 'A' &&
158574936Shm       ae->ae_adapter.aa_firmware[2] <= 'Z' &&
158674936Shm       ae->ae_adapter.aa_firmware[1] <  ' ' &&
158774936Shm       ae->ae_adapter.aa_firmware[0] <  ' ' &&
158874936Shm       ae->ae_adapter.aa_bios[2] >= 'A'     &&
158974936Shm       ae->ae_adapter.aa_bios[2] <= 'Z'     &&
159074936Shm       ae->ae_adapter.aa_bios[1] <  ' '     &&
159174936Shm       ae->ae_adapter.aa_bios[0] <  ' ') {
159274936Shm
159374936Shm	/* this looks like we have an HP NetRaid version of the MegaRaid */
159474936Shm
159574936Shm    	if(ae->ae_signature == AMR_SIG_438) {
159674936Shm    		/* the AMI 438 is an NetRaid 3si in HP-land */
159774936Shm    		prod = "HP NetRaid 3si";
159874936Shm    	}
159974936Shm
160074936Shm	device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n",
160174936Shm		      prod, ae->ae_adapter.aa_firmware[2],
160274936Shm		      ae->ae_adapter.aa_firmware[1],
160374936Shm		      ae->ae_adapter.aa_firmware[0],
160474936Shm		      ae->ae_adapter.aa_bios[2],
160574936Shm		      ae->ae_adapter.aa_bios[1],
160674936Shm		      ae->ae_adapter.aa_bios[0],
160774936Shm		      ae->ae_adapter.aa_memorysize);
160874936Shm    } else {
160974936Shm	device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n",
161074936Shm		      prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
161174936Shm		      ae->ae_adapter.aa_memorysize);
161274936Shm    }
161365245Smsmith    free(ae, M_DEVBUF);
161465245Smsmith}
161565245Smsmith
161665245Smsmith#ifdef AMR_DEBUG
161765245Smsmith/********************************************************************************
161851974Smsmith * Print the command (ac) in human-readable format
161951974Smsmith */
162051974Smsmithstatic void
162151974Smsmithamr_printcommand(struct amr_command *ac)
162251974Smsmith{
162351974Smsmith    struct amr_softc	*sc = ac->ac_sc;
162451974Smsmith    struct amr_sgentry	*sg;
162551974Smsmith    int			i;
162651974Smsmith
162751974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
162851974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
162951974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
163051974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
163154512Speter    device_printf(sc->amr_dev, "virtaddr %p  length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
163258883Smsmith    device_printf(sc->amr_dev, "sg physaddr %08x  nsg %d\n",
163351974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
163465245Smsmith    device_printf(sc->amr_dev, "ccb %p  bio %p\n", ac->ac_ccb_data, ac->ac_bio);
163551974Smsmith
163651974Smsmith    /* get base address of s/g table */
163751974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
163851974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
163951974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
164051974Smsmith}
164165245Smsmith#endif
1642