amr.c revision 107756
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 *
27106225Semoore * Copyright (c) 2002 Eric Moore
28106225Semoore * Copyright (c) 2002 LSI Logic Corporation
29106225Semoore * All rights reserved.
30106225Semoore *
31106225Semoore * Redistribution and use in source and binary forms, with or without
32106225Semoore * modification, are permitted provided that the following conditions
33106225Semoore * are met:
34106225Semoore * 1. Redistributions of source code must retain the above copyright
35106225Semoore *    notice, this list of conditions and the following disclaimer.
36106225Semoore * 2. Redistributions in binary form must reproduce the above copyright
37106225Semoore *    notice, this list of conditions and the following disclaimer in the
38106225Semoore *    documentation and/or other materials provided with the distribution.
39105419Semoore * 3. The party using or redistributing the source code and binary forms
40106225Semoore *    agrees to the disclaimer below and the terms and conditions set forth
41105419Semoore *    herein.
42105419Semoore *
43106225Semoore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
44106225Semoore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45106225Semoore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46106225Semoore * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
47106225Semoore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48106225Semoore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49106225Semoore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50106225Semoore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51106225Semoore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52106225Semoore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53106225Semoore * SUCH DAMAGE.
54105419Semoore *
55106225Semoore *
5651974Smsmith *	$FreeBSD: head/sys/dev/amr/amr.c 107756 2002-12-11 20:59:46Z emoore $
5751974Smsmith */
5851974Smsmith
5951974Smsmith/*
6065245Smsmith * Driver for the AMI MegaRaid family of controllers.
6151974Smsmith */
6251974Smsmith
6351974Smsmith#include <sys/param.h>
6451974Smsmith#include <sys/systm.h>
6551974Smsmith#include <sys/malloc.h>
6651974Smsmith#include <sys/kernel.h>
6751974Smsmith
6865245Smsmith#include <dev/amr/amr_compat.h>
6951974Smsmith#include <sys/bus.h>
7051974Smsmith#include <sys/conf.h>
7151974Smsmith#include <sys/devicestat.h>
7251974Smsmith#include <sys/disk.h>
7365245Smsmith#include <sys/stat.h>
7451974Smsmith
7565245Smsmith#include <machine/bus_memio.h>
7665245Smsmith#include <machine/bus_pio.h>
7765245Smsmith#include <machine/bus.h>
7851974Smsmith#include <machine/resource.h>
7951974Smsmith#include <sys/rman.h>
8051974Smsmith
8165245Smsmith#include <pci/pcireg.h>
8265245Smsmith#include <pci/pcivar.h>
8365245Smsmith
8451974Smsmith#include <dev/amr/amrio.h>
8551974Smsmith#include <dev/amr/amrreg.h>
8651974Smsmith#include <dev/amr/amrvar.h>
8765245Smsmith#define AMR_DEFINE_TABLES
8865245Smsmith#include <dev/amr/amr_tables.h>
8951974Smsmith
9051974Smsmith#define AMR_CDEV_MAJOR	132
9151974Smsmith
9265245Smsmithstatic d_open_t         amr_open;
9365245Smsmithstatic d_close_t        amr_close;
9465245Smsmithstatic d_ioctl_t        amr_ioctl;
9565245Smsmith
9651974Smsmithstatic struct cdevsw amr_cdevsw = {
9751974Smsmith		/* open */	amr_open,
9851974Smsmith		/* close */	amr_close,
9951974Smsmith		/* read */	noread,
10051974Smsmith		/* write */	nowrite,
10151974Smsmith		/* ioctl */	amr_ioctl,
10251974Smsmith		/* poll */	nopoll,
10351974Smsmith		/* mmap */	nommap,
10451974Smsmith		/* strategy */	nostrategy,
10551974Smsmith		/* name */ 	"amr",
10651974Smsmith		/* maj */	AMR_CDEV_MAJOR,
10751974Smsmith		/* dump */	nodump,
10851974Smsmith		/* psize */ 	nopsize,
10951974Smsmith		/* flags */	0,
11051974Smsmith};
11151974Smsmith
11265245Smsmith/*
11365245Smsmith * Initialisation, bus interface.
11465245Smsmith */
11565245Smsmithstatic void	amr_startup(void *arg);
11651974Smsmith
11751974Smsmith/*
11851974Smsmith * Command wrappers
11951974Smsmith */
12065245Smsmithstatic int	amr_query_controller(struct amr_softc *sc);
12165245Smsmithstatic void	*amr_enquiry(struct amr_softc *sc, size_t bufsize,
12265245Smsmith			     u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual);
12365245Smsmithstatic void	amr_completeio(struct amr_command *ac);
124106225Semoorestatic int	amr_support_ext_cdb(struct amr_softc *sc);
12551974Smsmith
12651974Smsmith/*
12765245Smsmith * Command buffer allocation.
12851974Smsmith */
12965245Smsmithstatic void	amr_alloccmd_cluster(struct amr_softc *sc);
13065245Smsmithstatic void	amr_freecmd_cluster(struct amr_command_cluster *acc);
13151974Smsmith
13251974Smsmith/*
13365245Smsmith * Command processing.
13451974Smsmith */
13565245Smsmithstatic int	amr_bio_command(struct amr_softc *sc, struct amr_command **acp);
13665245Smsmithstatic int	amr_wait_command(struct amr_command *ac);
13765245Smsmithstatic int	amr_getslot(struct amr_command *ac);
13865245Smsmithstatic void	amr_mapcmd(struct amr_command *ac);
13965245Smsmithstatic void	amr_unmapcmd(struct amr_command *ac);
14065245Smsmithstatic int	amr_start(struct amr_command *ac);
14165245Smsmithstatic void	amr_complete(void *context, int pending);
14251974Smsmith
14351974Smsmith/*
14458883Smsmith * Status monitoring
14558883Smsmith */
14665245Smsmithstatic void	amr_periodic(void *data);
14758883Smsmith
14858883Smsmith/*
14951974Smsmith * Interface-specific shims
15051974Smsmith */
15165245Smsmithstatic int	amr_quartz_submit_command(struct amr_softc *sc);
15265245Smsmithstatic int	amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
153107756Semoorestatic int	amr_quartz_poll_command(struct amr_command *ac);
15451974Smsmith
15565245Smsmithstatic int	amr_std_submit_command(struct amr_softc *sc);
15665245Smsmithstatic int	amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
157107756Semoorestatic int	amr_std_poll_command(struct amr_command *ac);
15865245Smsmithstatic void	amr_std_attach_mailbox(struct amr_softc *sc);
15951974Smsmith
16065245Smsmith#ifdef AMR_BOARD_INIT
16165245Smsmithstatic int	amr_quartz_init(struct amr_softc *sc);
16265245Smsmithstatic int	amr_std_init(struct amr_softc *sc);
16365245Smsmith#endif
16465245Smsmith
16551974Smsmith/*
16651974Smsmith * Debugging
16751974Smsmith */
16865245Smsmithstatic void	amr_describe_controller(struct amr_softc *sc);
16965245Smsmith#ifdef AMR_DEBUG
170107756Semoore#if 0
17165245Smsmithstatic void	amr_printcommand(struct amr_command *ac);
17265245Smsmith#endif
173107756Semoore#endif
17451974Smsmith
17551974Smsmith/********************************************************************************
17651974Smsmith ********************************************************************************
17765245Smsmith                                                                      Inline Glue
17851974Smsmith ********************************************************************************
17951974Smsmith ********************************************************************************/
18051974Smsmith
18151974Smsmith/********************************************************************************
18265245Smsmith ********************************************************************************
18365245Smsmith                                                                Public Interfaces
18465245Smsmith ********************************************************************************
18565245Smsmith ********************************************************************************/
18651974Smsmith
18751974Smsmith/********************************************************************************
18851974Smsmith * Initialise the controller and softc.
18951974Smsmith */
19051974Smsmithint
19151974Smsmithamr_attach(struct amr_softc *sc)
19251974Smsmith{
19351974Smsmith
19465245Smsmith    debug_called(1);
19565245Smsmith
19651974Smsmith    /*
19751974Smsmith     * Initialise per-controller queues.
19851974Smsmith     */
19965245Smsmith    TAILQ_INIT(&sc->amr_completed);
20051974Smsmith    TAILQ_INIT(&sc->amr_freecmds);
20165245Smsmith    TAILQ_INIT(&sc->amr_cmd_clusters);
20265245Smsmith    TAILQ_INIT(&sc->amr_ready);
20359249Sphk    bioq_init(&sc->amr_bioq);
20451974Smsmith
20565245Smsmith#if __FreeBSD_version >= 500005
20651974Smsmith    /*
20765245Smsmith     * Initialise command-completion task.
20865245Smsmith     */
20965245Smsmith    TASK_INIT(&sc->amr_task_complete, 0, amr_complete, sc);
21065245Smsmith#endif
21165245Smsmith
21265245Smsmith    debug(2, "queue init done");
21365245Smsmith
21465245Smsmith    /*
21551974Smsmith     * Configure for this controller type.
21651974Smsmith     */
21765245Smsmith    if (AMR_IS_QUARTZ(sc)) {
21851974Smsmith	sc->amr_submit_command = amr_quartz_submit_command;
21951974Smsmith	sc->amr_get_work       = amr_quartz_get_work;
220107756Semoore	sc->amr_poll_command   = amr_quartz_poll_command;
22151974Smsmith    } else {
22251974Smsmith	sc->amr_submit_command = amr_std_submit_command;
22351974Smsmith	sc->amr_get_work       = amr_std_get_work;
224107756Semoore	sc->amr_poll_command   = amr_std_poll_command;
22565245Smsmith	amr_std_attach_mailbox(sc);;
22651974Smsmith    }
22751974Smsmith
22865245Smsmith#ifdef AMR_BOARD_INIT
22965245Smsmith    if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc))))
23065245Smsmith	return(ENXIO);
23165245Smsmith#endif
23251974Smsmith
23351974Smsmith    /*
23465245Smsmith     * Quiz controller for features and limits.
23551974Smsmith     */
23665245Smsmith    if (amr_query_controller(sc))
23765245Smsmith	return(ENXIO);
23851974Smsmith
23965245Smsmith    debug(2, "controller query complete");
24051974Smsmith
24151974Smsmith    /*
24265245Smsmith     * Attach our 'real' SCSI channels to CAM.
24351974Smsmith     */
24465245Smsmith    if (amr_cam_attach(sc))
24551974Smsmith	return(ENXIO);
24665245Smsmith    debug(2, "CAM attach done");
24751974Smsmith
24851974Smsmith    /*
24965245Smsmith     * Create the control device.
25051974Smsmith     */
25165245Smsmith    sc->amr_dev_t = make_dev(&amr_cdevsw, device_get_unit(sc->amr_dev), UID_ROOT, GID_OPERATOR,
25265245Smsmith			     S_IRUSR | S_IWUSR, "amr%d", device_get_unit(sc->amr_dev));
25365245Smsmith    sc->amr_dev_t->si_drv1 = sc;
25451974Smsmith
25551974Smsmith    /*
25665245Smsmith     * Schedule ourselves to bring the controller up once interrupts are
25765245Smsmith     * available.
25851974Smsmith     */
25965245Smsmith    bzero(&sc->amr_ich, sizeof(struct intr_config_hook));
26065245Smsmith    sc->amr_ich.ich_func = amr_startup;
26165245Smsmith    sc->amr_ich.ich_arg = sc;
26265245Smsmith    if (config_intrhook_establish(&sc->amr_ich) != 0) {
26365245Smsmith	device_printf(sc->amr_dev, "can't establish configuration hook\n");
26465245Smsmith	return(ENOMEM);
26565245Smsmith    }
26651974Smsmith
26758883Smsmith    /*
26865245Smsmith     * Print a little information about the controller.
26958883Smsmith     */
27065245Smsmith    amr_describe_controller(sc);
27158883Smsmith
27265245Smsmith    debug(2, "attach complete");
27351974Smsmith    return(0);
27451974Smsmith}
27551974Smsmith
27651974Smsmith/********************************************************************************
27751974Smsmith * Locate disk resources and attach children to them.
27851974Smsmith */
27965245Smsmithstatic void
28065245Smsmithamr_startup(void *arg)
28151974Smsmith{
28265245Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
28351974Smsmith    struct amr_logdrive	*dr;
28451974Smsmith    int			i, error;
28551974Smsmith
28665245Smsmith    debug_called(1);
28751974Smsmith
28865245Smsmith    /* pull ourselves off the intrhook chain */
28965245Smsmith    config_intrhook_disestablish(&sc->amr_ich);
29065245Smsmith
29151974Smsmith    /* get up-to-date drive information */
29251974Smsmith    if (amr_query_controller(sc)) {
29365245Smsmith	device_printf(sc->amr_dev, "can't scan controller for drives\n");
29451974Smsmith	return;
29551974Smsmith    }
29651974Smsmith
29751974Smsmith    /* iterate over available drives */
29851974Smsmith    for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
29951974Smsmith	/* are we already attached to this drive? */
30051974Smsmith	if (dr->al_disk == 0) {
30151974Smsmith	    /* generate geometry information */
30251974Smsmith	    if (dr->al_size > 0x200000) {	/* extended translation? */
30351974Smsmith		dr->al_heads = 255;
30451974Smsmith		dr->al_sectors = 63;
30551974Smsmith	    } else {
30651974Smsmith		dr->al_heads = 64;
30751974Smsmith		dr->al_sectors = 32;
30851974Smsmith	    }
30951974Smsmith	    dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
31051974Smsmith
31154073Smdodd	    dr->al_disk = device_add_child(sc->amr_dev, NULL, -1);
31251974Smsmith	    if (dr->al_disk == 0)
31351974Smsmith		device_printf(sc->amr_dev, "device_add_child failed\n");
31454073Smdodd	    device_set_ivars(dr->al_disk, dr);
31551974Smsmith	}
31651974Smsmith    }
31751974Smsmith
31851974Smsmith    if ((error = bus_generic_attach(sc->amr_dev)) != 0)
31951974Smsmith	device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
32051974Smsmith
32151974Smsmith    /* mark controller back up */
32251974Smsmith    sc->amr_state &= ~AMR_STATE_SHUTDOWN;
32351974Smsmith
32451974Smsmith    /* interrupts will be enabled before we do anything more */
32551974Smsmith    sc->amr_state |= AMR_STATE_INTEN;
32651974Smsmith
32751974Smsmith    /*
32865245Smsmith     * Start the timeout routine.
32951974Smsmith     */
33065245Smsmith/*    sc->amr_timeout = timeout(amr_periodic, sc, hz);*/
33151974Smsmith
33265245Smsmith    return;
33351974Smsmith}
33451974Smsmith
33565245Smsmith/*******************************************************************************
33665245Smsmith * Free resources associated with a controller instance
33751974Smsmith */
33865245Smsmithvoid
33965245Smsmithamr_free(struct amr_softc *sc)
34051974Smsmith{
34165245Smsmith    struct amr_command_cluster	*acc;
34251974Smsmith
34365245Smsmith    /* detach from CAM */
344107756Semoore    amr_cam_detach(sc);
34551974Smsmith
34665245Smsmith    /* cancel status timeout */
34765245Smsmith    untimeout(amr_periodic, sc, sc->amr_timeout);
34851974Smsmith
34965245Smsmith    /* throw away any command buffers */
35065245Smsmith    while ((acc = TAILQ_FIRST(&sc->amr_cmd_clusters)) != NULL) {
35165245Smsmith	TAILQ_REMOVE(&sc->amr_cmd_clusters, acc, acc_link);
35265245Smsmith	amr_freecmd_cluster(acc);
35351974Smsmith    }
354107756Semoore
355107756Semoore    /* destroy control device */
356107756Semoore    if( sc->amr_dev_t != (dev_t)NULL)
357107756Semoore	    destroy_dev(sc->amr_dev_t);
35851974Smsmith}
35951974Smsmith
36051974Smsmith/*******************************************************************************
36165245Smsmith * Receive a bio structure from a child device and queue it on a particular
36251974Smsmith * disk resource, then poke the disk resource to start as much work as it can.
36351974Smsmith */
36451974Smsmithint
36565245Smsmithamr_submit_bio(struct amr_softc *sc, struct bio *bio)
36651974Smsmith{
36765245Smsmith    debug_called(2);
36852543Smsmith
36965245Smsmith    amr_enqueue_bio(sc, bio);
37051974Smsmith    amr_startio(sc);
37151974Smsmith    return(0);
37251974Smsmith}
37351974Smsmith
37451974Smsmith/********************************************************************************
37551974Smsmith * Accept an open operation on the control device.
37651974Smsmith */
377104094Sphkstatic int
378105419Semooreamr_open(dev_t dev, int flags, int fmt, d_thread_t *td)
37951974Smsmith{
38051974Smsmith    int			unit = minor(dev);
38189055Smsmith    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
38251974Smsmith
38365245Smsmith    debug_called(1);
38465245Smsmith
38551974Smsmith    sc->amr_state |= AMR_STATE_OPEN;
38651974Smsmith    return(0);
38751974Smsmith}
38851974Smsmith
38951974Smsmith/********************************************************************************
39051974Smsmith * Accept the last close on the control device.
39151974Smsmith */
392104094Sphkstatic int
393105419Semooreamr_close(dev_t dev, int flags, int fmt, d_thread_t *td)
39451974Smsmith{
39551974Smsmith    int			unit = minor(dev);
39689055Smsmith    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
39751974Smsmith
39865245Smsmith    debug_called(1);
39965245Smsmith
40051974Smsmith    sc->amr_state &= ~AMR_STATE_OPEN;
40151974Smsmith    return (0);
40251974Smsmith}
40351974Smsmith
40451974Smsmith/********************************************************************************
40551974Smsmith * Handle controller-specific control operations.
40651974Smsmith */
407104094Sphkstatic int
408105419Semooreamr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t *td)
40951974Smsmith{
41065245Smsmith    struct amr_softc		*sc = (struct amr_softc *)dev->si_drv1;
41165245Smsmith    int				*arg = (int *)addr;
41265245Smsmith    struct amr_user_ioctl	*au = (struct amr_user_ioctl *)addr;
41365245Smsmith    struct amr_command		*ac;
41465245Smsmith    struct amr_mailbox_ioctl	*mbi;
41565245Smsmith    struct amr_passthrough	*ap;
41665245Smsmith    void			*dp;
41765245Smsmith    int				error;
41865245Smsmith
41965245Smsmith    debug_called(1);
42065245Smsmith
42165245Smsmith    error = 0;
42265245Smsmith    dp = NULL;
42365245Smsmith    ap = NULL;
42465245Smsmith    ac = NULL;
42551974Smsmith    switch(cmd) {
42665245Smsmith
42765245Smsmith    case AMR_IO_VERSION:
42865245Smsmith	debug(1, "AMR_IO_VERSION");
42965245Smsmith	*arg = AMR_IO_VERSION_NUMBER;
43065245Smsmith	break;
43165245Smsmith
43265245Smsmith    case AMR_IO_COMMAND:
43369319Smsmith	debug(1, "AMR_IO_COMMAND  0x%x", au->au_cmd[0]);
43465245Smsmith	/* handle inbound data buffer */
43565245Smsmith	if (au->au_length != 0) {
43665245Smsmith	    if ((dp = malloc(au->au_length, M_DEVBUF, M_WAITOK)) == NULL) {
43765245Smsmith		error = ENOMEM;
43865245Smsmith		break;
43965245Smsmith	    }
44065245Smsmith	    if ((error = copyin(au->au_buffer, dp, au->au_length)) != 0)
44165245Smsmith		break;
44269319Smsmith	    debug(2, "copyin %ld bytes from %p -> %p", au->au_length, au->au_buffer, dp);
44365245Smsmith	}
44465245Smsmith
44565245Smsmith	if ((ac = amr_alloccmd(sc)) == NULL) {
44665245Smsmith	    error = ENOMEM;
44765245Smsmith	    break;
44865245Smsmith	}
44965245Smsmith
45065245Smsmith	/* handle SCSI passthrough command */
45165245Smsmith	if (au->au_cmd[0] == AMR_CMD_PASS) {
45268877Sdwmalone	    if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) {
45365245Smsmith		error = ENOMEM;
45465245Smsmith		break;
45565245Smsmith	    }
45665245Smsmith
45765245Smsmith	    /* copy cdb */
45865245Smsmith	    ap->ap_cdb_length = au->au_cmd[2];
45965245Smsmith	    bcopy(&au->au_cmd[3], &ap->ap_cdb[0], ap->ap_cdb_length);
46065245Smsmith
46165245Smsmith	    /* build passthrough */
46265245Smsmith	    ap->ap_timeout		= au->au_cmd[ap->ap_cdb_length + 3] & 0x07;
46365245Smsmith	    ap->ap_ars			= (au->au_cmd[ap->ap_cdb_length + 3] & 0x08) ? 1 : 0;
46465245Smsmith	    ap->ap_islogical		= (au->au_cmd[ap->ap_cdb_length + 3] & 0x80) ? 1 : 0;
46565245Smsmith	    ap->ap_logical_drive_no	= au->au_cmd[ap->ap_cdb_length + 4];
46665245Smsmith	    ap->ap_channel		= au->au_cmd[ap->ap_cdb_length + 5];
46765245Smsmith	    ap->ap_scsi_id 		= au->au_cmd[ap->ap_cdb_length + 6];
46865245Smsmith	    ap->ap_request_sense_length	= 14;
469105419Semoore	    ap->ap_data_transfer_length = au->au_length;
47065245Smsmith	    /* XXX what about the request-sense area? does the caller want it? */
47165245Smsmith
47265245Smsmith	    /* build command */
47365245Smsmith	    ac->ac_data = ap;
47465245Smsmith	    ac->ac_length = sizeof(*ap);
47565245Smsmith	    ac->ac_flags |= AMR_CMD_DATAOUT;
47665245Smsmith	    ac->ac_ccb_data = dp;
47765245Smsmith	    ac->ac_ccb_length = au->au_length;
47865245Smsmith	    if (au->au_direction & AMR_IO_READ)
47965245Smsmith		ac->ac_flags |= AMR_CMD_CCB_DATAIN;
48065245Smsmith	    if (au->au_direction & AMR_IO_WRITE)
48165245Smsmith		ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
48265245Smsmith
48365245Smsmith	    ac->ac_mailbox.mb_command = AMR_CMD_PASS;
48465245Smsmith
48565245Smsmith	} else {
48665245Smsmith	    /* direct command to controller */
48765245Smsmith	    mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
48865245Smsmith
48965245Smsmith	    /* copy pertinent mailbox items */
49065245Smsmith	    mbi->mb_command = au->au_cmd[0];
49165245Smsmith	    mbi->mb_channel = au->au_cmd[1];
49265245Smsmith	    mbi->mb_param = au->au_cmd[2];
49365245Smsmith	    mbi->mb_pad[0] = au->au_cmd[3];
49465245Smsmith	    mbi->mb_drive = au->au_cmd[4];
49565245Smsmith
49665245Smsmith	    /* build the command */
49765245Smsmith	    ac->ac_data = dp;
49865245Smsmith	    ac->ac_length = au->au_length;
49965245Smsmith	    if (au->au_direction & AMR_IO_READ)
50065245Smsmith		ac->ac_flags |= AMR_CMD_DATAIN;
50165245Smsmith	    if (au->au_direction & AMR_IO_WRITE)
50265245Smsmith		ac->ac_flags |= AMR_CMD_DATAOUT;
50365245Smsmith	}
50465245Smsmith
50565245Smsmith	/* run the command */
50665245Smsmith	if ((error = amr_wait_command(ac)) != 0)
50765245Smsmith	    break;
50865245Smsmith
50965245Smsmith	/* copy out data and set status */
51065245Smsmith	if (au->au_length != 0)
51165245Smsmith	    error = copyout(dp, au->au_buffer, au->au_length);
51269319Smsmith	debug(2, "copyout %ld bytes from %p -> %p", au->au_length, dp, au->au_buffer);
51369319Smsmith	if (dp != NULL)
514107756Semoore	    debug(2, "%16d", (int)dp);
51565245Smsmith	au->au_status = ac->ac_status;
51665245Smsmith	break;
517105419Semoore
51865245Smsmith    default:
51965245Smsmith	debug(1, "unknown ioctl 0x%lx", cmd);
52065245Smsmith	error = ENOIOCTL;
52165245Smsmith	break;
52251974Smsmith    }
52351974Smsmith
52465245Smsmith    if (dp != NULL)
52565245Smsmith	free(dp, M_DEVBUF);
52665245Smsmith    if (ap != NULL)
52765245Smsmith	free(ap, M_DEVBUF);
52865245Smsmith    if (ac != NULL)
52965245Smsmith	amr_releasecmd(ac);
53065245Smsmith    return(error);
53151974Smsmith}
53251974Smsmith
53351974Smsmith/********************************************************************************
53451974Smsmith ********************************************************************************
53558883Smsmith                                                                Status Monitoring
53658883Smsmith ********************************************************************************
53758883Smsmith ********************************************************************************/
53858883Smsmith
53958883Smsmith/********************************************************************************
54058883Smsmith * Perform a periodic check of the controller status
54158883Smsmith */
54258883Smsmithstatic void
54358883Smsmithamr_periodic(void *data)
54458883Smsmith{
54558883Smsmith    struct amr_softc	*sc = (struct amr_softc *)data;
54658883Smsmith
54765245Smsmith    debug_called(2);
54858883Smsmith
54965245Smsmith    /* XXX perform periodic status checks here */
55058883Smsmith
55165245Smsmith    /* compensate for missed interrupts */
55265245Smsmith    amr_done(sc);
55365245Smsmith
55458883Smsmith    /* reschedule */
55558883Smsmith    sc->amr_timeout = timeout(amr_periodic, sc, hz);
55658883Smsmith}
55758883Smsmith
55858883Smsmith/********************************************************************************
55958883Smsmith ********************************************************************************
56051974Smsmith                                                                 Command Wrappers
56151974Smsmith ********************************************************************************
56251974Smsmith ********************************************************************************/
56351974Smsmith
56451974Smsmith/********************************************************************************
56551974Smsmith * Interrogate the controller for the operational parameters we require.
56651974Smsmith */
56751974Smsmithstatic int
56851974Smsmithamr_query_controller(struct amr_softc *sc)
56951974Smsmith{
57065245Smsmith    struct amr_enquiry3	*aex;
57165245Smsmith    struct amr_prodinfo	*ap;
57265245Smsmith    struct amr_enquiry	*ae;
57365245Smsmith    int			ldrv;
57451974Smsmith
57565245Smsmith    /*
57665245Smsmith     * If we haven't found the real limit yet, let us have a couple of commands in
57765245Smsmith     * order to be able to probe.
57865245Smsmith     */
57965245Smsmith    if (sc->amr_maxio == 0)
58065245Smsmith	sc->amr_maxio = 2;
58151974Smsmith
582106225Semoore    /*
583106225Semoore     * Greater than 10 byte cdb support
584106225Semoore     */
585106225Semoore    sc->support_ext_cdb = amr_support_ext_cdb(sc);
586106225Semoore
587106225Semoore    if(sc->support_ext_cdb) {
588106225Semoore	debug(2,"supports extended CDBs.");
589106225Semoore    }
590106225Semoore
59165245Smsmith    /*
59265245Smsmith     * Try to issue an ENQUIRY3 command
59365245Smsmith     */
59465245Smsmith    if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
59565245Smsmith			   AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) {
59651974Smsmith
59765245Smsmith	/*
59865245Smsmith	 * Fetch current state of logical drives.
59965245Smsmith	 */
60065245Smsmith	for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) {
60165245Smsmith	    sc->amr_drive[ldrv].al_size       = aex->ae_drivesize[ldrv];
60265245Smsmith	    sc->amr_drive[ldrv].al_state      = aex->ae_drivestate[ldrv];
60365245Smsmith	    sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[ldrv];
60465245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
60565245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
60651974Smsmith	}
60765245Smsmith	free(aex, M_DEVBUF);
60858883Smsmith
60965245Smsmith	/*
61065245Smsmith	 * Get product info for channel count.
61158883Smsmith	 */
61265245Smsmith	if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) {
61365245Smsmith	    device_printf(sc->amr_dev, "can't obtain product data from controller\n");
61465245Smsmith	    return(1);
61565245Smsmith	}
61665245Smsmith	sc->amr_maxdrives = 40;
61765245Smsmith	sc->amr_maxchan = ap->ap_nschan;
61865245Smsmith	sc->amr_maxio = ap->ap_maxio;
61965245Smsmith	sc->amr_type |= AMR_TYPE_40LD;
62065245Smsmith	free(ap, M_DEVBUF);
62158883Smsmith
62265245Smsmith    } else {
62365245Smsmith
62465245Smsmith	/* failed, try the 8LD ENQUIRY commands */
62565245Smsmith	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) {
62665245Smsmith	    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
62765245Smsmith		device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
62865245Smsmith		return(1);
62965245Smsmith	    }
63065245Smsmith	    ae->ae_signature = 0;
63151974Smsmith	}
63265245Smsmith
63358883Smsmith	/*
63465245Smsmith	 * Fetch current state of logical drives.
63558883Smsmith	 */
63665245Smsmith	for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) {
63765245Smsmith	    sc->amr_drive[ldrv].al_size       = ae->ae_ldrv.al_size[ldrv];
63865245Smsmith	    sc->amr_drive[ldrv].al_state      = ae->ae_ldrv.al_state[ldrv];
63965245Smsmith	    sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv];
64065245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
64165245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
64251974Smsmith	}
64365245Smsmith
64465245Smsmith	sc->amr_maxdrives = 8;
64565245Smsmith	sc->amr_maxchan = ae->ae_adapter.aa_channels;
64665245Smsmith	sc->amr_maxio = ae->ae_adapter.aa_maxio;
64765245Smsmith	free(ae, M_DEVBUF);
64851974Smsmith    }
64965245Smsmith
65065245Smsmith    /*
65165245Smsmith     * Mark remaining drives as unused.
65265245Smsmith     */
65365245Smsmith    for (; ldrv < AMR_MAXLD; ldrv++)
65465245Smsmith	sc->amr_drive[ldrv].al_size = 0xffffffff;
65565245Smsmith
65665245Smsmith    /*
65765245Smsmith     * Cap the maximum number of outstanding I/Os.  AMI's Linux driver doesn't trust
65865245Smsmith     * the controller's reported value, and lockups have been seen when we do.
65965245Smsmith     */
66065245Smsmith    sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD);
66165245Smsmith
66251974Smsmith    return(0);
66351974Smsmith}
66451974Smsmith
66551974Smsmith/********************************************************************************
66651974Smsmith * Run a generic enquiry-style command.
66751974Smsmith */
66851974Smsmithstatic void *
66951974Smsmithamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
67051974Smsmith{
67151974Smsmith    struct amr_command	*ac;
67251974Smsmith    void		*result;
67351974Smsmith    u_int8_t		*mbox;
67451974Smsmith    int			error;
67551974Smsmith
67665245Smsmith    debug_called(1);
67751974Smsmith
67851974Smsmith    error = 1;
67951974Smsmith    result = NULL;
68051974Smsmith
68151974Smsmith    /* get ourselves a command buffer */
68251974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
68351974Smsmith	goto out;
68451974Smsmith    /* allocate the response structure */
68551974Smsmith    if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
68651974Smsmith	goto out;
68765245Smsmith    /* set command flags */
68851974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
68951974Smsmith
69065245Smsmith    /* point the command at our data */
69151974Smsmith    ac->ac_data = result;
69251974Smsmith    ac->ac_length = bufsize;
69351974Smsmith
69451974Smsmith    /* build the command proper */
69551974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
69651974Smsmith    mbox[0] = cmd;
69751974Smsmith    mbox[2] = cmdsub;
69851974Smsmith    mbox[3] = cmdqual;
69951974Smsmith
70058883Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
701107756Semoore    if (sc->amr_poll_command(ac))
70251974Smsmith	goto out;
70351974Smsmith    error = ac->ac_status;
70451974Smsmith
70551974Smsmith out:
70651974Smsmith    if (ac != NULL)
70751974Smsmith	amr_releasecmd(ac);
70851974Smsmith    if ((error != 0) && (result != NULL)) {
70951974Smsmith	free(result, M_DEVBUF);
71051974Smsmith	result = NULL;
71151974Smsmith    }
71251974Smsmith    return(result);
71351974Smsmith}
71451974Smsmith
71551974Smsmith/********************************************************************************
71651974Smsmith * Flush the controller's internal cache, return status.
71751974Smsmith */
71865245Smsmithint
71951974Smsmithamr_flush(struct amr_softc *sc)
72051974Smsmith{
72151974Smsmith    struct amr_command	*ac;
72251974Smsmith    int			error;
72351974Smsmith
72451974Smsmith    /* get ourselves a command buffer */
72551974Smsmith    error = 1;
72651974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
72751974Smsmith	goto out;
72865245Smsmith    /* set command flags */
72951974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
73051974Smsmith
73151974Smsmith    /* build the command proper */
73251974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
73351974Smsmith
73458883Smsmith    /* we have to poll, as the system may be going down or otherwise damaged */
735107756Semoore    if (sc->amr_poll_command(ac))
73651974Smsmith	goto out;
73751974Smsmith    error = ac->ac_status;
73851974Smsmith
73951974Smsmith out:
74051974Smsmith    if (ac != NULL)
74151974Smsmith	amr_releasecmd(ac);
74251974Smsmith    return(error);
74351974Smsmith}
74451974Smsmith
74551974Smsmith/********************************************************************************
746106225Semoore * Detect extented cdb >> greater than 10 byte cdb support
747106225Semoore * returns '1' means this support exist
748106225Semoore * returns '0' means this support doesn't exist
749106225Semoore */
750106225Semoorestatic int
751106225Semooreamr_support_ext_cdb(struct amr_softc *sc)
752106225Semoore{
753106225Semoore    struct amr_command	*ac;
754106225Semoore    u_int8_t		*mbox;
755106225Semoore    int			error;
756106225Semoore
757106225Semoore    /* get ourselves a command buffer */
758106225Semoore    error = 0;
759106225Semoore    if ((ac = amr_alloccmd(sc)) == NULL)
760106225Semoore	goto out;
761106225Semoore    /* set command flags */
762106225Semoore    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
763106225Semoore
764106225Semoore    /* build the command proper */
765106225Semoore    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
766106225Semoore    mbox[0] = 0xA4;
767106225Semoore    mbox[2] = 0x16;
768106225Semoore
769106225Semoore
770106225Semoore    /* we have to poll, as the system may be going down or otherwise damaged */
771107756Semoore    if (sc->amr_poll_command(ac))
772106225Semoore	goto out;
773106225Semoore    if( ac->ac_status == AMR_STATUS_SUCCESS ) {
774106225Semoore	    error = 1;
775106225Semoore    }
776106225Semoore
777106225Semooreout:
778106225Semoore    if (ac != NULL)
779106225Semoore	amr_releasecmd(ac);
780106225Semoore    return(error);
781106225Semoore}
782106225Semoore
783106225Semoore/********************************************************************************
78465245Smsmith * Try to find I/O work for the controller from one or more of the work queues.
78551974Smsmith *
78665245Smsmith * We make the assumption that if the controller is not ready to take a command
78765245Smsmith * at some given time, it will generate an interrupt at some later time when
78865245Smsmith * it is.
78951974Smsmith */
79065245Smsmithvoid
79151974Smsmithamr_startio(struct amr_softc *sc)
79251974Smsmith{
79351974Smsmith    struct amr_command	*ac;
79451974Smsmith
79551974Smsmith    /* spin until something prevents us from doing any work */
79651974Smsmith    for (;;) {
79751974Smsmith
79865245Smsmith	/* try to get a ready command */
79965245Smsmith	ac = amr_dequeue_ready(sc);
80051974Smsmith
80165245Smsmith	/* if that failed, build a command from a bio */
80265245Smsmith	if (ac == NULL)
80365245Smsmith	    (void)amr_bio_command(sc, &ac);
80451974Smsmith
80565245Smsmith	/* if that failed, build a command from a ccb */
80665245Smsmith	if (ac == NULL)
80765245Smsmith	    (void)amr_cam_command(sc, &ac);
808105419Semoore
80965245Smsmith	/* if we don't have anything to do, give up */
81065245Smsmith	if (ac == NULL)
81165245Smsmith	    break;
81251974Smsmith
81365245Smsmith	/* try to give the command to the controller; if this fails save it for later and give up */
81465245Smsmith	if (amr_start(ac)) {
81565245Smsmith	    debug(2, "controller busy, command deferred");
81665245Smsmith	    amr_requeue_ready(ac);	/* XXX schedule retry very soon? */
81765245Smsmith	    break;
81851974Smsmith	}
81951974Smsmith    }
82051974Smsmith}
82151974Smsmith
82251974Smsmith/********************************************************************************
82351974Smsmith * Handle completion of an I/O command.
82451974Smsmith */
82551974Smsmithstatic void
82651974Smsmithamr_completeio(struct amr_command *ac)
82751974Smsmith{
82851974Smsmith    struct amr_softc	*sc = ac->ac_sc;
82951974Smsmith
83051974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
83165245Smsmith	ac->ac_bio->bio_error = EIO;
83265245Smsmith	ac->ac_bio->bio_flags |= BIO_ERROR;
83351974Smsmith
83465245Smsmith	device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status);
83565245Smsmith/*	amr_printcommand(ac);*/
83651974Smsmith    }
83765245Smsmith    amrd_intr(ac->ac_bio);
83865245Smsmith    amr_releasecmd(ac);
83951974Smsmith}
84051974Smsmith
84151974Smsmith/********************************************************************************
84251974Smsmith ********************************************************************************
84351974Smsmith                                                               Command Processing
84451974Smsmith ********************************************************************************
84551974Smsmith ********************************************************************************/
84651974Smsmith
84751974Smsmith/********************************************************************************
84865245Smsmith * Convert a bio off the top of the bio queue into a command.
84965245Smsmith */
85065245Smsmithstatic int
85165245Smsmithamr_bio_command(struct amr_softc *sc, struct amr_command **acp)
85265245Smsmith{
85365245Smsmith    struct amr_command	*ac;
85465245Smsmith    struct amrd_softc	*amrd;
85565245Smsmith    struct bio		*bio;
85665245Smsmith    int			error;
85765245Smsmith    int			blkcount;
85865245Smsmith    int			driveno;
85965245Smsmith    int			cmd;
86065245Smsmith
86165245Smsmith    ac = NULL;
86265245Smsmith    error = 0;
86365245Smsmith
86465245Smsmith    /* get a bio to work on */
86565245Smsmith    if ((bio = amr_dequeue_bio(sc)) == NULL)
86665245Smsmith	goto out;
86765245Smsmith
86865245Smsmith    /* get a command */
86965245Smsmith    if ((ac = amr_alloccmd(sc)) == NULL) {
87065245Smsmith	error = ENOMEM;
87165245Smsmith	goto out;
87265245Smsmith    }
87365245Smsmith
87465245Smsmith    /* connect the bio to the command */
87565245Smsmith    ac->ac_complete = amr_completeio;
87665245Smsmith    ac->ac_bio = bio;
87765245Smsmith    ac->ac_data = bio->bio_data;
87865245Smsmith    ac->ac_length = bio->bio_bcount;
87965245Smsmith    if (BIO_IS_READ(bio)) {
88065245Smsmith	ac->ac_flags |= AMR_CMD_DATAIN;
88165245Smsmith	cmd = AMR_CMD_LREAD;
88265245Smsmith    } else {
88365245Smsmith	ac->ac_flags |= AMR_CMD_DATAOUT;
88465245Smsmith	cmd = AMR_CMD_LWRITE;
88565245Smsmith    }
88665245Smsmith    amrd = (struct amrd_softc *)bio->bio_dev->si_drv1;
88765245Smsmith    driveno = amrd->amrd_drive - sc->amr_drive;
88865245Smsmith    blkcount = (bio->bio_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
88965245Smsmith
89065245Smsmith    ac->ac_mailbox.mb_command = cmd;
89165245Smsmith    ac->ac_mailbox.mb_blkcount = blkcount;
89265245Smsmith    ac->ac_mailbox.mb_lba = bio->bio_pblkno;
89365245Smsmith    ac->ac_mailbox.mb_drive = driveno;
89465245Smsmith    /* we fill in the s/g related data when the command is mapped */
89565245Smsmith
89665245Smsmith    if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
89792610Sbde	device_printf(sc->amr_dev, "I/O beyond end of unit (%lld,%d > %lu)\n",
89892610Sbde		      (long long)bio->bio_pblkno, blkcount,
89992610Sbde		      (u_long)sc->amr_drive[driveno].al_size);
90065245Smsmith
90165245Smsmithout:
90265245Smsmith    if (error != 0) {
90365245Smsmith	if (ac != NULL)
90465245Smsmith	    amr_releasecmd(ac);
90565245Smsmith	if (bio != NULL)			/* this breaks ordering... */
90665245Smsmith	    amr_enqueue_bio(sc, bio);
90765245Smsmith    }
90865245Smsmith    *acp = ac;
90965245Smsmith    return(error);
91065245Smsmith}
91165245Smsmith
91265245Smsmith/********************************************************************************
91351974Smsmith * Take a command, submit it to the controller and sleep until it completes
91451974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
91551974Smsmith */
91651974Smsmithstatic int
91751974Smsmithamr_wait_command(struct amr_command *ac)
91851974Smsmith{
91951974Smsmith    int			error, count;
92051974Smsmith
92165245Smsmith    debug_called(1);
92251974Smsmith
92351974Smsmith    ac->ac_complete = NULL;
92465245Smsmith    ac->ac_flags |= AMR_CMD_SLEEP;
92551974Smsmith    if ((error = amr_start(ac)) != 0)
92651974Smsmith	return(error);
92751974Smsmith
92851974Smsmith    count = 0;
92951974Smsmith    /* XXX better timeout? */
93065245Smsmith    while ((ac->ac_flags & AMR_CMD_BUSY) && (count < 30)) {
93165245Smsmith	tsleep(ac, PRIBIO | PCATCH, "amrwcmd", hz);
93251974Smsmith    }
93351974Smsmith    return(0);
93451974Smsmith}
93551974Smsmith
93651974Smsmith/********************************************************************************
93751974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
93851974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
93951974Smsmith */
94051974Smsmithstatic int
941107756Semooreamr_std_poll_command(struct amr_command *ac)
94251974Smsmith{
94351974Smsmith    struct amr_softc	*sc = ac->ac_sc;
94465245Smsmith    int			error, count;
94551974Smsmith
94665245Smsmith    debug_called(2);
94751974Smsmith
94851974Smsmith    ac->ac_complete = NULL;
94951974Smsmith    if ((error = amr_start(ac)) != 0)
95051974Smsmith	return(error);
95151974Smsmith
95251974Smsmith    count = 0;
95351974Smsmith    do {
95451974Smsmith	/*
95551974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
95651974Smsmith	 * Note that the timeout here is somewhat arbitrary.
95751974Smsmith	 */
95851974Smsmith	amr_done(sc);
95965245Smsmith	DELAY(1000);
96065245Smsmith    } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000));
96165245Smsmith    if (!(ac->ac_flags & AMR_CMD_BUSY)) {
96251974Smsmith	error = 0;
96351974Smsmith    } else {
96465245Smsmith	/* XXX the slot is now marked permanently busy */
96551974Smsmith	error = EIO;
96665245Smsmith	device_printf(sc->amr_dev, "polled command timeout\n");
96751974Smsmith    }
96851974Smsmith    return(error);
96951974Smsmith}
97051974Smsmith
97151974Smsmith/********************************************************************************
972107756Semoore * Take a command, submit it to the controller and busy-wait for it to return.
973107756Semoore * Returns nonzero on error.  Can be safely called with interrupts enabled.
974107756Semoore */
975107756Semoorestatic int
976107756Semooreamr_quartz_poll_command(struct amr_command *ac)
977107756Semoore{
978107756Semoore    struct amr_softc	*sc = ac->ac_sc;
979107756Semoore    int			s;
980107756Semoore
981107756Semoore    debug_called(2);
982107756Semoore
983107756Semoore    /* now we have a slot, we can map the command (unmapped in amr_complete) */
984107756Semoore    amr_mapcmd(ac);
985107756Semoore
986107756Semoore    s = splbio();
987107756Semoore
988107756Semoore    if(sc->amr_busyslots) {
989107756Semoore	device_printf(sc->amr_dev, "adapter is busy");
990107756Semoore	splx(s);
991107756Semoore	amr_unmapcmd(ac);
992107756Semoore    	ac->ac_status=0;
993107756Semoore	return(1);
994107756Semoore    }
995107756Semoore
996107756Semoore    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
997107756Semoore
998107756Semoore    /* clear the poll/ack fields in the mailbox */
999107756Semoore    sc->amr_mailbox->mb_ident = 0xFE;
1000107756Semoore    sc->amr_mailbox->mb_nstatus = 0xFF;
1001107756Semoore    sc->amr_mailbox->mb_status = 0xFF;
1002107756Semoore    sc->amr_mailbox->mb_poll = 0;
1003107756Semoore    sc->amr_mailbox->mb_ack = 0;
1004107756Semoore
1005107756Semoore    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
1006107756Semoore
1007107756Semoore    while(sc->amr_mailbox->mb_nstatus == 0xFF);
1008107756Semoore    while(sc->amr_mailbox->mb_status == 0xFF);
1009107756Semoore    while(sc->amr_mailbox->mb_poll != 0x77);
1010107756Semoore    sc->amr_mailbox->mb_poll = 0;
1011107756Semoore    sc->amr_mailbox->mb_ack = 0x77;
1012107756Semoore
1013107756Semoore    /* acknowledge that we have the commands */
1014107756Semoore    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
1015107756Semoore
1016107756Semoore    splx(s);
1017107756Semoore
1018107756Semoore    /* unmap the command's data buffer */
1019107756Semoore    amr_unmapcmd(ac);
1020107756Semoore
1021107756Semoore    ac->ac_status=0;
1022107756Semoore    return(0);
1023107756Semoore}
1024107756Semoore
1025107756Semoore/********************************************************************************
102665245Smsmith * Get a free command slot for a command if it doesn't already have one.
102765245Smsmith *
102865245Smsmith * May be safely called multiple times for a given command.
102951974Smsmith */
103051974Smsmithstatic int
103151974Smsmithamr_getslot(struct amr_command *ac)
103251974Smsmith{
103351974Smsmith    struct amr_softc	*sc = ac->ac_sc;
103465245Smsmith    int			s, slot, limit, error;
103551974Smsmith
103665245Smsmith    debug_called(3);
103765245Smsmith
103865245Smsmith    /* if the command already has a slot, don't try to give it another one */
103965245Smsmith    if (ac->ac_slot != 0)
104065245Smsmith	return(0);
104165245Smsmith
104251974Smsmith    /* enforce slot usage limit */
104351974Smsmith    limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
104465245Smsmith    if (sc->amr_busyslots > limit)
104551974Smsmith	return(EBUSY);
104651974Smsmith
104751974Smsmith    /*
104865245Smsmith     * Allocate a slot.  XXX linear scan is slow
104951974Smsmith     */
105065245Smsmith    error = EBUSY;
105151974Smsmith    s = splbio();
105251974Smsmith    for (slot = 0; slot < sc->amr_maxio; slot++) {
105365245Smsmith	if (sc->amr_busycmd[slot] == NULL) {
105465245Smsmith	    sc->amr_busycmd[slot] = ac;
105565245Smsmith	    sc->amr_busyslots++;
105665245Smsmith	    ac->ac_slot = slot;
105765245Smsmith	    error = 0;
105851974Smsmith	    break;
105965245Smsmith	}
106051974Smsmith    }
106151974Smsmith    splx(s);
106251974Smsmith
106365245Smsmith    return(error);
106451974Smsmith}
106551974Smsmith
106651974Smsmith/********************************************************************************
106765245Smsmith * Map/unmap (ac)'s data in the controller's addressable space as required.
106865245Smsmith *
106965245Smsmith * These functions may be safely called multiple times on a given command.
107051974Smsmith */
107151974Smsmithstatic void
107251974Smsmithamr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
107351974Smsmith{
107451974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
107551974Smsmith    struct amr_softc	*sc = ac->ac_sc;
107651974Smsmith    struct amr_sgentry	*sg;
107751974Smsmith    int			i;
107869319Smsmith    u_int8_t		*sgc;
107951974Smsmith
108065245Smsmith    debug_called(3);
108151974Smsmith
108251974Smsmith    /* get base address of s/g table */
108351974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
108451974Smsmith
108565245Smsmith    /* save data physical address */
108651974Smsmith    ac->ac_dataphys = segs[0].ds_addr;
108751974Smsmith
108869319Smsmith    /* for AMR_CMD_CONFIG the s/g count goes elsewhere */
108969319Smsmith    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) {
109069319Smsmith	sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param);
109169319Smsmith    } else {
109269319Smsmith	sgc = &ac->ac_mailbox.mb_nsgelem;
109369319Smsmith    }
109469319Smsmith
109565245Smsmith    /* decide whether we need to populate the s/g table */
109665245Smsmith    if (nsegments < 2) {
109769319Smsmith	*sgc = 0;
1098105419Semoore	ac->ac_mailbox.mb_nsgelem = 0;
109965245Smsmith	ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
110065245Smsmith    } else {
1101105419Semoore        ac->ac_mailbox.mb_nsgelem = nsegments;
110269319Smsmith	*sgc = nsegments;
110365245Smsmith	ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
110465245Smsmith	for (i = 0; i < nsegments; i++, sg++) {
110565245Smsmith	    sg->sg_addr = segs[i].ds_addr;
110665245Smsmith	    sg->sg_count = segs[i].ds_len;
110765245Smsmith	}
110865245Smsmith    }
110965245Smsmith}
111065245Smsmith
111165245Smsmithstatic void
111265245Smsmithamr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
111365245Smsmith{
1114105419Semoore    struct amr_command          *ac = (struct amr_command *)arg;
1115105419Semoore    struct amr_softc            *sc = ac->ac_sc;
1116105419Semoore    struct amr_sgentry          *sg;
1117105419Semoore    struct amr_passthrough      *ap = (struct amr_passthrough *)ac->ac_data;
1118106252Semoore    struct amr_ext_passthrough	*aep = (struct amr_ext_passthrough *)ac->ac_data;
1119105419Semoore    int                         i;
112065245Smsmith
112165245Smsmith    /* get base address of s/g table */
112265245Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
112365245Smsmith
1124105419Semoore    /* decide whether we need to populate the s/g table */
1125106225Semoore    if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) {
1126106225Semoore	if (nsegments < 2) {
1127106225Semoore	    aep->ap_no_sg_elements = 0;
1128106225Semoore	    aep->ap_data_transfer_address =  segs[0].ds_addr;
1129106225Semoore	} else {
1130106225Semoore	    /* save s/g table information in passthrough */
1131106225Semoore	    aep->ap_no_sg_elements = nsegments;
1132106225Semoore	    aep->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
1133106225Semoore	    /* populate s/g table (overwrites previous call which mapped the passthrough) */
1134106225Semoore	    for (i = 0; i < nsegments; i++, sg++) {
1135106225Semoore		sg->sg_addr = segs[i].ds_addr;
1136106225Semoore		sg->sg_count = segs[i].ds_len;
1137106225Semoore		debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
1138106225Semoore	    }
1139106225Semoore	}
1140106225Semoore	debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
1141106225Semoore	    aep->ap_no_sg_elements, aep->ap_data_transfer_address, ac->ac_dataphys);
1142105419Semoore    } else {
1143106225Semoore	if (nsegments < 2) {
1144106225Semoore	    ap->ap_no_sg_elements = 0;
1145106225Semoore	    ap->ap_data_transfer_address =  segs[0].ds_addr;
1146106225Semoore	} else {
1147106225Semoore	    /* save s/g table information in passthrough */
1148106225Semoore	    ap->ap_no_sg_elements = nsegments;
1149106225Semoore	    ap->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
1150106225Semoore	    /* populate s/g table (overwrites previous call which mapped the passthrough) */
1151106225Semoore	    for (i = 0; i < nsegments; i++, sg++) {
1152105419Semoore		sg->sg_addr = segs[i].ds_addr;
1153105419Semoore		sg->sg_count = segs[i].ds_len;
1154105419Semoore		debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
1155106225Semoore	    }
1156105419Semoore	}
1157106225Semoore	debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
1158106225Semoore	    ap->ap_no_sg_elements, ap->ap_data_transfer_address, ac->ac_dataphys);
1159105419Semoore    }
116051974Smsmith}
116151974Smsmith
116251974Smsmithstatic void
116351974Smsmithamr_mapcmd(struct amr_command *ac)
116451974Smsmith{
116551974Smsmith    struct amr_softc	*sc = ac->ac_sc;
116651974Smsmith
116769319Smsmith    debug_called(3);
116851974Smsmith
116965245Smsmith    /* if the command involves data at all, and hasn't been mapped */
117065245Smsmith    if (!(ac->ac_flags & AMR_CMD_MAPPED)) {
117165245Smsmith
117265245Smsmith	if (ac->ac_data != NULL) {
117365245Smsmith	    /* map the data buffers into bus space and build the s/g list */
1174105419Semoore	    bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
117565245Smsmith			    amr_setup_dmamap, ac, 0);
117665245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
117765245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
117865245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
117965245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
118065245Smsmith	}
118165245Smsmith
118265245Smsmith	if (ac->ac_ccb_data != NULL) {
1183105419Semoore	    bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, ac->ac_ccb_data, ac->ac_ccb_length,
118465245Smsmith			    amr_setup_ccbmap, ac, 0);
118565245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
118665245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREREAD);
118765245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
118865245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREWRITE);
118965245Smsmith	}
119065245Smsmith	ac->ac_flags |= AMR_CMD_MAPPED;
119151974Smsmith    }
119251974Smsmith}
119351974Smsmith
119451974Smsmithstatic void
119551974Smsmithamr_unmapcmd(struct amr_command *ac)
119651974Smsmith{
119751974Smsmith    struct amr_softc	*sc = ac->ac_sc;
119851974Smsmith
119969319Smsmith    debug_called(3);
120051974Smsmith
120165245Smsmith    /* if the command involved data at all and was mapped */
120265245Smsmith    if (ac->ac_flags & AMR_CMD_MAPPED) {
120351974Smsmith
120465245Smsmith	if (ac->ac_data != NULL) {
120565245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
120665245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
120765245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
120865245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
120965245Smsmith	    bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
121065245Smsmith	}
121165245Smsmith
121265245Smsmith	if (ac->ac_ccb_data != NULL) {
121365245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
121465245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTREAD);
121565245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
121665245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTWRITE);
121765245Smsmith	    bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap);
121865245Smsmith	}
121965245Smsmith	ac->ac_flags &= ~AMR_CMD_MAPPED;
122051974Smsmith    }
122151974Smsmith}
122251974Smsmith
122351974Smsmith/********************************************************************************
122465245Smsmith * Take a command and give it to the controller, returns 0 if successful, or
122565245Smsmith * EBUSY if the command should be retried later.
122651974Smsmith */
122751974Smsmithstatic int
122851974Smsmithamr_start(struct amr_command *ac)
122951974Smsmith{
123051974Smsmith    struct amr_softc	*sc = ac->ac_sc;
123158883Smsmith    int			done, s, i;
1232105419Semoore
123369319Smsmith    debug_called(3);
123451974Smsmith
123565245Smsmith    /* mark command as busy so that polling consumer can tell */
123665245Smsmith    ac->ac_flags |= AMR_CMD_BUSY;
123765245Smsmith
123865245Smsmith    /* get a command slot (freed in amr_done) */
123965245Smsmith    if (amr_getslot(ac))
124065245Smsmith	return(EBUSY);
124165245Smsmith
124265245Smsmith    /* now we have a slot, we can map the command (unmapped in amr_complete) */
124365245Smsmith    amr_mapcmd(ac);
124465245Smsmith
124565245Smsmith    /* mark the new mailbox we are going to copy in as busy */
124665245Smsmith    ac->ac_mailbox.mb_busy = 1;
124765245Smsmith
124865245Smsmith    /* clear the poll/ack fields in the mailbox */
124965245Smsmith    sc->amr_mailbox->mb_poll = 0;
125065245Smsmith    sc->amr_mailbox->mb_ack = 0;
125165245Smsmith
125251974Smsmith    /*
125351974Smsmith     * Save the slot number so that we can locate this command when complete.
125451974Smsmith     * Note that ident = 0 seems to be special, so we don't use it.
125551974Smsmith     */
125651974Smsmith    ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
125751974Smsmith
125858883Smsmith    /*
125965245Smsmith     * Spin waiting for the mailbox, give up after ~1 second.  We expect the
126065245Smsmith     * controller to be able to handle our I/O.
126165245Smsmith     *
126265245Smsmith     * XXX perhaps we should wait for less time, and count on the deferred command
126365245Smsmith     * handling to deal with retries?
126458883Smsmith     */
126569319Smsmith    debug(4, "wait for mailbox");
126658883Smsmith    for (i = 10000, done = 0; (i > 0) && !done; i--) {
126751974Smsmith	s = splbio();
126851974Smsmith
126951974Smsmith	/* is the mailbox free? */
127051974Smsmith	if (sc->amr_mailbox->mb_busy == 0) {
127169319Smsmith	    debug(4, "got mailbox");
127251974Smsmith	    sc->amr_mailbox64->mb64_segment = 0;
127365245Smsmith	    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
127451974Smsmith	    done = 1;
127551974Smsmith
127665245Smsmith	    /* not free, spin waiting */
127751974Smsmith	} else {
127869319Smsmith	    debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy);
127958883Smsmith	    /* this is somewhat ugly */
128058883Smsmith	    DELAY(100);
128151974Smsmith	}
128258883Smsmith	splx(s);	/* drop spl to allow completion interrupts */
128351974Smsmith    }
128465245Smsmith
128565245Smsmith    /*
128665245Smsmith     * Now give the command to the controller
128765245Smsmith     */
128851974Smsmith    if (done) {
128965245Smsmith	if (sc->amr_submit_command(sc)) {
129065245Smsmith	    /* the controller wasn't ready to take the command, forget that we tried to post it */
129165245Smsmith	    sc->amr_mailbox->mb_busy = 0;
129265245Smsmith	    return(EBUSY);
129365245Smsmith	}
129469319Smsmith	debug(3, "posted command");
129551974Smsmith	return(0);
129651974Smsmith    }
129751974Smsmith
129851974Smsmith    /*
129965245Smsmith     * The controller wouldn't take the command.  Return the command as busy
130065245Smsmith     * so that it is retried later.
130151974Smsmith     */
130265245Smsmith    return(EBUSY);
130351974Smsmith}
130451974Smsmith
130551974Smsmith/********************************************************************************
130651974Smsmith * Extract one or more completed commands from the controller (sc)
130751974Smsmith *
130852543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
130951974Smsmith */
131065245Smsmithint
131151974Smsmithamr_done(struct amr_softc *sc)
131251974Smsmith{
131351974Smsmith    struct amr_command	*ac;
131451974Smsmith    struct amr_mailbox	mbox;
131565245Smsmith    int			i, idx, result;
131651974Smsmith
131769319Smsmith    debug_called(3);
131851974Smsmith
131951974Smsmith    /* See if there's anything for us to do */
132051974Smsmith    result = 0;
132151974Smsmith
132258883Smsmith    /* loop collecting completed commands */
132358883Smsmith    for (;;) {
132458883Smsmith	/* poll for a completed command's identifier and status */
132558883Smsmith	if (sc->amr_get_work(sc, &mbox)) {
132658883Smsmith	    result = 1;
132758883Smsmith
132858883Smsmith	    /* iterate over completed commands in this result */
132958883Smsmith	    for (i = 0; i < mbox.mb_nstatus; i++) {
133058883Smsmith		/* get pointer to busy command */
133158883Smsmith		idx = mbox.mb_completed[i] - 1;
133258883Smsmith		ac = sc->amr_busycmd[idx];
133351974Smsmith
133458883Smsmith		/* really a busy command? */
133558883Smsmith		if (ac != NULL) {
133658883Smsmith
133758883Smsmith		    /* pull the command from the busy index */
133858883Smsmith		    sc->amr_busycmd[idx] = NULL;
133965245Smsmith		    sc->amr_busyslots--;
134051974Smsmith
134165245Smsmith		    /* save status for later use */
134265245Smsmith		    ac->ac_status = mbox.mb_status;
134365245Smsmith		    amr_enqueue_completed(ac);
134465245Smsmith		    debug(3, "completed command with status %x", mbox.mb_status);
134565245Smsmith		} else {
134665245Smsmith		    device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
134751974Smsmith		}
134851974Smsmith	    }
134958883Smsmith	} else {
135065245Smsmith	    break;	/* no work */
135151974Smsmith	}
135251974Smsmith    }
135358883Smsmith
135458883Smsmith    /* if we've completed any commands, try posting some more */
135558883Smsmith    if (result)
135658883Smsmith	amr_startio(sc);
135758883Smsmith
135858883Smsmith    /* handle completion and timeouts */
135965245Smsmith#if __FreeBSD_version >= 500005
136065245Smsmith    if (sc->amr_state & AMR_STATE_INTEN)
136165245Smsmith	taskqueue_enqueue(taskqueue_swi, &sc->amr_task_complete);
136265245Smsmith    else
136365245Smsmith#endif
136465245Smsmith	amr_complete(sc, 0);
136558883Smsmith
136651974Smsmith    return(result);
136751974Smsmith}
136851974Smsmith
136951974Smsmith/********************************************************************************
137051974Smsmith * Do completion processing on done commands on (sc)
137151974Smsmith */
137251974Smsmithstatic void
137365245Smsmithamr_complete(void *context, int pending)
137451974Smsmith{
137565245Smsmith    struct amr_softc	*sc = (struct amr_softc *)context;
137665245Smsmith    struct amr_command	*ac;
137751974Smsmith
137869319Smsmith    debug_called(3);
137951974Smsmith
138065245Smsmith    /* pull completed commands off the queue */
138165245Smsmith    for (;;) {
138265245Smsmith	ac = amr_dequeue_completed(sc);
138365245Smsmith	if (ac == NULL)
138465245Smsmith	    break;
138558883Smsmith
138665245Smsmith	/* unmap the command's data buffer */
138765245Smsmith	amr_unmapcmd(ac);
138851974Smsmith
138965245Smsmith	/* unbusy the command */
139065245Smsmith	ac->ac_flags &= ~AMR_CMD_BUSY;
139151974Smsmith
139265245Smsmith	/*
139365245Smsmith	 * Is there a completion handler?
139465245Smsmith	 */
139565245Smsmith	if (ac->ac_complete != NULL) {
139665245Smsmith	    ac->ac_complete(ac);
139765245Smsmith
139851974Smsmith	    /*
139965245Smsmith	     * Is someone sleeping on this one?
140051974Smsmith	     */
140165245Smsmith	} else if (ac->ac_flags & AMR_CMD_SLEEP) {
140265245Smsmith	    wakeup(ac);
140351974Smsmith	}
140451974Smsmith    }
140551974Smsmith}
140651974Smsmith
140751974Smsmith/********************************************************************************
140851974Smsmith ********************************************************************************
140951974Smsmith                                                        Command Buffer Management
141051974Smsmith ********************************************************************************
141151974Smsmith ********************************************************************************/
141251974Smsmith
141351974Smsmith/********************************************************************************
141451974Smsmith * Get a new command buffer.
141551974Smsmith *
141651974Smsmith * This may return NULL in low-memory cases.
141751974Smsmith *
141851974Smsmith * If possible, we recycle a command buffer that's been used before.
141951974Smsmith */
142065245Smsmithstruct amr_command *
142151974Smsmithamr_alloccmd(struct amr_softc *sc)
142251974Smsmith{
142351974Smsmith    struct amr_command	*ac;
142451974Smsmith
142565245Smsmith    debug_called(3);
142651974Smsmith
142765245Smsmith    ac = amr_dequeue_free(sc);
142851974Smsmith    if (ac == NULL) {
142965245Smsmith	amr_alloccmd_cluster(sc);
143065245Smsmith	ac = amr_dequeue_free(sc);
143151974Smsmith    }
143265245Smsmith    if (ac == NULL)
143365245Smsmith	return(NULL);
143465245Smsmith
143565245Smsmith    /* clear out significant fields */
143665245Smsmith    ac->ac_slot = 0;
143765245Smsmith    ac->ac_status = 0;
143851974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
143965245Smsmith    ac->ac_flags = 0;
144065245Smsmith    ac->ac_bio = NULL;
144165245Smsmith    ac->ac_data = NULL;
144265245Smsmith    ac->ac_ccb_data = NULL;
144365245Smsmith    ac->ac_complete = NULL;
144451974Smsmith    return(ac);
144551974Smsmith}
144651974Smsmith
144751974Smsmith/********************************************************************************
144851974Smsmith * Release a command buffer for recycling.
144951974Smsmith */
145065245Smsmithvoid
145151974Smsmithamr_releasecmd(struct amr_command *ac)
145251974Smsmith{
145365245Smsmith    debug_called(3);
145451974Smsmith
145565245Smsmith    amr_enqueue_free(ac);
145651974Smsmith}
145751974Smsmith
145851974Smsmith/********************************************************************************
145965245Smsmith * Allocate a new command cluster and initialise it.
146051974Smsmith */
1461104094Sphkstatic void
146265245Smsmithamr_alloccmd_cluster(struct amr_softc *sc)
146351974Smsmith{
146465245Smsmith    struct amr_command_cluster	*acc;
146565245Smsmith    struct amr_command		*ac;
146665245Smsmith    int				s, i;
146751974Smsmith
146865245Smsmith    acc = malloc(AMR_CMD_CLUSTERSIZE, M_DEVBUF, M_NOWAIT);
146965245Smsmith    if (acc != NULL) {
147065245Smsmith	s = splbio();
147165245Smsmith	TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
147265245Smsmith	splx(s);
147365245Smsmith	for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
147465245Smsmith	    ac = &acc->acc_command[i];
147565245Smsmith	    bzero(ac, sizeof(*ac));
147665245Smsmith	    ac->ac_sc = sc;
147765245Smsmith	    if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) &&
147865245Smsmith		!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap))
147965245Smsmith		amr_releasecmd(ac);
148065245Smsmith	}
148165245Smsmith    }
148251974Smsmith}
148351974Smsmith
148451974Smsmith/********************************************************************************
148565245Smsmith * Free a command cluster
148665245Smsmith */
1487104094Sphkstatic void
148865245Smsmithamr_freecmd_cluster(struct amr_command_cluster *acc)
148965245Smsmith{
149065245Smsmith    struct amr_softc	*sc = acc->acc_command[0].ac_sc;
149165245Smsmith    int			i;
149265245Smsmith
149365245Smsmith    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++)
149465245Smsmith	bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
149565245Smsmith    free(acc, M_DEVBUF);
149665245Smsmith}
149765245Smsmith
149865245Smsmith/********************************************************************************
149951974Smsmith ********************************************************************************
150051974Smsmith                                                         Interface-specific Shims
150151974Smsmith ********************************************************************************
150251974Smsmith ********************************************************************************/
150351974Smsmith
150451974Smsmith/********************************************************************************
150551974Smsmith * Tell the controller that the mailbox contains a valid command
150651974Smsmith */
150765245Smsmithstatic int
150851974Smsmithamr_quartz_submit_command(struct amr_softc *sc)
150951974Smsmith{
151065245Smsmith    debug_called(3);
151151974Smsmith
151265245Smsmith    if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
151365245Smsmith	return(EBUSY);
151451974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
151565245Smsmith    return(0);
151651974Smsmith}
151751974Smsmith
151865245Smsmithstatic int
151951974Smsmithamr_std_submit_command(struct amr_softc *sc)
152051974Smsmith{
152165245Smsmith    debug_called(3);
152251974Smsmith
152365245Smsmith    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
152465245Smsmith	return(EBUSY);
152551974Smsmith    AMR_SPOST_COMMAND(sc);
152665245Smsmith    return(0);
152751974Smsmith}
152851974Smsmith
152951974Smsmith/********************************************************************************
153051974Smsmith * Claim any work that the controller has completed; acknowledge completion,
153151974Smsmith * save details of the completion in (mbsave)
153251974Smsmith */
153351974Smsmithstatic int
153451974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
153551974Smsmith{
153651974Smsmith    int		s, worked;
153751974Smsmith    u_int32_t	outd;
153851974Smsmith
153965245Smsmith    debug_called(3);
154065245Smsmith
154151974Smsmith    worked = 0;
154251974Smsmith    s = splbio();
154351974Smsmith
154451974Smsmith    /* work waiting for us? */
154551974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
154651974Smsmith
154751974Smsmith	/* save mailbox, which contains a list of completed commands */
154865245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
154951974Smsmith
155065245Smsmith	/* acknowledge interrupt */
155165245Smsmith	AMR_QPUT_ODB(sc, AMR_QODB_READY);
155265245Smsmith
155351974Smsmith	/* acknowledge that we have the commands */
155451974Smsmith	AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
155565245Smsmith
155665763Smsmith#ifndef AMR_QUARTZ_GOFASTER
155765245Smsmith	/*
155865245Smsmith	 * This waits for the controller to notice that we've taken the
155965245Smsmith	 * command from it.  It's very inefficient, and we shouldn't do it,
156065245Smsmith	 * but if we remove this code, we stop completing commands under
156165245Smsmith	 * load.
156265245Smsmith	 *
156365245Smsmith	 * Peter J says we shouldn't do this.  The documentation says we
156465245Smsmith	 * should.  Who is right?
156565245Smsmith	 */
156651974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
156751974Smsmith	    ;				/* XXX aiee! what if it dies? */
156865245Smsmith#endif
156965245Smsmith
157051974Smsmith	worked = 1;			/* got some work */
157151974Smsmith    }
157251974Smsmith
157351974Smsmith    splx(s);
157451974Smsmith    return(worked);
157551974Smsmith}
157651974Smsmith
157751974Smsmithstatic int
157851974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
157951974Smsmith{
158051974Smsmith    int		s, worked;
158151974Smsmith    u_int8_t	istat;
158251974Smsmith
158365245Smsmith    debug_called(3);
158451974Smsmith
158551974Smsmith    worked = 0;
158651974Smsmith    s = splbio();
158751974Smsmith
158851974Smsmith    /* check for valid interrupt status */
158951974Smsmith    istat = AMR_SGET_ISTAT(sc);
159051974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
159151974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
159251974Smsmith
159351974Smsmith	/* save mailbox, which contains a list of completed commands */
159465245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
159551974Smsmith
159651974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
159751974Smsmith	worked = 1;
159851974Smsmith    }
159951974Smsmith
160051974Smsmith    splx(s);
160151974Smsmith    return(worked);
160251974Smsmith}
160351974Smsmith
160451974Smsmith/********************************************************************************
160551974Smsmith * Notify the controller of the mailbox location.
160651974Smsmith */
160751974Smsmithstatic void
160851974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
160951974Smsmith{
161051974Smsmith
161151974Smsmith    /* program the mailbox physical address */
161251974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
161351974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
161451974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
161551974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
161651974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
161751974Smsmith
161851974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
161951974Smsmith    AMR_SACK_INTERRUPT(sc);
162051974Smsmith    AMR_SENABLE_INTR(sc);
162151974Smsmith}
162251974Smsmith
162365245Smsmith#ifdef AMR_BOARD_INIT
162451974Smsmith/********************************************************************************
162565245Smsmith * Initialise the controller
162665245Smsmith */
162765245Smsmithstatic int
162865245Smsmithamr_quartz_init(struct amr_softc *sc)
162965245Smsmith{
163065245Smsmith    int		status, ostatus;
163165245Smsmith
163265245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc));
163365245Smsmith
163465245Smsmith    AMR_QRESET(sc);
163565245Smsmith
163665245Smsmith    ostatus = 0xff;
163765245Smsmith    while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) {
163865245Smsmith	if (status != ostatus) {
163965245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status));
164065245Smsmith	    ostatus = status;
164165245Smsmith	}
164265245Smsmith	switch (status) {
164365245Smsmith	case AMR_QINIT_NOMEM:
164465245Smsmith	    return(ENOMEM);
164565245Smsmith
164665245Smsmith	case AMR_QINIT_SCAN:
164765245Smsmith	    /* XXX we could print channel/target here */
164865245Smsmith	    break;
164965245Smsmith	}
165065245Smsmith    }
165165245Smsmith    return(0);
165265245Smsmith}
165365245Smsmith
165465245Smsmithstatic int
165565245Smsmithamr_std_init(struct amr_softc *sc)
165665245Smsmith{
165765245Smsmith    int		status, ostatus;
165865245Smsmith
165965245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc));
166065245Smsmith
166165245Smsmith    AMR_SRESET(sc);
166265245Smsmith
166365245Smsmith    ostatus = 0xff;
166465245Smsmith    while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) {
166565245Smsmith	if (status != ostatus) {
166665245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status));
166765245Smsmith	    ostatus = status;
166865245Smsmith	}
166965245Smsmith	switch (status) {
167065245Smsmith	case AMR_SINIT_NOMEM:
167165245Smsmith	    return(ENOMEM);
167265245Smsmith
167365245Smsmith	case AMR_SINIT_INPROG:
167465245Smsmith	    /* XXX we could print channel/target here? */
167565245Smsmith	    break;
167665245Smsmith	}
167765245Smsmith    }
167865245Smsmith    return(0);
167965245Smsmith}
168065245Smsmith#endif
168165245Smsmith
168265245Smsmith/********************************************************************************
168351974Smsmith ********************************************************************************
168451974Smsmith                                                                        Debugging
168551974Smsmith ********************************************************************************
168651974Smsmith ********************************************************************************/
168751974Smsmith
168851974Smsmith/********************************************************************************
168965245Smsmith * Identify the controller and print some information about it.
169065245Smsmith */
169165245Smsmithstatic void
169265245Smsmithamr_describe_controller(struct amr_softc *sc)
169365245Smsmith{
169465245Smsmith    struct amr_prodinfo	*ap;
169565245Smsmith    struct amr_enquiry	*ae;
169665245Smsmith    char		*prod;
169765245Smsmith
169865245Smsmith    /*
169965245Smsmith     * Try to get 40LD product info, which tells us what the card is labelled as.
170065245Smsmith     */
170165245Smsmith    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) {
1702105419Semoore	device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
170365245Smsmith		      ap->ap_product, ap->ap_firmware, ap->ap_bios,
170465245Smsmith		      ap->ap_memsize);
170565245Smsmith
170665245Smsmith	free(ap, M_DEVBUF);
170765245Smsmith	return;
170865245Smsmith    }
170965245Smsmith
171065245Smsmith    /*
171165245Smsmith     * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
171265245Smsmith     */
171365245Smsmith    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) {
171465245Smsmith	prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
171565245Smsmith
171665245Smsmith    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) {
171765245Smsmith
171865245Smsmith	/*
171965245Smsmith	 * Try to work it out based on the PCI signatures.
172065245Smsmith	 */
172165245Smsmith	switch (pci_get_device(sc->amr_dev)) {
172265245Smsmith	case 0x9010:
172365245Smsmith	    prod = "Series 428";
172465245Smsmith	    break;
172565245Smsmith	case 0x9060:
172665245Smsmith	    prod = "Series 434";
172765245Smsmith	    break;
172865245Smsmith	default:
172965245Smsmith	    prod = "unknown controller";
173065245Smsmith	    break;
173165245Smsmith	}
173265245Smsmith    } else {
173365245Smsmith	prod = "unsupported controller";
173465245Smsmith    }
173574936Shm
173674936Shm    /*
173774936Shm     * HP NetRaid controllers have a special encoding of the firmware and
173874936Shm     * BIOS versions. The AMI version seems to have it as strings whereas
173974936Shm     * the HP version does it with a leading uppercase character and two
174074936Shm     * binary numbers.
174174936Shm     */
174274936Shm
174374936Shm    if(ae->ae_adapter.aa_firmware[2] >= 'A' &&
174474936Shm       ae->ae_adapter.aa_firmware[2] <= 'Z' &&
174574936Shm       ae->ae_adapter.aa_firmware[1] <  ' ' &&
174674936Shm       ae->ae_adapter.aa_firmware[0] <  ' ' &&
174774936Shm       ae->ae_adapter.aa_bios[2] >= 'A'     &&
174874936Shm       ae->ae_adapter.aa_bios[2] <= 'Z'     &&
174974936Shm       ae->ae_adapter.aa_bios[1] <  ' '     &&
175074936Shm       ae->ae_adapter.aa_bios[0] <  ' ') {
175174936Shm
175274936Shm	/* this looks like we have an HP NetRaid version of the MegaRaid */
175374936Shm
175474936Shm    	if(ae->ae_signature == AMR_SIG_438) {
175574936Shm    		/* the AMI 438 is an NetRaid 3si in HP-land */
175674936Shm    		prod = "HP NetRaid 3si";
175774936Shm    	}
175874936Shm
175974936Shm	device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n",
176074936Shm		      prod, ae->ae_adapter.aa_firmware[2],
176174936Shm		      ae->ae_adapter.aa_firmware[1],
176274936Shm		      ae->ae_adapter.aa_firmware[0],
176374936Shm		      ae->ae_adapter.aa_bios[2],
176474936Shm		      ae->ae_adapter.aa_bios[1],
176574936Shm		      ae->ae_adapter.aa_bios[0],
176674936Shm		      ae->ae_adapter.aa_memorysize);
176774936Shm    } else {
176874936Shm	device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n",
176974936Shm		      prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
177074936Shm		      ae->ae_adapter.aa_memorysize);
177174936Shm    }
177265245Smsmith    free(ae, M_DEVBUF);
177365245Smsmith}
177465245Smsmith
177565245Smsmith#ifdef AMR_DEBUG
177665245Smsmith/********************************************************************************
177751974Smsmith * Print the command (ac) in human-readable format
177851974Smsmith */
1779107756Semoore#if 0
178051974Smsmithstatic void
178151974Smsmithamr_printcommand(struct amr_command *ac)
178251974Smsmith{
178351974Smsmith    struct amr_softc	*sc = ac->ac_sc;
178451974Smsmith    struct amr_sgentry	*sg;
178551974Smsmith    int			i;
178651974Smsmith
178751974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
178851974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
178951974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
179051974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
179154512Speter    device_printf(sc->amr_dev, "virtaddr %p  length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
179258883Smsmith    device_printf(sc->amr_dev, "sg physaddr %08x  nsg %d\n",
179351974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
179465245Smsmith    device_printf(sc->amr_dev, "ccb %p  bio %p\n", ac->ac_ccb_data, ac->ac_bio);
179551974Smsmith
179651974Smsmith    /* get base address of s/g table */
179751974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
179851974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
179951974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
180051974Smsmith}
180165245Smsmith#endif
1802107756Semoore#endif
1803