amr.c revision 106225
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 106225 2002-10-30 22:00:11Z 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_poll_command(struct amr_command *ac);
13865245Smsmithstatic int	amr_getslot(struct amr_command *ac);
13965245Smsmithstatic void	amr_mapcmd(struct amr_command *ac);
14065245Smsmithstatic void	amr_unmapcmd(struct amr_command *ac);
14165245Smsmithstatic int	amr_start(struct amr_command *ac);
14265245Smsmithstatic void	amr_complete(void *context, int pending);
14351974Smsmith
14451974Smsmith/*
14558883Smsmith * Status monitoring
14658883Smsmith */
14765245Smsmithstatic void	amr_periodic(void *data);
14858883Smsmith
14958883Smsmith/*
15051974Smsmith * Interface-specific shims
15151974Smsmith */
15265245Smsmithstatic int	amr_quartz_submit_command(struct amr_softc *sc);
15365245Smsmithstatic int	amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
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);
15765245Smsmithstatic void	amr_std_attach_mailbox(struct amr_softc *sc);
15851974Smsmith
15965245Smsmith#ifdef AMR_BOARD_INIT
16065245Smsmithstatic int	amr_quartz_init(struct amr_softc *sc);
16165245Smsmithstatic int	amr_std_init(struct amr_softc *sc);
16265245Smsmith#endif
16365245Smsmith
16451974Smsmith/*
16551974Smsmith * Debugging
16651974Smsmith */
16765245Smsmithstatic void	amr_describe_controller(struct amr_softc *sc);
16865245Smsmith#ifdef AMR_DEBUG
16965245Smsmithstatic void	amr_printcommand(struct amr_command *ac);
17065245Smsmith#endif
17151974Smsmith
17251974Smsmith/********************************************************************************
17351974Smsmith ********************************************************************************
17465245Smsmith                                                                      Inline Glue
17551974Smsmith ********************************************************************************
17651974Smsmith ********************************************************************************/
17751974Smsmith
17851974Smsmith/********************************************************************************
17965245Smsmith ********************************************************************************
18065245Smsmith                                                                Public Interfaces
18165245Smsmith ********************************************************************************
18265245Smsmith ********************************************************************************/
18351974Smsmith
18451974Smsmith/********************************************************************************
18551974Smsmith * Initialise the controller and softc.
18651974Smsmith */
18751974Smsmithint
18851974Smsmithamr_attach(struct amr_softc *sc)
18951974Smsmith{
19051974Smsmith
19165245Smsmith    debug_called(1);
19265245Smsmith
19351974Smsmith    /*
19451974Smsmith     * Initialise per-controller queues.
19551974Smsmith     */
19665245Smsmith    TAILQ_INIT(&sc->amr_completed);
19751974Smsmith    TAILQ_INIT(&sc->amr_freecmds);
19865245Smsmith    TAILQ_INIT(&sc->amr_cmd_clusters);
19965245Smsmith    TAILQ_INIT(&sc->amr_ready);
20059249Sphk    bioq_init(&sc->amr_bioq);
20151974Smsmith
20265245Smsmith#if __FreeBSD_version >= 500005
20351974Smsmith    /*
20465245Smsmith     * Initialise command-completion task.
20565245Smsmith     */
20665245Smsmith    TASK_INIT(&sc->amr_task_complete, 0, amr_complete, sc);
20765245Smsmith#endif
20865245Smsmith
20965245Smsmith    debug(2, "queue init done");
21065245Smsmith
21165245Smsmith    /*
21251974Smsmith     * Configure for this controller type.
21351974Smsmith     */
21465245Smsmith    if (AMR_IS_QUARTZ(sc)) {
21551974Smsmith	sc->amr_submit_command = amr_quartz_submit_command;
21651974Smsmith	sc->amr_get_work       = amr_quartz_get_work;
21751974Smsmith    } else {
21851974Smsmith	sc->amr_submit_command = amr_std_submit_command;
21951974Smsmith	sc->amr_get_work       = amr_std_get_work;
22065245Smsmith	amr_std_attach_mailbox(sc);;
22151974Smsmith    }
22251974Smsmith
22365245Smsmith#ifdef AMR_BOARD_INIT
22465245Smsmith    if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc))))
22565245Smsmith	return(ENXIO);
22665245Smsmith#endif
22751974Smsmith
22851974Smsmith    /*
22965245Smsmith     * Quiz controller for features and limits.
23051974Smsmith     */
23165245Smsmith    if (amr_query_controller(sc))
23265245Smsmith	return(ENXIO);
23351974Smsmith
23465245Smsmith    debug(2, "controller query complete");
23551974Smsmith
23651974Smsmith    /*
23765245Smsmith     * Attach our 'real' SCSI channels to CAM.
23851974Smsmith     */
23965245Smsmith    if (amr_cam_attach(sc))
24051974Smsmith	return(ENXIO);
24165245Smsmith    debug(2, "CAM attach done");
24251974Smsmith
24351974Smsmith    /*
24465245Smsmith     * Create the control device.
24551974Smsmith     */
24665245Smsmith    sc->amr_dev_t = make_dev(&amr_cdevsw, device_get_unit(sc->amr_dev), UID_ROOT, GID_OPERATOR,
24765245Smsmith			     S_IRUSR | S_IWUSR, "amr%d", device_get_unit(sc->amr_dev));
24865245Smsmith    sc->amr_dev_t->si_drv1 = sc;
24951974Smsmith
25051974Smsmith    /*
25165245Smsmith     * Schedule ourselves to bring the controller up once interrupts are
25265245Smsmith     * available.
25351974Smsmith     */
25465245Smsmith    bzero(&sc->amr_ich, sizeof(struct intr_config_hook));
25565245Smsmith    sc->amr_ich.ich_func = amr_startup;
25665245Smsmith    sc->amr_ich.ich_arg = sc;
25765245Smsmith    if (config_intrhook_establish(&sc->amr_ich) != 0) {
25865245Smsmith	device_printf(sc->amr_dev, "can't establish configuration hook\n");
25965245Smsmith	return(ENOMEM);
26065245Smsmith    }
26151974Smsmith
26258883Smsmith    /*
26365245Smsmith     * Print a little information about the controller.
26458883Smsmith     */
26565245Smsmith    amr_describe_controller(sc);
26658883Smsmith
26765245Smsmith    debug(2, "attach complete");
26851974Smsmith    return(0);
26951974Smsmith}
27051974Smsmith
27151974Smsmith/********************************************************************************
27251974Smsmith * Locate disk resources and attach children to them.
27351974Smsmith */
27465245Smsmithstatic void
27565245Smsmithamr_startup(void *arg)
27651974Smsmith{
27765245Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
27851974Smsmith    struct amr_logdrive	*dr;
27951974Smsmith    int			i, error;
28051974Smsmith
28165245Smsmith    debug_called(1);
28251974Smsmith
28365245Smsmith    /* pull ourselves off the intrhook chain */
28465245Smsmith    config_intrhook_disestablish(&sc->amr_ich);
28565245Smsmith
28651974Smsmith    /* get up-to-date drive information */
28751974Smsmith    if (amr_query_controller(sc)) {
28865245Smsmith	device_printf(sc->amr_dev, "can't scan controller for drives\n");
28951974Smsmith	return;
29051974Smsmith    }
29151974Smsmith
29251974Smsmith    /* iterate over available drives */
29351974Smsmith    for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
29451974Smsmith	/* are we already attached to this drive? */
29551974Smsmith	if (dr->al_disk == 0) {
29651974Smsmith	    /* generate geometry information */
29751974Smsmith	    if (dr->al_size > 0x200000) {	/* extended translation? */
29851974Smsmith		dr->al_heads = 255;
29951974Smsmith		dr->al_sectors = 63;
30051974Smsmith	    } else {
30151974Smsmith		dr->al_heads = 64;
30251974Smsmith		dr->al_sectors = 32;
30351974Smsmith	    }
30451974Smsmith	    dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
30551974Smsmith
30654073Smdodd	    dr->al_disk = device_add_child(sc->amr_dev, NULL, -1);
30751974Smsmith	    if (dr->al_disk == 0)
30851974Smsmith		device_printf(sc->amr_dev, "device_add_child failed\n");
30954073Smdodd	    device_set_ivars(dr->al_disk, dr);
31051974Smsmith	}
31151974Smsmith    }
31251974Smsmith
31351974Smsmith    if ((error = bus_generic_attach(sc->amr_dev)) != 0)
31451974Smsmith	device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
31551974Smsmith
31651974Smsmith    /* mark controller back up */
31751974Smsmith    sc->amr_state &= ~AMR_STATE_SHUTDOWN;
31851974Smsmith
31951974Smsmith    /* interrupts will be enabled before we do anything more */
32051974Smsmith    sc->amr_state |= AMR_STATE_INTEN;
32151974Smsmith
32251974Smsmith    /*
32365245Smsmith     * Start the timeout routine.
32451974Smsmith     */
32565245Smsmith/*    sc->amr_timeout = timeout(amr_periodic, sc, hz);*/
32651974Smsmith
32765245Smsmith    return;
32851974Smsmith}
32951974Smsmith
33065245Smsmith/*******************************************************************************
33165245Smsmith * Free resources associated with a controller instance
33251974Smsmith */
33365245Smsmithvoid
33465245Smsmithamr_free(struct amr_softc *sc)
33551974Smsmith{
33665245Smsmith    struct amr_command_cluster	*acc;
33751974Smsmith
33865245Smsmith    /* detach from CAM */
33965245Smsmith    amr_cam_detach(sc);
34051974Smsmith
34165245Smsmith    /* cancel status timeout */
34265245Smsmith    untimeout(amr_periodic, sc, sc->amr_timeout);
34351974Smsmith
34465245Smsmith    /* throw away any command buffers */
34565245Smsmith    while ((acc = TAILQ_FIRST(&sc->amr_cmd_clusters)) != NULL) {
34665245Smsmith	TAILQ_REMOVE(&sc->amr_cmd_clusters, acc, acc_link);
34765245Smsmith	amr_freecmd_cluster(acc);
34851974Smsmith    }
34951974Smsmith}
35051974Smsmith
35151974Smsmith/*******************************************************************************
35265245Smsmith * Receive a bio structure from a child device and queue it on a particular
35351974Smsmith * disk resource, then poke the disk resource to start as much work as it can.
35451974Smsmith */
35551974Smsmithint
35665245Smsmithamr_submit_bio(struct amr_softc *sc, struct bio *bio)
35751974Smsmith{
35865245Smsmith    debug_called(2);
35952543Smsmith
36065245Smsmith    amr_enqueue_bio(sc, bio);
36151974Smsmith    amr_startio(sc);
36251974Smsmith    return(0);
36351974Smsmith}
36451974Smsmith
36551974Smsmith/********************************************************************************
36651974Smsmith * Accept an open operation on the control device.
36751974Smsmith */
368104094Sphkstatic int
369105419Semooreamr_open(dev_t dev, int flags, int fmt, d_thread_t *td)
37051974Smsmith{
37151974Smsmith    int			unit = minor(dev);
37289055Smsmith    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
37351974Smsmith
37465245Smsmith    debug_called(1);
37565245Smsmith
37651974Smsmith    sc->amr_state |= AMR_STATE_OPEN;
37751974Smsmith    return(0);
37851974Smsmith}
37951974Smsmith
38051974Smsmith/********************************************************************************
38151974Smsmith * Accept the last close on the control device.
38251974Smsmith */
383104094Sphkstatic int
384105419Semooreamr_close(dev_t dev, int flags, int fmt, d_thread_t *td)
38551974Smsmith{
38651974Smsmith    int			unit = minor(dev);
38789055Smsmith    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
38851974Smsmith
38965245Smsmith    debug_called(1);
39065245Smsmith
39151974Smsmith    sc->amr_state &= ~AMR_STATE_OPEN;
39251974Smsmith    return (0);
39351974Smsmith}
39451974Smsmith
39551974Smsmith/********************************************************************************
39651974Smsmith * Handle controller-specific control operations.
39751974Smsmith */
398104094Sphkstatic int
399105419Semooreamr_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t *td)
40051974Smsmith{
40165245Smsmith    struct amr_softc		*sc = (struct amr_softc *)dev->si_drv1;
40265245Smsmith    int				*arg = (int *)addr;
40365245Smsmith    struct amr_user_ioctl	*au = (struct amr_user_ioctl *)addr;
40465245Smsmith    struct amr_command		*ac;
40565245Smsmith    struct amr_mailbox_ioctl	*mbi;
40665245Smsmith    struct amr_passthrough	*ap;
40765245Smsmith    void			*dp;
40865245Smsmith    int				error;
40965245Smsmith
41065245Smsmith    debug_called(1);
41165245Smsmith
41265245Smsmith    error = 0;
41365245Smsmith    dp = NULL;
41465245Smsmith    ap = NULL;
41565245Smsmith    ac = NULL;
41651974Smsmith    switch(cmd) {
41765245Smsmith
41865245Smsmith    case AMR_IO_VERSION:
41965245Smsmith	debug(1, "AMR_IO_VERSION");
42065245Smsmith	*arg = AMR_IO_VERSION_NUMBER;
42165245Smsmith	break;
42265245Smsmith
42365245Smsmith    case AMR_IO_COMMAND:
42469319Smsmith	debug(1, "AMR_IO_COMMAND  0x%x", au->au_cmd[0]);
42565245Smsmith	/* handle inbound data buffer */
42665245Smsmith	if (au->au_length != 0) {
42765245Smsmith	    if ((dp = malloc(au->au_length, M_DEVBUF, M_WAITOK)) == NULL) {
42865245Smsmith		error = ENOMEM;
42965245Smsmith		break;
43065245Smsmith	    }
43165245Smsmith	    if ((error = copyin(au->au_buffer, dp, au->au_length)) != 0)
43265245Smsmith		break;
43369319Smsmith	    debug(2, "copyin %ld bytes from %p -> %p", au->au_length, au->au_buffer, dp);
43465245Smsmith	}
43565245Smsmith
43665245Smsmith	if ((ac = amr_alloccmd(sc)) == NULL) {
43765245Smsmith	    error = ENOMEM;
43865245Smsmith	    break;
43965245Smsmith	}
44065245Smsmith
44165245Smsmith	/* handle SCSI passthrough command */
44265245Smsmith	if (au->au_cmd[0] == AMR_CMD_PASS) {
44368877Sdwmalone	    if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) {
44465245Smsmith		error = ENOMEM;
44565245Smsmith		break;
44665245Smsmith	    }
44765245Smsmith
44865245Smsmith	    /* copy cdb */
44965245Smsmith	    ap->ap_cdb_length = au->au_cmd[2];
45065245Smsmith	    bcopy(&au->au_cmd[3], &ap->ap_cdb[0], ap->ap_cdb_length);
45165245Smsmith
45265245Smsmith	    /* build passthrough */
45365245Smsmith	    ap->ap_timeout		= au->au_cmd[ap->ap_cdb_length + 3] & 0x07;
45465245Smsmith	    ap->ap_ars			= (au->au_cmd[ap->ap_cdb_length + 3] & 0x08) ? 1 : 0;
45565245Smsmith	    ap->ap_islogical		= (au->au_cmd[ap->ap_cdb_length + 3] & 0x80) ? 1 : 0;
45665245Smsmith	    ap->ap_logical_drive_no	= au->au_cmd[ap->ap_cdb_length + 4];
45765245Smsmith	    ap->ap_channel		= au->au_cmd[ap->ap_cdb_length + 5];
45865245Smsmith	    ap->ap_scsi_id 		= au->au_cmd[ap->ap_cdb_length + 6];
45965245Smsmith	    ap->ap_request_sense_length	= 14;
460105419Semoore	    ap->ap_data_transfer_length = au->au_length;
46165245Smsmith	    /* XXX what about the request-sense area? does the caller want it? */
46265245Smsmith
46365245Smsmith	    /* build command */
46465245Smsmith	    ac->ac_data = ap;
46565245Smsmith	    ac->ac_length = sizeof(*ap);
46665245Smsmith	    ac->ac_flags |= AMR_CMD_DATAOUT;
46765245Smsmith	    ac->ac_ccb_data = dp;
46865245Smsmith	    ac->ac_ccb_length = au->au_length;
46965245Smsmith	    if (au->au_direction & AMR_IO_READ)
47065245Smsmith		ac->ac_flags |= AMR_CMD_CCB_DATAIN;
47165245Smsmith	    if (au->au_direction & AMR_IO_WRITE)
47265245Smsmith		ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
47365245Smsmith
47465245Smsmith	    ac->ac_mailbox.mb_command = AMR_CMD_PASS;
47565245Smsmith
47665245Smsmith	} else {
47765245Smsmith	    /* direct command to controller */
47865245Smsmith	    mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
47965245Smsmith
48065245Smsmith	    /* copy pertinent mailbox items */
48165245Smsmith	    mbi->mb_command = au->au_cmd[0];
48265245Smsmith	    mbi->mb_channel = au->au_cmd[1];
48365245Smsmith	    mbi->mb_param = au->au_cmd[2];
48465245Smsmith	    mbi->mb_pad[0] = au->au_cmd[3];
48565245Smsmith	    mbi->mb_drive = au->au_cmd[4];
48665245Smsmith
48765245Smsmith	    /* build the command */
48865245Smsmith	    ac->ac_data = dp;
48965245Smsmith	    ac->ac_length = au->au_length;
49065245Smsmith	    if (au->au_direction & AMR_IO_READ)
49165245Smsmith		ac->ac_flags |= AMR_CMD_DATAIN;
49265245Smsmith	    if (au->au_direction & AMR_IO_WRITE)
49365245Smsmith		ac->ac_flags |= AMR_CMD_DATAOUT;
49465245Smsmith	}
49565245Smsmith
49665245Smsmith	/* run the command */
49765245Smsmith	if ((error = amr_wait_command(ac)) != 0)
49865245Smsmith	    break;
49965245Smsmith
50065245Smsmith	/* copy out data and set status */
50165245Smsmith	if (au->au_length != 0)
50265245Smsmith	    error = copyout(dp, au->au_buffer, au->au_length);
50369319Smsmith	debug(2, "copyout %ld bytes from %p -> %p", au->au_length, dp, au->au_buffer);
50469319Smsmith	if (dp != NULL)
50569319Smsmith	    debug(2, "%16D", dp, " ");
50665245Smsmith	au->au_status = ac->ac_status;
50765245Smsmith	break;
508105419Semoore
50965245Smsmith    default:
51065245Smsmith	debug(1, "unknown ioctl 0x%lx", cmd);
51165245Smsmith	error = ENOIOCTL;
51265245Smsmith	break;
51351974Smsmith    }
51451974Smsmith
51565245Smsmith    if (dp != NULL)
51665245Smsmith	free(dp, M_DEVBUF);
51765245Smsmith    if (ap != NULL)
51865245Smsmith	free(ap, M_DEVBUF);
51965245Smsmith    if (ac != NULL)
52065245Smsmith	amr_releasecmd(ac);
52165245Smsmith    return(error);
52251974Smsmith}
52351974Smsmith
52451974Smsmith/********************************************************************************
52551974Smsmith ********************************************************************************
52658883Smsmith                                                                Status Monitoring
52758883Smsmith ********************************************************************************
52858883Smsmith ********************************************************************************/
52958883Smsmith
53058883Smsmith/********************************************************************************
53158883Smsmith * Perform a periodic check of the controller status
53258883Smsmith */
53358883Smsmithstatic void
53458883Smsmithamr_periodic(void *data)
53558883Smsmith{
53658883Smsmith    struct amr_softc	*sc = (struct amr_softc *)data;
53758883Smsmith
53865245Smsmith    debug_called(2);
53958883Smsmith
54065245Smsmith    /* XXX perform periodic status checks here */
54158883Smsmith
54265245Smsmith    /* compensate for missed interrupts */
54365245Smsmith    amr_done(sc);
54465245Smsmith
54558883Smsmith    /* reschedule */
54658883Smsmith    sc->amr_timeout = timeout(amr_periodic, sc, hz);
54758883Smsmith}
54858883Smsmith
54958883Smsmith/********************************************************************************
55058883Smsmith ********************************************************************************
55151974Smsmith                                                                 Command Wrappers
55251974Smsmith ********************************************************************************
55351974Smsmith ********************************************************************************/
55451974Smsmith
55551974Smsmith/********************************************************************************
55651974Smsmith * Interrogate the controller for the operational parameters we require.
55751974Smsmith */
55851974Smsmithstatic int
55951974Smsmithamr_query_controller(struct amr_softc *sc)
56051974Smsmith{
56165245Smsmith    struct amr_enquiry3	*aex;
56265245Smsmith    struct amr_prodinfo	*ap;
56365245Smsmith    struct amr_enquiry	*ae;
56465245Smsmith    int			ldrv;
56551974Smsmith
56665245Smsmith    /*
56765245Smsmith     * If we haven't found the real limit yet, let us have a couple of commands in
56865245Smsmith     * order to be able to probe.
56965245Smsmith     */
57065245Smsmith    if (sc->amr_maxio == 0)
57165245Smsmith	sc->amr_maxio = 2;
57251974Smsmith
573106225Semoore    /*
574106225Semoore     * Greater than 10 byte cdb support
575106225Semoore     */
576106225Semoore    sc->support_ext_cdb = amr_support_ext_cdb(sc);
577106225Semoore
578106225Semoore    if(sc->support_ext_cdb) {
579106225Semoore	debug(2,"supports extended CDBs.");
580106225Semoore    }
581106225Semoore
58265245Smsmith    /*
58365245Smsmith     * Try to issue an ENQUIRY3 command
58465245Smsmith     */
58565245Smsmith    if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
58665245Smsmith			   AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) {
58751974Smsmith
58865245Smsmith	/*
58965245Smsmith	 * Fetch current state of logical drives.
59065245Smsmith	 */
59165245Smsmith	for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) {
59265245Smsmith	    sc->amr_drive[ldrv].al_size       = aex->ae_drivesize[ldrv];
59365245Smsmith	    sc->amr_drive[ldrv].al_state      = aex->ae_drivestate[ldrv];
59465245Smsmith	    sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[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	free(aex, M_DEVBUF);
59958883Smsmith
60065245Smsmith	/*
60165245Smsmith	 * Get product info for channel count.
60258883Smsmith	 */
60365245Smsmith	if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) {
60465245Smsmith	    device_printf(sc->amr_dev, "can't obtain product data from controller\n");
60565245Smsmith	    return(1);
60665245Smsmith	}
60765245Smsmith	sc->amr_maxdrives = 40;
60865245Smsmith	sc->amr_maxchan = ap->ap_nschan;
60965245Smsmith	sc->amr_maxio = ap->ap_maxio;
61065245Smsmith	sc->amr_type |= AMR_TYPE_40LD;
61165245Smsmith	free(ap, M_DEVBUF);
61258883Smsmith
61365245Smsmith    } else {
61465245Smsmith
61565245Smsmith	/* failed, try the 8LD ENQUIRY commands */
61665245Smsmith	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) {
61765245Smsmith	    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
61865245Smsmith		device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
61965245Smsmith		return(1);
62065245Smsmith	    }
62165245Smsmith	    ae->ae_signature = 0;
62251974Smsmith	}
62365245Smsmith
62458883Smsmith	/*
62565245Smsmith	 * Fetch current state of logical drives.
62658883Smsmith	 */
62765245Smsmith	for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) {
62865245Smsmith	    sc->amr_drive[ldrv].al_size       = ae->ae_ldrv.al_size[ldrv];
62965245Smsmith	    sc->amr_drive[ldrv].al_state      = ae->ae_ldrv.al_state[ldrv];
63065245Smsmith	    sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv];
63165245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
63265245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
63351974Smsmith	}
63465245Smsmith
63565245Smsmith	sc->amr_maxdrives = 8;
63665245Smsmith	sc->amr_maxchan = ae->ae_adapter.aa_channels;
63765245Smsmith	sc->amr_maxio = ae->ae_adapter.aa_maxio;
63865245Smsmith	free(ae, M_DEVBUF);
63951974Smsmith    }
64065245Smsmith
64165245Smsmith    /*
64265245Smsmith     * Mark remaining drives as unused.
64365245Smsmith     */
64465245Smsmith    for (; ldrv < AMR_MAXLD; ldrv++)
64565245Smsmith	sc->amr_drive[ldrv].al_size = 0xffffffff;
64665245Smsmith
64765245Smsmith    /*
64865245Smsmith     * Cap the maximum number of outstanding I/Os.  AMI's Linux driver doesn't trust
64965245Smsmith     * the controller's reported value, and lockups have been seen when we do.
65065245Smsmith     */
65165245Smsmith    sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD);
65265245Smsmith
65351974Smsmith    return(0);
65451974Smsmith}
65551974Smsmith
65651974Smsmith/********************************************************************************
65751974Smsmith * Run a generic enquiry-style command.
65851974Smsmith */
65951974Smsmithstatic void *
66051974Smsmithamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
66151974Smsmith{
66251974Smsmith    struct amr_command	*ac;
66351974Smsmith    void		*result;
66451974Smsmith    u_int8_t		*mbox;
66551974Smsmith    int			error;
66651974Smsmith
66765245Smsmith    debug_called(1);
66851974Smsmith
66951974Smsmith    error = 1;
67051974Smsmith    result = NULL;
67151974Smsmith
67251974Smsmith    /* get ourselves a command buffer */
67351974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
67451974Smsmith	goto out;
67551974Smsmith    /* allocate the response structure */
67651974Smsmith    if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
67751974Smsmith	goto out;
67865245Smsmith    /* set command flags */
67951974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
68051974Smsmith
68165245Smsmith    /* point the command at our data */
68251974Smsmith    ac->ac_data = result;
68351974Smsmith    ac->ac_length = bufsize;
68451974Smsmith
68551974Smsmith    /* build the command proper */
68651974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
68751974Smsmith    mbox[0] = cmd;
68851974Smsmith    mbox[2] = cmdsub;
68951974Smsmith    mbox[3] = cmdqual;
69051974Smsmith
69158883Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
69258883Smsmith    if (amr_poll_command(ac))
69351974Smsmith	goto out;
69451974Smsmith    error = ac->ac_status;
69551974Smsmith
69651974Smsmith out:
69751974Smsmith    if (ac != NULL)
69851974Smsmith	amr_releasecmd(ac);
69951974Smsmith    if ((error != 0) && (result != NULL)) {
70051974Smsmith	free(result, M_DEVBUF);
70151974Smsmith	result = NULL;
70251974Smsmith    }
70351974Smsmith    return(result);
70451974Smsmith}
70551974Smsmith
70651974Smsmith/********************************************************************************
70751974Smsmith * Flush the controller's internal cache, return status.
70851974Smsmith */
70965245Smsmithint
71051974Smsmithamr_flush(struct amr_softc *sc)
71151974Smsmith{
71251974Smsmith    struct amr_command	*ac;
71351974Smsmith    int			error;
71451974Smsmith
71551974Smsmith    /* get ourselves a command buffer */
71651974Smsmith    error = 1;
71751974Smsmith    if ((ac = amr_alloccmd(sc)) == NULL)
71851974Smsmith	goto out;
71965245Smsmith    /* set command flags */
72051974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
72151974Smsmith
72251974Smsmith    /* build the command proper */
72351974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
72451974Smsmith
72558883Smsmith    /* we have to poll, as the system may be going down or otherwise damaged */
72658883Smsmith    if (amr_poll_command(ac))
72751974Smsmith	goto out;
72851974Smsmith    error = ac->ac_status;
72951974Smsmith
73051974Smsmith out:
73151974Smsmith    if (ac != NULL)
73251974Smsmith	amr_releasecmd(ac);
73351974Smsmith    return(error);
73451974Smsmith}
73551974Smsmith
73651974Smsmith/********************************************************************************
737106225Semoore * Detect extented cdb >> greater than 10 byte cdb support
738106225Semoore * returns '1' means this support exist
739106225Semoore * returns '0' means this support doesn't exist
740106225Semoore */
741106225Semoorestatic int
742106225Semooreamr_support_ext_cdb(struct amr_softc *sc)
743106225Semoore{
744106225Semoore    struct amr_command	*ac;
745106225Semoore    u_int8_t		*mbox;
746106225Semoore    int			error;
747106225Semoore
748106225Semoore    /* get ourselves a command buffer */
749106225Semoore    error = 0;
750106225Semoore    if ((ac = amr_alloccmd(sc)) == NULL)
751106225Semoore	goto out;
752106225Semoore    /* set command flags */
753106225Semoore    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
754106225Semoore
755106225Semoore    /* build the command proper */
756106225Semoore    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
757106225Semoore    mbox[0] = 0xA4;
758106225Semoore    mbox[2] = 0x16;
759106225Semoore
760106225Semoore
761106225Semoore    /* we have to poll, as the system may be going down or otherwise damaged */
762106225Semoore    if (amr_poll_command(ac))
763106225Semoore	goto out;
764106225Semoore    if( ac->ac_status == AMR_STATUS_SUCCESS ) {
765106225Semoore	    error = 1;
766106225Semoore    }
767106225Semoore
768106225Semooreout:
769106225Semoore    if (ac != NULL)
770106225Semoore	amr_releasecmd(ac);
771106225Semoore    return(error);
772106225Semoore}
773106225Semoore
774106225Semoore/********************************************************************************
77565245Smsmith * Try to find I/O work for the controller from one or more of the work queues.
77651974Smsmith *
77765245Smsmith * We make the assumption that if the controller is not ready to take a command
77865245Smsmith * at some given time, it will generate an interrupt at some later time when
77965245Smsmith * it is.
78051974Smsmith */
78165245Smsmithvoid
78251974Smsmithamr_startio(struct amr_softc *sc)
78351974Smsmith{
78451974Smsmith    struct amr_command	*ac;
78551974Smsmith
78651974Smsmith    /* spin until something prevents us from doing any work */
78751974Smsmith    for (;;) {
78851974Smsmith
78965245Smsmith	/* try to get a ready command */
79065245Smsmith	ac = amr_dequeue_ready(sc);
79151974Smsmith
79265245Smsmith	/* if that failed, build a command from a bio */
79365245Smsmith	if (ac == NULL)
79465245Smsmith	    (void)amr_bio_command(sc, &ac);
79551974Smsmith
79665245Smsmith	/* if that failed, build a command from a ccb */
79765245Smsmith	if (ac == NULL)
79865245Smsmith	    (void)amr_cam_command(sc, &ac);
799105419Semoore
80065245Smsmith	/* if we don't have anything to do, give up */
80165245Smsmith	if (ac == NULL)
80265245Smsmith	    break;
80351974Smsmith
80465245Smsmith	/* try to give the command to the controller; if this fails save it for later and give up */
80565245Smsmith	if (amr_start(ac)) {
80665245Smsmith	    debug(2, "controller busy, command deferred");
80765245Smsmith	    amr_requeue_ready(ac);	/* XXX schedule retry very soon? */
80865245Smsmith	    break;
80951974Smsmith	}
81051974Smsmith    }
81151974Smsmith}
81251974Smsmith
81351974Smsmith/********************************************************************************
81451974Smsmith * Handle completion of an I/O command.
81551974Smsmith */
81651974Smsmithstatic void
81751974Smsmithamr_completeio(struct amr_command *ac)
81851974Smsmith{
81951974Smsmith    struct amr_softc	*sc = ac->ac_sc;
82051974Smsmith
82151974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
82265245Smsmith	ac->ac_bio->bio_error = EIO;
82365245Smsmith	ac->ac_bio->bio_flags |= BIO_ERROR;
82451974Smsmith
82565245Smsmith	device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status);
82665245Smsmith/*	amr_printcommand(ac);*/
82751974Smsmith    }
82865245Smsmith    amrd_intr(ac->ac_bio);
82965245Smsmith    amr_releasecmd(ac);
83051974Smsmith}
83151974Smsmith
83251974Smsmith/********************************************************************************
83351974Smsmith ********************************************************************************
83451974Smsmith                                                               Command Processing
83551974Smsmith ********************************************************************************
83651974Smsmith ********************************************************************************/
83751974Smsmith
83851974Smsmith/********************************************************************************
83965245Smsmith * Convert a bio off the top of the bio queue into a command.
84065245Smsmith */
84165245Smsmithstatic int
84265245Smsmithamr_bio_command(struct amr_softc *sc, struct amr_command **acp)
84365245Smsmith{
84465245Smsmith    struct amr_command	*ac;
84565245Smsmith    struct amrd_softc	*amrd;
84665245Smsmith    struct bio		*bio;
84765245Smsmith    int			error;
84865245Smsmith    int			blkcount;
84965245Smsmith    int			driveno;
85065245Smsmith    int			cmd;
85165245Smsmith
85265245Smsmith    ac = NULL;
85365245Smsmith    error = 0;
85465245Smsmith
85565245Smsmith    /* get a bio to work on */
85665245Smsmith    if ((bio = amr_dequeue_bio(sc)) == NULL)
85765245Smsmith	goto out;
85865245Smsmith
85965245Smsmith    /* get a command */
86065245Smsmith    if ((ac = amr_alloccmd(sc)) == NULL) {
86165245Smsmith	error = ENOMEM;
86265245Smsmith	goto out;
86365245Smsmith    }
86465245Smsmith
86565245Smsmith    /* connect the bio to the command */
86665245Smsmith    ac->ac_complete = amr_completeio;
86765245Smsmith    ac->ac_bio = bio;
86865245Smsmith    ac->ac_data = bio->bio_data;
86965245Smsmith    ac->ac_length = bio->bio_bcount;
87065245Smsmith    if (BIO_IS_READ(bio)) {
87165245Smsmith	ac->ac_flags |= AMR_CMD_DATAIN;
87265245Smsmith	cmd = AMR_CMD_LREAD;
87365245Smsmith    } else {
87465245Smsmith	ac->ac_flags |= AMR_CMD_DATAOUT;
87565245Smsmith	cmd = AMR_CMD_LWRITE;
87665245Smsmith    }
87765245Smsmith    amrd = (struct amrd_softc *)bio->bio_dev->si_drv1;
87865245Smsmith    driveno = amrd->amrd_drive - sc->amr_drive;
87965245Smsmith    blkcount = (bio->bio_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
88065245Smsmith
88165245Smsmith    ac->ac_mailbox.mb_command = cmd;
88265245Smsmith    ac->ac_mailbox.mb_blkcount = blkcount;
88365245Smsmith    ac->ac_mailbox.mb_lba = bio->bio_pblkno;
88465245Smsmith    ac->ac_mailbox.mb_drive = driveno;
88565245Smsmith    /* we fill in the s/g related data when the command is mapped */
88665245Smsmith
88765245Smsmith    if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
88892610Sbde	device_printf(sc->amr_dev, "I/O beyond end of unit (%lld,%d > %lu)\n",
88992610Sbde		      (long long)bio->bio_pblkno, blkcount,
89092610Sbde		      (u_long)sc->amr_drive[driveno].al_size);
89165245Smsmith
89265245Smsmithout:
89365245Smsmith    if (error != 0) {
89465245Smsmith	if (ac != NULL)
89565245Smsmith	    amr_releasecmd(ac);
89665245Smsmith	if (bio != NULL)			/* this breaks ordering... */
89765245Smsmith	    amr_enqueue_bio(sc, bio);
89865245Smsmith    }
89965245Smsmith    *acp = ac;
90065245Smsmith    return(error);
90165245Smsmith}
90265245Smsmith
90365245Smsmith/********************************************************************************
90451974Smsmith * Take a command, submit it to the controller and sleep until it completes
90551974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
90651974Smsmith */
90751974Smsmithstatic int
90851974Smsmithamr_wait_command(struct amr_command *ac)
90951974Smsmith{
91051974Smsmith    int			error, count;
91151974Smsmith
91265245Smsmith    debug_called(1);
91351974Smsmith
91451974Smsmith    ac->ac_complete = NULL;
91565245Smsmith    ac->ac_flags |= AMR_CMD_SLEEP;
91651974Smsmith    if ((error = amr_start(ac)) != 0)
91751974Smsmith	return(error);
91851974Smsmith
91951974Smsmith    count = 0;
92051974Smsmith    /* XXX better timeout? */
92165245Smsmith    while ((ac->ac_flags & AMR_CMD_BUSY) && (count < 30)) {
92265245Smsmith	tsleep(ac, PRIBIO | PCATCH, "amrwcmd", hz);
92351974Smsmith    }
92451974Smsmith    return(0);
92551974Smsmith}
92651974Smsmith
92751974Smsmith/********************************************************************************
92851974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
92951974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
93051974Smsmith */
93151974Smsmithstatic int
93251974Smsmithamr_poll_command(struct amr_command *ac)
93351974Smsmith{
93451974Smsmith    struct amr_softc	*sc = ac->ac_sc;
93565245Smsmith    int			error, count;
93651974Smsmith
93765245Smsmith    debug_called(2);
93851974Smsmith
93951974Smsmith    ac->ac_complete = NULL;
94051974Smsmith    if ((error = amr_start(ac)) != 0)
94151974Smsmith	return(error);
94251974Smsmith
94351974Smsmith    count = 0;
94451974Smsmith    do {
94551974Smsmith	/*
94651974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
94751974Smsmith	 * Note that the timeout here is somewhat arbitrary.
94851974Smsmith	 */
94951974Smsmith	amr_done(sc);
95065245Smsmith	DELAY(1000);
95165245Smsmith    } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000));
95265245Smsmith    if (!(ac->ac_flags & AMR_CMD_BUSY)) {
95351974Smsmith	error = 0;
95451974Smsmith    } else {
95565245Smsmith	/* XXX the slot is now marked permanently busy */
95651974Smsmith	error = EIO;
95765245Smsmith	device_printf(sc->amr_dev, "polled command timeout\n");
95851974Smsmith    }
95951974Smsmith    return(error);
96051974Smsmith}
96151974Smsmith
96251974Smsmith/********************************************************************************
96365245Smsmith * Get a free command slot for a command if it doesn't already have one.
96465245Smsmith *
96565245Smsmith * May be safely called multiple times for a given command.
96651974Smsmith */
96751974Smsmithstatic int
96851974Smsmithamr_getslot(struct amr_command *ac)
96951974Smsmith{
97051974Smsmith    struct amr_softc	*sc = ac->ac_sc;
97165245Smsmith    int			s, slot, limit, error;
97251974Smsmith
97365245Smsmith    debug_called(3);
97465245Smsmith
97565245Smsmith    /* if the command already has a slot, don't try to give it another one */
97665245Smsmith    if (ac->ac_slot != 0)
97765245Smsmith	return(0);
97865245Smsmith
97951974Smsmith    /* enforce slot usage limit */
98051974Smsmith    limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
98165245Smsmith    if (sc->amr_busyslots > limit)
98251974Smsmith	return(EBUSY);
98351974Smsmith
98451974Smsmith    /*
98565245Smsmith     * Allocate a slot.  XXX linear scan is slow
98651974Smsmith     */
98765245Smsmith    error = EBUSY;
98851974Smsmith    s = splbio();
98951974Smsmith    for (slot = 0; slot < sc->amr_maxio; slot++) {
99065245Smsmith	if (sc->amr_busycmd[slot] == NULL) {
99165245Smsmith	    sc->amr_busycmd[slot] = ac;
99265245Smsmith	    sc->amr_busyslots++;
99365245Smsmith	    ac->ac_slot = slot;
99465245Smsmith	    error = 0;
99551974Smsmith	    break;
99665245Smsmith	}
99751974Smsmith    }
99851974Smsmith    splx(s);
99951974Smsmith
100065245Smsmith    return(error);
100151974Smsmith}
100251974Smsmith
100351974Smsmith/********************************************************************************
100465245Smsmith * Map/unmap (ac)'s data in the controller's addressable space as required.
100565245Smsmith *
100665245Smsmith * These functions may be safely called multiple times on a given command.
100751974Smsmith */
100851974Smsmithstatic void
100951974Smsmithamr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
101051974Smsmith{
101151974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
101251974Smsmith    struct amr_softc	*sc = ac->ac_sc;
101351974Smsmith    struct amr_sgentry	*sg;
101451974Smsmith    int			i;
101569319Smsmith    u_int8_t		*sgc;
101651974Smsmith
101765245Smsmith    debug_called(3);
101851974Smsmith
101951974Smsmith    /* get base address of s/g table */
102051974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
102151974Smsmith
102265245Smsmith    /* save data physical address */
102351974Smsmith    ac->ac_dataphys = segs[0].ds_addr;
102451974Smsmith
102569319Smsmith    /* for AMR_CMD_CONFIG the s/g count goes elsewhere */
102669319Smsmith    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) {
102769319Smsmith	sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param);
102869319Smsmith    } else {
102969319Smsmith	sgc = &ac->ac_mailbox.mb_nsgelem;
103069319Smsmith    }
103169319Smsmith
103265245Smsmith    /* decide whether we need to populate the s/g table */
103365245Smsmith    if (nsegments < 2) {
103469319Smsmith	*sgc = 0;
1035105419Semoore	ac->ac_mailbox.mb_nsgelem = 0;
103665245Smsmith	ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
103765245Smsmith    } else {
1038105419Semoore        ac->ac_mailbox.mb_nsgelem = nsegments;
103969319Smsmith	*sgc = nsegments;
104065245Smsmith	ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
104165245Smsmith	for (i = 0; i < nsegments; i++, sg++) {
104265245Smsmith	    sg->sg_addr = segs[i].ds_addr;
104365245Smsmith	    sg->sg_count = segs[i].ds_len;
104465245Smsmith	}
104565245Smsmith    }
104665245Smsmith}
104765245Smsmith
104865245Smsmithstatic void
104965245Smsmithamr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
105065245Smsmith{
1051105419Semoore    struct amr_command          *ac = (struct amr_command *)arg;
1052105419Semoore    struct amr_softc            *sc = ac->ac_sc;
1053105419Semoore    struct amr_sgentry          *sg;
1054105419Semoore    struct amr_passthrough      *ap = (struct amr_passthrough *)ac->ac_data;
1055106225Semoore    struct amr_ext_passthrough	*aep = (struct amr_passthrough *)ac->ac_data;
1056105419Semoore    int                         i;
105765245Smsmith
105865245Smsmith    /* get base address of s/g table */
105965245Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
106065245Smsmith
1061105419Semoore    /* decide whether we need to populate the s/g table */
1062106225Semoore    if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) {
1063106225Semoore	if (nsegments < 2) {
1064106225Semoore	    aep->ap_no_sg_elements = 0;
1065106225Semoore	    aep->ap_data_transfer_address =  segs[0].ds_addr;
1066106225Semoore	} else {
1067106225Semoore	    /* save s/g table information in passthrough */
1068106225Semoore	    aep->ap_no_sg_elements = nsegments;
1069106225Semoore	    aep->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
1070106225Semoore	    /* populate s/g table (overwrites previous call which mapped the passthrough) */
1071106225Semoore	    for (i = 0; i < nsegments; i++, sg++) {
1072106225Semoore		sg->sg_addr = segs[i].ds_addr;
1073106225Semoore		sg->sg_count = segs[i].ds_len;
1074106225Semoore		debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
1075106225Semoore	    }
1076106225Semoore	}
1077106225Semoore	debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
1078106225Semoore	    aep->ap_no_sg_elements, aep->ap_data_transfer_address, ac->ac_dataphys);
1079105419Semoore    } else {
1080106225Semoore	if (nsegments < 2) {
1081106225Semoore	    ap->ap_no_sg_elements = 0;
1082106225Semoore	    ap->ap_data_transfer_address =  segs[0].ds_addr;
1083106225Semoore	} else {
1084106225Semoore	    /* save s/g table information in passthrough */
1085106225Semoore	    ap->ap_no_sg_elements = nsegments;
1086106225Semoore	    ap->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
1087106225Semoore	    /* populate s/g table (overwrites previous call which mapped the passthrough) */
1088106225Semoore	    for (i = 0; i < nsegments; i++, sg++) {
1089105419Semoore		sg->sg_addr = segs[i].ds_addr;
1090105419Semoore		sg->sg_count = segs[i].ds_len;
1091105419Semoore		debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
1092106225Semoore	    }
1093105419Semoore	}
1094106225Semoore	debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
1095106225Semoore	    ap->ap_no_sg_elements, ap->ap_data_transfer_address, ac->ac_dataphys);
1096105419Semoore    }
109751974Smsmith}
109851974Smsmith
109951974Smsmithstatic void
110051974Smsmithamr_mapcmd(struct amr_command *ac)
110151974Smsmith{
110251974Smsmith    struct amr_softc	*sc = ac->ac_sc;
110351974Smsmith
110469319Smsmith    debug_called(3);
110551974Smsmith
110665245Smsmith    /* if the command involves data at all, and hasn't been mapped */
110765245Smsmith    if (!(ac->ac_flags & AMR_CMD_MAPPED)) {
110865245Smsmith
110965245Smsmith	if (ac->ac_data != NULL) {
111065245Smsmith	    /* map the data buffers into bus space and build the s/g list */
1111105419Semoore	    bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
111265245Smsmith			    amr_setup_dmamap, ac, 0);
111365245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
111465245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
111565245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
111665245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
111765245Smsmith	}
111865245Smsmith
111965245Smsmith	if (ac->ac_ccb_data != NULL) {
1120105419Semoore	    bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, ac->ac_ccb_data, ac->ac_ccb_length,
112165245Smsmith			    amr_setup_ccbmap, ac, 0);
112265245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
112365245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREREAD);
112465245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
112565245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREWRITE);
112665245Smsmith	}
112765245Smsmith	ac->ac_flags |= AMR_CMD_MAPPED;
112851974Smsmith    }
112951974Smsmith}
113051974Smsmith
113151974Smsmithstatic void
113251974Smsmithamr_unmapcmd(struct amr_command *ac)
113351974Smsmith{
113451974Smsmith    struct amr_softc	*sc = ac->ac_sc;
113551974Smsmith
113669319Smsmith    debug_called(3);
113751974Smsmith
113865245Smsmith    /* if the command involved data at all and was mapped */
113965245Smsmith    if (ac->ac_flags & AMR_CMD_MAPPED) {
114051974Smsmith
114165245Smsmith	if (ac->ac_data != NULL) {
114265245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
114365245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
114465245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
114565245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
114665245Smsmith	    bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
114765245Smsmith	}
114865245Smsmith
114965245Smsmith	if (ac->ac_ccb_data != NULL) {
115065245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
115165245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTREAD);
115265245Smsmith	    if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
115365245Smsmith		bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTWRITE);
115465245Smsmith	    bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap);
115565245Smsmith	}
115665245Smsmith	ac->ac_flags &= ~AMR_CMD_MAPPED;
115751974Smsmith    }
115851974Smsmith}
115951974Smsmith
116051974Smsmith/********************************************************************************
116165245Smsmith * Take a command and give it to the controller, returns 0 if successful, or
116265245Smsmith * EBUSY if the command should be retried later.
116351974Smsmith */
116451974Smsmithstatic int
116551974Smsmithamr_start(struct amr_command *ac)
116651974Smsmith{
116751974Smsmith    struct amr_softc	*sc = ac->ac_sc;
116858883Smsmith    int			done, s, i;
1169105419Semoore
117069319Smsmith    debug_called(3);
117151974Smsmith
117265245Smsmith    /* mark command as busy so that polling consumer can tell */
117365245Smsmith    ac->ac_flags |= AMR_CMD_BUSY;
117465245Smsmith
117565245Smsmith    /* get a command slot (freed in amr_done) */
117665245Smsmith    if (amr_getslot(ac))
117765245Smsmith	return(EBUSY);
117865245Smsmith
117965245Smsmith    /* now we have a slot, we can map the command (unmapped in amr_complete) */
118065245Smsmith    amr_mapcmd(ac);
118165245Smsmith
118265245Smsmith    /* mark the new mailbox we are going to copy in as busy */
118365245Smsmith    ac->ac_mailbox.mb_busy = 1;
118465245Smsmith
118565245Smsmith    /* clear the poll/ack fields in the mailbox */
118665245Smsmith    sc->amr_mailbox->mb_poll = 0;
118765245Smsmith    sc->amr_mailbox->mb_ack = 0;
118865245Smsmith
118951974Smsmith    /*
119051974Smsmith     * Save the slot number so that we can locate this command when complete.
119151974Smsmith     * Note that ident = 0 seems to be special, so we don't use it.
119251974Smsmith     */
119351974Smsmith    ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
119451974Smsmith
119558883Smsmith    /*
119665245Smsmith     * Spin waiting for the mailbox, give up after ~1 second.  We expect the
119765245Smsmith     * controller to be able to handle our I/O.
119865245Smsmith     *
119965245Smsmith     * XXX perhaps we should wait for less time, and count on the deferred command
120065245Smsmith     * handling to deal with retries?
120158883Smsmith     */
120269319Smsmith    debug(4, "wait for mailbox");
120358883Smsmith    for (i = 10000, done = 0; (i > 0) && !done; i--) {
120451974Smsmith	s = splbio();
120551974Smsmith
120651974Smsmith	/* is the mailbox free? */
120751974Smsmith	if (sc->amr_mailbox->mb_busy == 0) {
120869319Smsmith	    debug(4, "got mailbox");
120951974Smsmith	    sc->amr_mailbox64->mb64_segment = 0;
121065245Smsmith	    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
121151974Smsmith	    done = 1;
121251974Smsmith
121365245Smsmith	    /* not free, spin waiting */
121451974Smsmith	} else {
121569319Smsmith	    debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy);
121658883Smsmith	    /* this is somewhat ugly */
121758883Smsmith	    DELAY(100);
121851974Smsmith	}
121958883Smsmith	splx(s);	/* drop spl to allow completion interrupts */
122051974Smsmith    }
122165245Smsmith
122265245Smsmith    /*
122365245Smsmith     * Now give the command to the controller
122465245Smsmith     */
122551974Smsmith    if (done) {
122665245Smsmith	if (sc->amr_submit_command(sc)) {
122765245Smsmith	    /* the controller wasn't ready to take the command, forget that we tried to post it */
122865245Smsmith	    sc->amr_mailbox->mb_busy = 0;
122965245Smsmith	    return(EBUSY);
123065245Smsmith	}
123169319Smsmith	debug(3, "posted command");
123251974Smsmith	return(0);
123351974Smsmith    }
123451974Smsmith
123551974Smsmith    /*
123665245Smsmith     * The controller wouldn't take the command.  Return the command as busy
123765245Smsmith     * so that it is retried later.
123851974Smsmith     */
123965245Smsmith    return(EBUSY);
124051974Smsmith}
124151974Smsmith
124251974Smsmith/********************************************************************************
124351974Smsmith * Extract one or more completed commands from the controller (sc)
124451974Smsmith *
124552543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
124651974Smsmith */
124765245Smsmithint
124851974Smsmithamr_done(struct amr_softc *sc)
124951974Smsmith{
125051974Smsmith    struct amr_command	*ac;
125151974Smsmith    struct amr_mailbox	mbox;
125265245Smsmith    int			i, idx, result;
125351974Smsmith
125469319Smsmith    debug_called(3);
125551974Smsmith
125651974Smsmith    /* See if there's anything for us to do */
125751974Smsmith    result = 0;
125851974Smsmith
125958883Smsmith    /* loop collecting completed commands */
126058883Smsmith    for (;;) {
126158883Smsmith	/* poll for a completed command's identifier and status */
126258883Smsmith	if (sc->amr_get_work(sc, &mbox)) {
126358883Smsmith	    result = 1;
126458883Smsmith
126558883Smsmith	    /* iterate over completed commands in this result */
126658883Smsmith	    for (i = 0; i < mbox.mb_nstatus; i++) {
126758883Smsmith		/* get pointer to busy command */
126858883Smsmith		idx = mbox.mb_completed[i] - 1;
126958883Smsmith		ac = sc->amr_busycmd[idx];
127051974Smsmith
127158883Smsmith		/* really a busy command? */
127258883Smsmith		if (ac != NULL) {
127358883Smsmith
127458883Smsmith		    /* pull the command from the busy index */
127558883Smsmith		    sc->amr_busycmd[idx] = NULL;
127665245Smsmith		    sc->amr_busyslots--;
127751974Smsmith
127865245Smsmith		    /* save status for later use */
127965245Smsmith		    ac->ac_status = mbox.mb_status;
128065245Smsmith		    amr_enqueue_completed(ac);
128165245Smsmith		    debug(3, "completed command with status %x", mbox.mb_status);
128265245Smsmith		} else {
128365245Smsmith		    device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
128451974Smsmith		}
128551974Smsmith	    }
128658883Smsmith	} else {
128765245Smsmith	    break;	/* no work */
128851974Smsmith	}
128951974Smsmith    }
129058883Smsmith
129158883Smsmith    /* if we've completed any commands, try posting some more */
129258883Smsmith    if (result)
129358883Smsmith	amr_startio(sc);
129458883Smsmith
129558883Smsmith    /* handle completion and timeouts */
129665245Smsmith#if __FreeBSD_version >= 500005
129765245Smsmith    if (sc->amr_state & AMR_STATE_INTEN)
129865245Smsmith	taskqueue_enqueue(taskqueue_swi, &sc->amr_task_complete);
129965245Smsmith    else
130065245Smsmith#endif
130165245Smsmith	amr_complete(sc, 0);
130258883Smsmith
130351974Smsmith    return(result);
130451974Smsmith}
130551974Smsmith
130651974Smsmith/********************************************************************************
130751974Smsmith * Do completion processing on done commands on (sc)
130851974Smsmith */
130951974Smsmithstatic void
131065245Smsmithamr_complete(void *context, int pending)
131151974Smsmith{
131265245Smsmith    struct amr_softc	*sc = (struct amr_softc *)context;
131365245Smsmith    struct amr_command	*ac;
131451974Smsmith
131569319Smsmith    debug_called(3);
131651974Smsmith
131765245Smsmith    /* pull completed commands off the queue */
131865245Smsmith    for (;;) {
131965245Smsmith	ac = amr_dequeue_completed(sc);
132065245Smsmith	if (ac == NULL)
132165245Smsmith	    break;
132258883Smsmith
132365245Smsmith	/* unmap the command's data buffer */
132465245Smsmith	amr_unmapcmd(ac);
132551974Smsmith
132665245Smsmith	/* unbusy the command */
132765245Smsmith	ac->ac_flags &= ~AMR_CMD_BUSY;
132851974Smsmith
132965245Smsmith	/*
133065245Smsmith	 * Is there a completion handler?
133165245Smsmith	 */
133265245Smsmith	if (ac->ac_complete != NULL) {
133365245Smsmith	    ac->ac_complete(ac);
133465245Smsmith
133551974Smsmith	    /*
133665245Smsmith	     * Is someone sleeping on this one?
133751974Smsmith	     */
133865245Smsmith	} else if (ac->ac_flags & AMR_CMD_SLEEP) {
133965245Smsmith	    wakeup(ac);
134051974Smsmith	}
134151974Smsmith    }
134251974Smsmith}
134351974Smsmith
134451974Smsmith/********************************************************************************
134551974Smsmith ********************************************************************************
134651974Smsmith                                                        Command Buffer Management
134751974Smsmith ********************************************************************************
134851974Smsmith ********************************************************************************/
134951974Smsmith
135051974Smsmith/********************************************************************************
135151974Smsmith * Get a new command buffer.
135251974Smsmith *
135351974Smsmith * This may return NULL in low-memory cases.
135451974Smsmith *
135551974Smsmith * If possible, we recycle a command buffer that's been used before.
135651974Smsmith */
135765245Smsmithstruct amr_command *
135851974Smsmithamr_alloccmd(struct amr_softc *sc)
135951974Smsmith{
136051974Smsmith    struct amr_command	*ac;
136151974Smsmith
136265245Smsmith    debug_called(3);
136351974Smsmith
136465245Smsmith    ac = amr_dequeue_free(sc);
136551974Smsmith    if (ac == NULL) {
136665245Smsmith	amr_alloccmd_cluster(sc);
136765245Smsmith	ac = amr_dequeue_free(sc);
136851974Smsmith    }
136965245Smsmith    if (ac == NULL)
137065245Smsmith	return(NULL);
137165245Smsmith
137265245Smsmith    /* clear out significant fields */
137365245Smsmith    ac->ac_slot = 0;
137465245Smsmith    ac->ac_status = 0;
137551974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
137665245Smsmith    ac->ac_flags = 0;
137765245Smsmith    ac->ac_bio = NULL;
137865245Smsmith    ac->ac_data = NULL;
137965245Smsmith    ac->ac_ccb_data = NULL;
138065245Smsmith    ac->ac_complete = NULL;
138151974Smsmith    return(ac);
138251974Smsmith}
138351974Smsmith
138451974Smsmith/********************************************************************************
138551974Smsmith * Release a command buffer for recycling.
138651974Smsmith */
138765245Smsmithvoid
138851974Smsmithamr_releasecmd(struct amr_command *ac)
138951974Smsmith{
139065245Smsmith    debug_called(3);
139151974Smsmith
139265245Smsmith    amr_enqueue_free(ac);
139351974Smsmith}
139451974Smsmith
139551974Smsmith/********************************************************************************
139665245Smsmith * Allocate a new command cluster and initialise it.
139751974Smsmith */
1398104094Sphkstatic void
139965245Smsmithamr_alloccmd_cluster(struct amr_softc *sc)
140051974Smsmith{
140165245Smsmith    struct amr_command_cluster	*acc;
140265245Smsmith    struct amr_command		*ac;
140365245Smsmith    int				s, i;
140451974Smsmith
140565245Smsmith    acc = malloc(AMR_CMD_CLUSTERSIZE, M_DEVBUF, M_NOWAIT);
140665245Smsmith    if (acc != NULL) {
140765245Smsmith	s = splbio();
140865245Smsmith	TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
140965245Smsmith	splx(s);
141065245Smsmith	for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
141165245Smsmith	    ac = &acc->acc_command[i];
141265245Smsmith	    bzero(ac, sizeof(*ac));
141365245Smsmith	    ac->ac_sc = sc;
141465245Smsmith	    if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) &&
141565245Smsmith		!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap))
141665245Smsmith		amr_releasecmd(ac);
141765245Smsmith	}
141865245Smsmith    }
141951974Smsmith}
142051974Smsmith
142151974Smsmith/********************************************************************************
142265245Smsmith * Free a command cluster
142365245Smsmith */
1424104094Sphkstatic void
142565245Smsmithamr_freecmd_cluster(struct amr_command_cluster *acc)
142665245Smsmith{
142765245Smsmith    struct amr_softc	*sc = acc->acc_command[0].ac_sc;
142865245Smsmith    int			i;
142965245Smsmith
143065245Smsmith    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++)
143165245Smsmith	bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
143265245Smsmith    free(acc, M_DEVBUF);
143365245Smsmith}
143465245Smsmith
143565245Smsmith/********************************************************************************
143651974Smsmith ********************************************************************************
143751974Smsmith                                                         Interface-specific Shims
143851974Smsmith ********************************************************************************
143951974Smsmith ********************************************************************************/
144051974Smsmith
144151974Smsmith/********************************************************************************
144251974Smsmith * Tell the controller that the mailbox contains a valid command
144351974Smsmith */
144465245Smsmithstatic int
144551974Smsmithamr_quartz_submit_command(struct amr_softc *sc)
144651974Smsmith{
144765245Smsmith    debug_called(3);
144851974Smsmith
144965245Smsmith    if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
145065245Smsmith	return(EBUSY);
145151974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
145265245Smsmith    return(0);
145351974Smsmith}
145451974Smsmith
145565245Smsmithstatic int
145651974Smsmithamr_std_submit_command(struct amr_softc *sc)
145751974Smsmith{
145865245Smsmith    debug_called(3);
145951974Smsmith
146065245Smsmith    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
146165245Smsmith	return(EBUSY);
146251974Smsmith    AMR_SPOST_COMMAND(sc);
146365245Smsmith    return(0);
146451974Smsmith}
146551974Smsmith
146651974Smsmith/********************************************************************************
146751974Smsmith * Claim any work that the controller has completed; acknowledge completion,
146851974Smsmith * save details of the completion in (mbsave)
146951974Smsmith */
147051974Smsmithstatic int
147151974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
147251974Smsmith{
147351974Smsmith    int		s, worked;
147451974Smsmith    u_int32_t	outd;
147551974Smsmith
147665245Smsmith    debug_called(3);
147765245Smsmith
147851974Smsmith    worked = 0;
147951974Smsmith    s = splbio();
148051974Smsmith
148151974Smsmith    /* work waiting for us? */
148251974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
148351974Smsmith
148451974Smsmith	/* save mailbox, which contains a list of completed commands */
148565245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
148651974Smsmith
148765245Smsmith	/* acknowledge interrupt */
148865245Smsmith	AMR_QPUT_ODB(sc, AMR_QODB_READY);
148965245Smsmith
149051974Smsmith	/* acknowledge that we have the commands */
149151974Smsmith	AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
149265245Smsmith
149365763Smsmith#ifndef AMR_QUARTZ_GOFASTER
149465245Smsmith	/*
149565245Smsmith	 * This waits for the controller to notice that we've taken the
149665245Smsmith	 * command from it.  It's very inefficient, and we shouldn't do it,
149765245Smsmith	 * but if we remove this code, we stop completing commands under
149865245Smsmith	 * load.
149965245Smsmith	 *
150065245Smsmith	 * Peter J says we shouldn't do this.  The documentation says we
150165245Smsmith	 * should.  Who is right?
150265245Smsmith	 */
150351974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
150451974Smsmith	    ;				/* XXX aiee! what if it dies? */
150565245Smsmith#endif
150665245Smsmith
150751974Smsmith	worked = 1;			/* got some work */
150851974Smsmith    }
150951974Smsmith
151051974Smsmith    splx(s);
151151974Smsmith    return(worked);
151251974Smsmith}
151351974Smsmith
151451974Smsmithstatic int
151551974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
151651974Smsmith{
151751974Smsmith    int		s, worked;
151851974Smsmith    u_int8_t	istat;
151951974Smsmith
152065245Smsmith    debug_called(3);
152151974Smsmith
152251974Smsmith    worked = 0;
152351974Smsmith    s = splbio();
152451974Smsmith
152551974Smsmith    /* check for valid interrupt status */
152651974Smsmith    istat = AMR_SGET_ISTAT(sc);
152751974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
152851974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
152951974Smsmith
153051974Smsmith	/* save mailbox, which contains a list of completed commands */
153165245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
153251974Smsmith
153351974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
153451974Smsmith	worked = 1;
153551974Smsmith    }
153651974Smsmith
153751974Smsmith    splx(s);
153851974Smsmith    return(worked);
153951974Smsmith}
154051974Smsmith
154151974Smsmith/********************************************************************************
154251974Smsmith * Notify the controller of the mailbox location.
154351974Smsmith */
154451974Smsmithstatic void
154551974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
154651974Smsmith{
154751974Smsmith
154851974Smsmith    /* program the mailbox physical address */
154951974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
155051974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
155151974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
155251974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
155351974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
155451974Smsmith
155551974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
155651974Smsmith    AMR_SACK_INTERRUPT(sc);
155751974Smsmith    AMR_SENABLE_INTR(sc);
155851974Smsmith}
155951974Smsmith
156065245Smsmith#ifdef AMR_BOARD_INIT
156151974Smsmith/********************************************************************************
156265245Smsmith * Initialise the controller
156365245Smsmith */
156465245Smsmithstatic int
156565245Smsmithamr_quartz_init(struct amr_softc *sc)
156665245Smsmith{
156765245Smsmith    int		status, ostatus;
156865245Smsmith
156965245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc));
157065245Smsmith
157165245Smsmith    AMR_QRESET(sc);
157265245Smsmith
157365245Smsmith    ostatus = 0xff;
157465245Smsmith    while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) {
157565245Smsmith	if (status != ostatus) {
157665245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status));
157765245Smsmith	    ostatus = status;
157865245Smsmith	}
157965245Smsmith	switch (status) {
158065245Smsmith	case AMR_QINIT_NOMEM:
158165245Smsmith	    return(ENOMEM);
158265245Smsmith
158365245Smsmith	case AMR_QINIT_SCAN:
158465245Smsmith	    /* XXX we could print channel/target here */
158565245Smsmith	    break;
158665245Smsmith	}
158765245Smsmith    }
158865245Smsmith    return(0);
158965245Smsmith}
159065245Smsmith
159165245Smsmithstatic int
159265245Smsmithamr_std_init(struct amr_softc *sc)
159365245Smsmith{
159465245Smsmith    int		status, ostatus;
159565245Smsmith
159665245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc));
159765245Smsmith
159865245Smsmith    AMR_SRESET(sc);
159965245Smsmith
160065245Smsmith    ostatus = 0xff;
160165245Smsmith    while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) {
160265245Smsmith	if (status != ostatus) {
160365245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status));
160465245Smsmith	    ostatus = status;
160565245Smsmith	}
160665245Smsmith	switch (status) {
160765245Smsmith	case AMR_SINIT_NOMEM:
160865245Smsmith	    return(ENOMEM);
160965245Smsmith
161065245Smsmith	case AMR_SINIT_INPROG:
161165245Smsmith	    /* XXX we could print channel/target here? */
161265245Smsmith	    break;
161365245Smsmith	}
161465245Smsmith    }
161565245Smsmith    return(0);
161665245Smsmith}
161765245Smsmith#endif
161865245Smsmith
161965245Smsmith/********************************************************************************
162051974Smsmith ********************************************************************************
162151974Smsmith                                                                        Debugging
162251974Smsmith ********************************************************************************
162351974Smsmith ********************************************************************************/
162451974Smsmith
162551974Smsmith/********************************************************************************
162665245Smsmith * Identify the controller and print some information about it.
162765245Smsmith */
162865245Smsmithstatic void
162965245Smsmithamr_describe_controller(struct amr_softc *sc)
163065245Smsmith{
163165245Smsmith    struct amr_prodinfo	*ap;
163265245Smsmith    struct amr_enquiry	*ae;
163365245Smsmith    char		*prod;
163465245Smsmith
163565245Smsmith    /*
163665245Smsmith     * Try to get 40LD product info, which tells us what the card is labelled as.
163765245Smsmith     */
163865245Smsmith    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) {
1639105419Semoore	device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
164065245Smsmith		      ap->ap_product, ap->ap_firmware, ap->ap_bios,
164165245Smsmith		      ap->ap_memsize);
164265245Smsmith
164365245Smsmith	free(ap, M_DEVBUF);
164465245Smsmith	return;
164565245Smsmith    }
164665245Smsmith
164765245Smsmith    /*
164865245Smsmith     * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
164965245Smsmith     */
165065245Smsmith    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) {
165165245Smsmith	prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
165265245Smsmith
165365245Smsmith    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) {
165465245Smsmith
165565245Smsmith	/*
165665245Smsmith	 * Try to work it out based on the PCI signatures.
165765245Smsmith	 */
165865245Smsmith	switch (pci_get_device(sc->amr_dev)) {
165965245Smsmith	case 0x9010:
166065245Smsmith	    prod = "Series 428";
166165245Smsmith	    break;
166265245Smsmith	case 0x9060:
166365245Smsmith	    prod = "Series 434";
166465245Smsmith	    break;
166565245Smsmith	default:
166665245Smsmith	    prod = "unknown controller";
166765245Smsmith	    break;
166865245Smsmith	}
166965245Smsmith    } else {
167065245Smsmith	prod = "unsupported controller";
167165245Smsmith    }
167274936Shm
167374936Shm    /*
167474936Shm     * HP NetRaid controllers have a special encoding of the firmware and
167574936Shm     * BIOS versions. The AMI version seems to have it as strings whereas
167674936Shm     * the HP version does it with a leading uppercase character and two
167774936Shm     * binary numbers.
167874936Shm     */
167974936Shm
168074936Shm    if(ae->ae_adapter.aa_firmware[2] >= 'A' &&
168174936Shm       ae->ae_adapter.aa_firmware[2] <= 'Z' &&
168274936Shm       ae->ae_adapter.aa_firmware[1] <  ' ' &&
168374936Shm       ae->ae_adapter.aa_firmware[0] <  ' ' &&
168474936Shm       ae->ae_adapter.aa_bios[2] >= 'A'     &&
168574936Shm       ae->ae_adapter.aa_bios[2] <= 'Z'     &&
168674936Shm       ae->ae_adapter.aa_bios[1] <  ' '     &&
168774936Shm       ae->ae_adapter.aa_bios[0] <  ' ') {
168874936Shm
168974936Shm	/* this looks like we have an HP NetRaid version of the MegaRaid */
169074936Shm
169174936Shm    	if(ae->ae_signature == AMR_SIG_438) {
169274936Shm    		/* the AMI 438 is an NetRaid 3si in HP-land */
169374936Shm    		prod = "HP NetRaid 3si";
169474936Shm    	}
169574936Shm
169674936Shm	device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n",
169774936Shm		      prod, ae->ae_adapter.aa_firmware[2],
169874936Shm		      ae->ae_adapter.aa_firmware[1],
169974936Shm		      ae->ae_adapter.aa_firmware[0],
170074936Shm		      ae->ae_adapter.aa_bios[2],
170174936Shm		      ae->ae_adapter.aa_bios[1],
170274936Shm		      ae->ae_adapter.aa_bios[0],
170374936Shm		      ae->ae_adapter.aa_memorysize);
170474936Shm    } else {
170574936Shm	device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n",
170674936Shm		      prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
170774936Shm		      ae->ae_adapter.aa_memorysize);
170874936Shm    }
170965245Smsmith    free(ae, M_DEVBUF);
171065245Smsmith}
171165245Smsmith
171265245Smsmith#ifdef AMR_DEBUG
171365245Smsmith/********************************************************************************
171451974Smsmith * Print the command (ac) in human-readable format
171551974Smsmith */
171651974Smsmithstatic void
171751974Smsmithamr_printcommand(struct amr_command *ac)
171851974Smsmith{
171951974Smsmith    struct amr_softc	*sc = ac->ac_sc;
172051974Smsmith    struct amr_sgentry	*sg;
172151974Smsmith    int			i;
172251974Smsmith
172351974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
172451974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
172551974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
172651974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
172754512Speter    device_printf(sc->amr_dev, "virtaddr %p  length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
172858883Smsmith    device_printf(sc->amr_dev, "sg physaddr %08x  nsg %d\n",
172951974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
173065245Smsmith    device_printf(sc->amr_dev, "ccb %p  bio %p\n", ac->ac_ccb_data, ac->ac_bio);
173151974Smsmith
173251974Smsmith    /* get base address of s/g table */
173351974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
173451974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
173551974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
173651974Smsmith}
173765245Smsmith#endif
1738