151974Smsmith/*-
265245Smsmith * Copyright (c) 1999,2000 Michael Smith
365245Smsmith * Copyright (c) 2000 BSDi
4140687Sscottl * Copyright (c) 2005 Scott Long
551974Smsmith * All rights reserved.
651974Smsmith *
751974Smsmith * Redistribution and use in source and binary forms, with or without
851974Smsmith * modification, are permitted provided that the following conditions
951974Smsmith * are met:
1051974Smsmith * 1. Redistributions of source code must retain the above copyright
1151974Smsmith *    notice, this list of conditions and the following disclaimer.
1251974Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1351974Smsmith *    notice, this list of conditions and the following disclaimer in the
1451974Smsmith *    documentation and/or other materials provided with the distribution.
1551974Smsmith *
1651974Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1751974Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1851974Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1951974Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2051974Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2151974Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2251974Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2351974Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2451974Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2551974Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2651974Smsmith * SUCH DAMAGE.
27119418Sobrien */
28139749Simp/*-
29106225Semoore * Copyright (c) 2002 Eric Moore
30140688Sscottl * Copyright (c) 2002, 2004 LSI Logic Corporation
31106225Semoore * All rights reserved.
32106225Semoore *
33106225Semoore * Redistribution and use in source and binary forms, with or without
34106225Semoore * modification, are permitted provided that the following conditions
35106225Semoore * are met:
36106225Semoore * 1. Redistributions of source code must retain the above copyright
37106225Semoore *    notice, this list of conditions and the following disclaimer.
38106225Semoore * 2. Redistributions in binary form must reproduce the above copyright
39106225Semoore *    notice, this list of conditions and the following disclaimer in the
40106225Semoore *    documentation and/or other materials provided with the distribution.
41105419Semoore * 3. The party using or redistributing the source code and binary forms
42106225Semoore *    agrees to the disclaimer below and the terms and conditions set forth
43105419Semoore *    herein.
44105419Semoore *
45106225Semoore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46106225Semoore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47106225Semoore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48106225Semoore * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49106225Semoore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50106225Semoore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51106225Semoore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52106225Semoore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53106225Semoore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54106225Semoore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55106225Semoore * SUCH DAMAGE.
5651974Smsmith */
5751974Smsmith
58119418Sobrien#include <sys/cdefs.h>
59119418Sobrien__FBSDID("$FreeBSD$");
60119418Sobrien
6151974Smsmith/*
6265245Smsmith * Driver for the AMI MegaRaid family of controllers.
6351974Smsmith */
6451974Smsmith
6551974Smsmith#include <sys/param.h>
6651974Smsmith#include <sys/systm.h>
6751974Smsmith#include <sys/malloc.h>
6851974Smsmith#include <sys/kernel.h>
69153409Sscottl#include <sys/proc.h>
70153409Sscottl#include <sys/sysctl.h>
7151974Smsmith
72148850Sscottl#include <sys/bio.h>
7351974Smsmith#include <sys/bus.h>
7451974Smsmith#include <sys/conf.h>
7565245Smsmith#include <sys/stat.h>
7651974Smsmith
7765245Smsmith#include <machine/bus.h>
78153409Sscottl#include <machine/cpu.h>
7951974Smsmith#include <machine/resource.h>
8051974Smsmith#include <sys/rman.h>
8151974Smsmith
82119277Simp#include <dev/pci/pcireg.h>
83119277Simp#include <dev/pci/pcivar.h>
8465245Smsmith
8551974Smsmith#include <dev/amr/amrio.h>
8651974Smsmith#include <dev/amr/amrreg.h>
8751974Smsmith#include <dev/amr/amrvar.h>
8865245Smsmith#define AMR_DEFINE_TABLES
8965245Smsmith#include <dev/amr/amr_tables.h>
9051974Smsmith
91153409SscottlSYSCTL_NODE(_hw, OID_AUTO, amr, CTLFLAG_RD, 0, "AMR driver parameters");
92153409Sscottl
9365245Smsmithstatic d_open_t         amr_open;
9465245Smsmithstatic d_close_t        amr_close;
9565245Smsmithstatic d_ioctl_t        amr_ioctl;
9665245Smsmith
9751974Smsmithstatic struct cdevsw amr_cdevsw = {
98126080Sphk	.d_version =	D_VERSION,
99126080Sphk	.d_flags =	D_NEEDGIANT,
100111815Sphk	.d_open =	amr_open,
101111815Sphk	.d_close =	amr_close,
102111815Sphk	.d_ioctl =	amr_ioctl,
103111815Sphk	.d_name =	"amr",
10451974Smsmith};
10551974Smsmith
106158267Sambriskoint linux_no_adapter = 0;
10765245Smsmith/*
10865245Smsmith * Initialisation, bus interface.
10965245Smsmith */
11065245Smsmithstatic void	amr_startup(void *arg);
11151974Smsmith
11251974Smsmith/*
11351974Smsmith * Command wrappers
11451974Smsmith */
11565245Smsmithstatic int	amr_query_controller(struct amr_softc *sc);
11665245Smsmithstatic void	*amr_enquiry(struct amr_softc *sc, size_t bufsize,
117153409Sscottl			     u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status);
11865245Smsmithstatic void	amr_completeio(struct amr_command *ac);
119106225Semoorestatic int	amr_support_ext_cdb(struct amr_softc *sc);
12051974Smsmith
12151974Smsmith/*
12265245Smsmith * Command buffer allocation.
12351974Smsmith */
12465245Smsmithstatic void	amr_alloccmd_cluster(struct amr_softc *sc);
12565245Smsmithstatic void	amr_freecmd_cluster(struct amr_command_cluster *acc);
12651974Smsmith
12751974Smsmith/*
12865245Smsmith * Command processing.
12951974Smsmith */
13065245Smsmithstatic int	amr_bio_command(struct amr_softc *sc, struct amr_command **acp);
131138422Sscottlstatic int	amr_wait_command(struct amr_command *ac) __unused;
132138422Sscottlstatic int	amr_mapcmd(struct amr_command *ac);
13365245Smsmithstatic void	amr_unmapcmd(struct amr_command *ac);
13465245Smsmithstatic int	amr_start(struct amr_command *ac);
135175622Sscottlstatic void	amr_complete(void *context, ac_qhead_t *head);
136174544Sscottlstatic void	amr_setup_sg(void *arg, bus_dma_segment_t *segs, int nsegments, int error);
137174544Sscottlstatic void	amr_setup_data(void *arg, bus_dma_segment_t *segs, int nsegments, int error);
138174544Sscottlstatic void	amr_setup_ccb(void *arg, bus_dma_segment_t *segs, int nsegments, int error);
139175622Sscottlstatic void	amr_abort_load(struct amr_command *ac);
14051974Smsmith
14151974Smsmith/*
14251974Smsmith * Interface-specific shims
14351974Smsmith */
144155222Spsstatic int	amr_quartz_submit_command(struct amr_command *ac);
14565245Smsmithstatic int	amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
146107756Semoorestatic int	amr_quartz_poll_command(struct amr_command *ac);
147138422Sscottlstatic int	amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac);
14851974Smsmith
149155222Spsstatic int	amr_std_submit_command(struct amr_command *ac);
15065245Smsmithstatic int	amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
151107756Semoorestatic int	amr_std_poll_command(struct amr_command *ac);
15265245Smsmithstatic void	amr_std_attach_mailbox(struct amr_softc *sc);
15351974Smsmith
15465245Smsmith#ifdef AMR_BOARD_INIT
15565245Smsmithstatic int	amr_quartz_init(struct amr_softc *sc);
15665245Smsmithstatic int	amr_std_init(struct amr_softc *sc);
15765245Smsmith#endif
15865245Smsmith
15951974Smsmith/*
16051974Smsmith * Debugging
16151974Smsmith */
16265245Smsmithstatic void	amr_describe_controller(struct amr_softc *sc);
16365245Smsmith#ifdef AMR_DEBUG
164107756Semoore#if 0
16565245Smsmithstatic void	amr_printcommand(struct amr_command *ac);
16665245Smsmith#endif
167107756Semoore#endif
16851974Smsmith
169153409Sscottlstatic void	amr_init_sysctl(struct amr_softc *sc);
170158267Sambriskostatic int	amr_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t addr,
171192450Simp		    int32_t flag, struct thread *td);
172153409Sscottl
173227293Sedstatic MALLOC_DEFINE(M_AMR, "amr", "AMR memory");
174174194Sscottl
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{
193184573Sscottl    device_t child;
19451974Smsmith
19565245Smsmith    debug_called(1);
19665245Smsmith
19751974Smsmith    /*
19851974Smsmith     * Initialise per-controller queues.
19951974Smsmith     */
200175622Sscottl    amr_init_qhead(&sc->amr_freecmds);
201175622Sscottl    amr_init_qhead(&sc->amr_ready);
20265245Smsmith    TAILQ_INIT(&sc->amr_cmd_clusters);
20359249Sphk    bioq_init(&sc->amr_bioq);
20451974Smsmith
20565245Smsmith    debug(2, "queue init done");
20665245Smsmith
20765245Smsmith    /*
20851974Smsmith     * Configure for this controller type.
20951974Smsmith     */
21065245Smsmith    if (AMR_IS_QUARTZ(sc)) {
21151974Smsmith	sc->amr_submit_command = amr_quartz_submit_command;
21251974Smsmith	sc->amr_get_work       = amr_quartz_get_work;
213107756Semoore	sc->amr_poll_command   = amr_quartz_poll_command;
214138422Sscottl	sc->amr_poll_command1  = amr_quartz_poll_command1;
21551974Smsmith    } else {
21651974Smsmith	sc->amr_submit_command = amr_std_submit_command;
21751974Smsmith	sc->amr_get_work       = amr_std_get_work;
218107756Semoore	sc->amr_poll_command   = amr_std_poll_command;
219201758Smbr	amr_std_attach_mailbox(sc);
22051974Smsmith    }
22151974Smsmith
22265245Smsmith#ifdef AMR_BOARD_INIT
223198546Sbrueffer    if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc)))
22465245Smsmith	return(ENXIO);
22565245Smsmith#endif
22651974Smsmith
22751974Smsmith    /*
228175622Sscottl     * Allocate initial commands.
229175622Sscottl     */
230175622Sscottl    amr_alloccmd_cluster(sc);
231175622Sscottl
232175622Sscottl    /*
23365245Smsmith     * Quiz controller for features and limits.
23451974Smsmith     */
23565245Smsmith    if (amr_query_controller(sc))
23665245Smsmith	return(ENXIO);
23751974Smsmith
23865245Smsmith    debug(2, "controller query complete");
23951974Smsmith
240153409Sscottl    /*
241175622Sscottl     * preallocate the remaining commands.
242175622Sscottl     */
243175622Sscottl    while (sc->amr_nextslot < sc->amr_maxio)
244175622Sscottl	amr_alloccmd_cluster(sc);
245175622Sscottl
246175622Sscottl    /*
247153409Sscottl     * Setup sysctls.
248153409Sscottl     */
249153409Sscottl    amr_init_sysctl(sc);
250153409Sscottl
25151974Smsmith    /*
25265245Smsmith     * Attach our 'real' SCSI channels to CAM.
25351974Smsmith     */
254184573Sscottl    child = device_add_child(sc->amr_dev, "amrp", -1);
255184573Sscottl    sc->amr_pass = child;
256184573Sscottl    if (child != NULL) {
257184573Sscottl	device_set_softc(child, sc);
258184573Sscottl	device_set_desc(child, "SCSI Passthrough Bus");
259184573Sscottl	bus_generic_attach(sc->amr_dev);
260184573Sscottl    }
26151974Smsmith
26251974Smsmith    /*
26365245Smsmith     * Create the control device.
26451974Smsmith     */
26565245Smsmith    sc->amr_dev_t = make_dev(&amr_cdevsw, device_get_unit(sc->amr_dev), UID_ROOT, GID_OPERATOR,
26665245Smsmith			     S_IRUSR | S_IWUSR, "amr%d", device_get_unit(sc->amr_dev));
26765245Smsmith    sc->amr_dev_t->si_drv1 = sc;
268158267Sambrisko    linux_no_adapter++;
269158267Sambrisko    if (device_get_unit(sc->amr_dev) == 0)
270158267Sambrisko	make_dev_alias(sc->amr_dev_t, "megadev0");
27151974Smsmith
27251974Smsmith    /*
27365245Smsmith     * Schedule ourselves to bring the controller up once interrupts are
27465245Smsmith     * available.
27551974Smsmith     */
27665245Smsmith    bzero(&sc->amr_ich, sizeof(struct intr_config_hook));
27765245Smsmith    sc->amr_ich.ich_func = amr_startup;
27865245Smsmith    sc->amr_ich.ich_arg = sc;
27965245Smsmith    if (config_intrhook_establish(&sc->amr_ich) != 0) {
28065245Smsmith	device_printf(sc->amr_dev, "can't establish configuration hook\n");
28165245Smsmith	return(ENOMEM);
28265245Smsmith    }
28351974Smsmith
28458883Smsmith    /*
28565245Smsmith     * Print a little information about the controller.
28658883Smsmith     */
28765245Smsmith    amr_describe_controller(sc);
28858883Smsmith
28965245Smsmith    debug(2, "attach complete");
29051974Smsmith    return(0);
29151974Smsmith}
29251974Smsmith
29351974Smsmith/********************************************************************************
29451974Smsmith * Locate disk resources and attach children to them.
29551974Smsmith */
29665245Smsmithstatic void
29765245Smsmithamr_startup(void *arg)
29851974Smsmith{
29965245Smsmith    struct amr_softc	*sc = (struct amr_softc *)arg;
30051974Smsmith    struct amr_logdrive	*dr;
30151974Smsmith    int			i, error;
30251974Smsmith
30365245Smsmith    debug_called(1);
30451974Smsmith
30565245Smsmith    /* pull ourselves off the intrhook chain */
306153409Sscottl    if (sc->amr_ich.ich_func)
307153409Sscottl	config_intrhook_disestablish(&sc->amr_ich);
308153409Sscottl    sc->amr_ich.ich_func = NULL;
30965245Smsmith
31051974Smsmith    /* get up-to-date drive information */
31151974Smsmith    if (amr_query_controller(sc)) {
31265245Smsmith	device_printf(sc->amr_dev, "can't scan controller for drives\n");
31351974Smsmith	return;
31451974Smsmith    }
31551974Smsmith
31651974Smsmith    /* iterate over available drives */
31751974Smsmith    for (i = 0, dr = &sc->amr_drive[0]; (i < AMR_MAXLD) && (dr->al_size != 0xffffffff); i++, dr++) {
31851974Smsmith	/* are we already attached to this drive? */
31951974Smsmith	if (dr->al_disk == 0) {
32051974Smsmith	    /* generate geometry information */
32151974Smsmith	    if (dr->al_size > 0x200000) {	/* extended translation? */
32251974Smsmith		dr->al_heads = 255;
32351974Smsmith		dr->al_sectors = 63;
32451974Smsmith	    } else {
32551974Smsmith		dr->al_heads = 64;
32651974Smsmith		dr->al_sectors = 32;
32751974Smsmith	    }
32851974Smsmith	    dr->al_cylinders = dr->al_size / (dr->al_heads * dr->al_sectors);
32951974Smsmith
33054073Smdodd	    dr->al_disk = device_add_child(sc->amr_dev, NULL, -1);
33151974Smsmith	    if (dr->al_disk == 0)
33251974Smsmith		device_printf(sc->amr_dev, "device_add_child failed\n");
33354073Smdodd	    device_set_ivars(dr->al_disk, dr);
33451974Smsmith	}
33551974Smsmith    }
33651974Smsmith
33751974Smsmith    if ((error = bus_generic_attach(sc->amr_dev)) != 0)
33851974Smsmith	device_printf(sc->amr_dev, "bus_generic_attach returned %d\n", error);
33951974Smsmith
34051974Smsmith    /* mark controller back up */
34151974Smsmith    sc->amr_state &= ~AMR_STATE_SHUTDOWN;
34251974Smsmith
34351974Smsmith    /* interrupts will be enabled before we do anything more */
34451974Smsmith    sc->amr_state |= AMR_STATE_INTEN;
34551974Smsmith
34665245Smsmith    return;
34751974Smsmith}
34851974Smsmith
349153409Sscottlstatic void
350153409Sscottlamr_init_sysctl(struct amr_softc *sc)
351153409Sscottl{
352153409Sscottl
353153409Sscottl    SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->amr_dev),
354153409Sscottl	SYSCTL_CHILDREN(device_get_sysctl_tree(sc->amr_dev)),
355153409Sscottl	OID_AUTO, "allow_volume_configure", CTLFLAG_RW, &sc->amr_allow_vol_config, 0,
356153409Sscottl	"");
357175622Sscottl    SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->amr_dev),
358175622Sscottl	SYSCTL_CHILDREN(device_get_sysctl_tree(sc->amr_dev)),
359175622Sscottl	OID_AUTO, "nextslot", CTLFLAG_RD, &sc->amr_nextslot, 0,
360175622Sscottl	"");
361175622Sscottl    SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->amr_dev),
362175622Sscottl	SYSCTL_CHILDREN(device_get_sysctl_tree(sc->amr_dev)),
363175622Sscottl	OID_AUTO, "busyslots", CTLFLAG_RD, &sc->amr_busyslots, 0,
364175622Sscottl	"");
365175622Sscottl    SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->amr_dev),
366175622Sscottl	SYSCTL_CHILDREN(device_get_sysctl_tree(sc->amr_dev)),
367175622Sscottl	OID_AUTO, "maxio", CTLFLAG_RD, &sc->amr_maxio, 0,
368175622Sscottl	"");
369153409Sscottl}
370153409Sscottl
371153409Sscottl
37265245Smsmith/*******************************************************************************
37365245Smsmith * Free resources associated with a controller instance
37451974Smsmith */
37565245Smsmithvoid
37665245Smsmithamr_free(struct amr_softc *sc)
37751974Smsmith{
37865245Smsmith    struct amr_command_cluster	*acc;
37951974Smsmith
38065245Smsmith    /* detach from CAM */
381184573Sscottl    if (sc->amr_pass != NULL)
382184573Sscottl	device_delete_child(sc->amr_dev, sc->amr_pass);
38351974Smsmith
38465245Smsmith    /* throw away any command buffers */
38565245Smsmith    while ((acc = TAILQ_FIRST(&sc->amr_cmd_clusters)) != NULL) {
38665245Smsmith	TAILQ_REMOVE(&sc->amr_cmd_clusters, acc, acc_link);
38765245Smsmith	amr_freecmd_cluster(acc);
38851974Smsmith    }
389107756Semoore
390107756Semoore    /* destroy control device */
391130585Sphk    if( sc->amr_dev_t != (struct cdev *)NULL)
392107756Semoore	    destroy_dev(sc->amr_dev_t);
393140340Sscottl
394153409Sscottl    if (mtx_initialized(&sc->amr_hw_lock))
395153409Sscottl	mtx_destroy(&sc->amr_hw_lock);
396153409Sscottl
397153409Sscottl    if (mtx_initialized(&sc->amr_list_lock))
398153409Sscottl	mtx_destroy(&sc->amr_list_lock);
39951974Smsmith}
40051974Smsmith
40151974Smsmith/*******************************************************************************
40265245Smsmith * Receive a bio structure from a child device and queue it on a particular
40351974Smsmith * disk resource, then poke the disk resource to start as much work as it can.
40451974Smsmith */
40551974Smsmithint
40665245Smsmithamr_submit_bio(struct amr_softc *sc, struct bio *bio)
40751974Smsmith{
40865245Smsmith    debug_called(2);
40952543Smsmith
410153409Sscottl    mtx_lock(&sc->amr_list_lock);
41165245Smsmith    amr_enqueue_bio(sc, bio);
41251974Smsmith    amr_startio(sc);
413153409Sscottl    mtx_unlock(&sc->amr_list_lock);
41451974Smsmith    return(0);
41551974Smsmith}
41651974Smsmith
41751974Smsmith/********************************************************************************
41851974Smsmith * Accept an open operation on the control device.
41951974Smsmith */
420104094Sphkstatic int
421192450Simpamr_open(struct cdev *dev, int flags, int fmt, struct thread *td)
42251974Smsmith{
423183397Sed    int			unit = dev2unit(dev);
424196403Sjhb    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
42551974Smsmith
42665245Smsmith    debug_called(1);
42765245Smsmith
42851974Smsmith    sc->amr_state |= AMR_STATE_OPEN;
42951974Smsmith    return(0);
43051974Smsmith}
43151974Smsmith
432153409Sscottl#ifdef LSI
433153409Sscottlstatic int
434153409Sscottlamr_del_ld(struct amr_softc *sc, int drv_no, int status)
435153409Sscottl{
436153409Sscottl
437153409Sscottl    debug_called(1);
438153409Sscottl
439153409Sscottl    sc->amr_state &= ~AMR_STATE_QUEUE_FRZN;
440153409Sscottl    sc->amr_state &= ~AMR_STATE_LD_DELETE;
441153409Sscottl    sc->amr_state |= AMR_STATE_REMAP_LD;
442153409Sscottl    debug(1, "State Set");
443153409Sscottl
444153409Sscottl    if (!status) {
445153409Sscottl	debug(1, "disk begin destroyed %d",drv_no);
446153409Sscottl	if (--amr_disks_registered == 0)
447153409Sscottl	    cdevsw_remove(&amrddisk_cdevsw);
448153409Sscottl	debug(1, "disk begin destroyed success");
449153409Sscottl    }
450153409Sscottl    return 0;
451153409Sscottl}
452153409Sscottl
453153409Sscottlstatic int
454153409Sscottlamr_prepare_ld_delete(struct amr_softc *sc)
455153409Sscottl{
456153409Sscottl
457153409Sscottl    debug_called(1);
458153409Sscottl    if (sc->ld_del_supported == 0)
459153409Sscottl	return(ENOIOCTL);
460153409Sscottl
461153409Sscottl    sc->amr_state |= AMR_STATE_QUEUE_FRZN;
462153409Sscottl    sc->amr_state |= AMR_STATE_LD_DELETE;
463153409Sscottl
464153409Sscottl    /* 5 minutes for the all the commands to be flushed.*/
465153409Sscottl    tsleep((void *)&sc->ld_del_supported, PCATCH | PRIBIO,"delete_logical_drv",hz * 60 * 1);
466153409Sscottl    if ( sc->amr_busyslots )
467153409Sscottl	return(ENOIOCTL);
468153409Sscottl
469153409Sscottl    return 0;
470153409Sscottl}
471153409Sscottl#endif
472153409Sscottl
47351974Smsmith/********************************************************************************
47451974Smsmith * Accept the last close on the control device.
47551974Smsmith */
476104094Sphkstatic int
477192450Simpamr_close(struct cdev *dev, int flags, int fmt, struct thread *td)
47851974Smsmith{
479183397Sed    int			unit = dev2unit(dev);
480196403Sjhb    struct amr_softc	*sc = devclass_get_softc(devclass_find("amr"), unit);
48151974Smsmith
48265245Smsmith    debug_called(1);
48365245Smsmith
48451974Smsmith    sc->amr_state &= ~AMR_STATE_OPEN;
48551974Smsmith    return (0);
48651974Smsmith}
48751974Smsmith
48851974Smsmith/********************************************************************************
48951974Smsmith * Handle controller-specific control operations.
49051974Smsmith */
491153409Sscottlstatic void
492153409Sscottlamr_rescan_drives(struct cdev *dev)
493153409Sscottl{
494153409Sscottl    struct amr_softc	*sc = (struct amr_softc *)dev->si_drv1;
495153409Sscottl    int			i, error = 0;
496153409Sscottl
497153409Sscottl    sc->amr_state |= AMR_STATE_REMAP_LD;
498153409Sscottl    while (sc->amr_busyslots) {
499153409Sscottl	device_printf(sc->amr_dev, "idle controller\n");
500153409Sscottl	amr_done(sc);
501153409Sscottl    }
502153409Sscottl
503153409Sscottl    /* mark ourselves as in-shutdown */
504153409Sscottl    sc->amr_state |= AMR_STATE_SHUTDOWN;
505153409Sscottl
506153409Sscottl    /* flush controller */
507153409Sscottl    device_printf(sc->amr_dev, "flushing cache...");
508153409Sscottl    printf("%s\n", amr_flush(sc) ? "failed" : "done");
509153409Sscottl
510153409Sscottl    /* delete all our child devices */
511153409Sscottl    for(i = 0 ; i < AMR_MAXLD; i++) {
512153409Sscottl	if(sc->amr_drive[i].al_disk != 0) {
513153409Sscottl	    if((error = device_delete_child(sc->amr_dev,
514153409Sscottl		sc->amr_drive[i].al_disk)) != 0)
515153409Sscottl		goto shutdown_out;
516153409Sscottl
517196970Sphk	     sc->amr_drive[i].al_disk = 0;
518153409Sscottl	}
519153409Sscottl    }
520153409Sscottl
521153409Sscottlshutdown_out:
522153409Sscottl    amr_startup(sc);
523153409Sscottl}
524153409Sscottl
525234501Sjhb/*
526234501Sjhb * Bug-for-bug compatibility with Linux!
527234501Sjhb * Some apps will send commands with inlen and outlen set to 0,
528298955Spfg * even though they expect data to be transferred to them from the
529234501Sjhb * card.  Linux accidentally allows this by allocating a 4KB
530234501Sjhb * buffer for the transfer anyways, but it then throws it away
531234501Sjhb * without copying it back to the app.
532234501Sjhb *
533234501Sjhb * The amr(4) firmware relies on this feature.  In fact, it assumes
534234501Sjhb * the buffer is always a power of 2 up to a max of 64k.  There is
535234501Sjhb * also at least one case where it assumes a buffer less than 16k is
536241228Sjhb * greater than 16k.  However, forcing all buffers to a size of 32k
537241228Sjhb * causes stalls in the firmware.  Force each command smaller than
538241228Sjhb * 64k up to the next power of two except that commands between 8k
539241228Sjhb * and 16k are rounded up to 32k instead of 16k.
540234501Sjhb */
541234501Sjhbstatic unsigned long
542234501Sjhbamr_ioctl_buffer_length(unsigned long len)
543234501Sjhb{
544234501Sjhb
545241228Sjhb    if (len <= 4 * 1024)
546241228Sjhb	return (4 * 1024);
547241228Sjhb    if (len <= 8 * 1024)
548241228Sjhb	return (8 * 1024);
549234501Sjhb    if (len <= 32 * 1024)
550234501Sjhb	return (32 * 1024);
551234501Sjhb    if (len <= 64 * 1024)
552234501Sjhb	return (64 * 1024);
553234501Sjhb    return (len);
554234501Sjhb}
555234501Sjhb
556153409Sscottlint
557153409Sscottlamr_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag,
558192450Simp    struct thread *td)
559153409Sscottl{
560153409Sscottl    struct amr_softc		*sc = (struct amr_softc *)dev->si_drv1;
561153409Sscottl    struct amr_command		*ac;
562153409Sscottl    struct amr_mailbox		*mb;
563153409Sscottl    struct amr_linux_ioctl	ali;
564153409Sscottl    void			*dp, *temp;
565153409Sscottl    int				error;
566269711Simp    int				len, ac_flags = 0;
567153409Sscottl    int				logical_drives_changed = 0;
568153409Sscottl    u_int32_t			linux_version = 0x02100000;
569153409Sscottl    u_int8_t			status;
570153409Sscottl    struct amr_passthrough	*ap;	/* 60 bytes */
571153409Sscottl
572153409Sscottl    error = 0;
573153409Sscottl    dp = NULL;
574153409Sscottl    ac = NULL;
575153409Sscottl    ap = NULL;
576153409Sscottl
577154370Sscottl    if ((error = copyin(addr, &ali, sizeof(ali))) != 0)
578154370Sscottl	return (error);
579153409Sscottl    switch (ali.ui.fcs.opcode) {
580153409Sscottl    case 0x82:
581153409Sscottl	switch(ali.ui.fcs.subopcode) {
582153409Sscottl	case 'e':
583153409Sscottl	    copyout(&linux_version, (void *)(uintptr_t)ali.data,
584153409Sscottl		sizeof(linux_version));
585153409Sscottl	    error = 0;
586153409Sscottl	    break;
587153409Sscottl
588153409Sscottl	case 'm':
589158267Sambrisko	    copyout(&linux_no_adapter, (void *)(uintptr_t)ali.data,
590158267Sambrisko		sizeof(linux_no_adapter));
591158267Sambrisko	    td->td_retval[0] = linux_no_adapter;
592153409Sscottl	    error = 0;
593153409Sscottl	    break;
594153409Sscottl
595153409Sscottl	default:
596153409Sscottl	    printf("Unknown subopcode\n");
597153409Sscottl	    error = ENOIOCTL;
598153409Sscottl	    break;
599153409Sscottl	}
600153409Sscottl    break;
601153409Sscottl
602153409Sscottl    case 0x80:
603153409Sscottl    case 0x81:
604153409Sscottl	if (ali.ui.fcs.opcode == 0x80)
605153409Sscottl	    len = max(ali.outlen, ali.inlen);
606153409Sscottl	else
607153409Sscottl	    len = ali.ui.fcs.length;
608153409Sscottl
609153409Sscottl	mb = (void *)&ali.mbox[0];
610153409Sscottl
611153409Sscottl	if ((ali.mbox[0] == FC_DEL_LOGDRV  && ali.mbox[2] == OP_DEL_LOGDRV) ||	/* delete */
612153409Sscottl	    (ali.mbox[0] == AMR_CMD_CONFIG && ali.mbox[2] == 0x0d)) {		/* create */
613153409Sscottl	    if (sc->amr_allow_vol_config == 0) {
614153409Sscottl		error = EPERM;
615153409Sscottl		break;
616153409Sscottl	    }
617153409Sscottl	    logical_drives_changed = 1;
618153409Sscottl	}
619153409Sscottl
620153409Sscottl	if (ali.mbox[0] == AMR_CMD_PASS) {
621174544Sscottl	    mtx_lock(&sc->amr_list_lock);
622174544Sscottl	    while ((ac = amr_alloccmd(sc)) == NULL)
623174544Sscottl		msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz);
624174544Sscottl	    mtx_unlock(&sc->amr_list_lock);
625174544Sscottl	    ap = &ac->ac_ccb->ccb_pthru;
626174544Sscottl
627153409Sscottl	    error = copyin((void *)(uintptr_t)mb->mb_physaddr, ap,
628153409Sscottl		sizeof(struct amr_passthrough));
629153409Sscottl	    if (error)
630153409Sscottl		break;
631153409Sscottl
632153409Sscottl	    if (ap->ap_data_transfer_length)
633174194Sscottl		dp = malloc(ap->ap_data_transfer_length, M_AMR,
634153409Sscottl		    M_WAITOK | M_ZERO);
635153409Sscottl
636153409Sscottl	    if (ali.inlen) {
637153409Sscottl		error = copyin((void *)(uintptr_t)ap->ap_data_transfer_address,
638153409Sscottl		    dp, ap->ap_data_transfer_length);
639153409Sscottl		if (error)
640153409Sscottl		    break;
641153409Sscottl	    }
642153409Sscottl
643174544Sscottl	    ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT|AMR_CMD_CCB;
644153409Sscottl	    bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox));
645153409Sscottl	    ac->ac_mailbox.mb_command = AMR_CMD_PASS;
646153409Sscottl	    ac->ac_flags = ac_flags;
647153409Sscottl
648174544Sscottl	    ac->ac_data = dp;
649174544Sscottl	    ac->ac_length = ap->ap_data_transfer_length;
650153409Sscottl	    temp = (void *)(uintptr_t)ap->ap_data_transfer_address;
651153409Sscottl
652174544Sscottl	    mtx_lock(&sc->amr_list_lock);
653153409Sscottl	    error = amr_wait_command(ac);
654157586Sscottl	    mtx_unlock(&sc->amr_list_lock);
655153409Sscottl	    if (error)
656153409Sscottl		break;
657153409Sscottl
658153409Sscottl	    status = ac->ac_status;
659153409Sscottl	    error = copyout(&status, &((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_scsi_status, sizeof(status));
660153409Sscottl	    if (error)
661153409Sscottl		break;
662153409Sscottl
663153409Sscottl	    if (ali.outlen) {
664153409Sscottl		error = copyout(dp, temp, ap->ap_data_transfer_length);
665153409Sscottl	        if (error)
666153409Sscottl		    break;
667153409Sscottl	    }
668153409Sscottl	    error = copyout(ap->ap_request_sense_area, ((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_request_sense_area, ap->ap_request_sense_length);
669153409Sscottl	    if (error)
670153409Sscottl		break;
671153409Sscottl
672153409Sscottl	    error = 0;
673153409Sscottl	    break;
674153409Sscottl	} else if (ali.mbox[0] == AMR_CMD_PASS_64) {
675153409Sscottl	    printf("No AMR_CMD_PASS_64\n");
676153409Sscottl	    error = ENOIOCTL;
677153409Sscottl	    break;
678153409Sscottl	} else if (ali.mbox[0] == AMR_CMD_EXTPASS) {
679153409Sscottl	    printf("No AMR_CMD_EXTPASS\n");
680153409Sscottl	    error = ENOIOCTL;
681153409Sscottl	    break;
682153409Sscottl	} else {
683234501Sjhb	    len = amr_ioctl_buffer_length(imax(ali.inlen, ali.outlen));
684153409Sscottl
685175622Sscottl	    dp = malloc(len, M_AMR, M_WAITOK | M_ZERO);
686175622Sscottl
687153409Sscottl	    if (ali.inlen) {
688153409Sscottl		error = copyin((void *)(uintptr_t)mb->mb_physaddr, dp, len);
689153409Sscottl		if (error)
690153409Sscottl		    break;
691153409Sscottl	    }
692153409Sscottl
693153409Sscottl	    mtx_lock(&sc->amr_list_lock);
694153409Sscottl	    while ((ac = amr_alloccmd(sc)) == NULL)
695153409Sscottl		msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz);
696153409Sscottl
697153409Sscottl	    ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT;
698153409Sscottl	    bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox));
699153409Sscottl	    bcopy(&ali.mbox[0], &ac->ac_mailbox, sizeof(ali.mbox));
700153409Sscottl
701153409Sscottl	    ac->ac_length = len;
702153409Sscottl	    ac->ac_data = dp;
703153409Sscottl	    ac->ac_flags = ac_flags;
704153409Sscottl
705153409Sscottl	    error = amr_wait_command(ac);
706157586Sscottl	    mtx_unlock(&sc->amr_list_lock);
707153409Sscottl	    if (error)
708153409Sscottl		break;
709153409Sscottl
710153409Sscottl	    status = ac->ac_status;
711153409Sscottl	    error = copyout(&status, &((struct amr_mailbox *)&((struct amr_linux_ioctl *)addr)->mbox[0])->mb_status, sizeof(status));
712153409Sscottl	    if (ali.outlen) {
713234501Sjhb		error = copyout(dp, (void *)(uintptr_t)mb->mb_physaddr, ali.outlen);
714153409Sscottl		if (error)
715153409Sscottl		    break;
716153409Sscottl	    }
717153409Sscottl
718153409Sscottl	    error = 0;
719153409Sscottl	    if (logical_drives_changed)
720153409Sscottl		amr_rescan_drives(dev);
721153409Sscottl	    break;
722153409Sscottl	}
723153409Sscottl	break;
724153409Sscottl
725153409Sscottl    default:
726153409Sscottl	debug(1, "unknown linux ioctl 0x%lx", cmd);
727153409Sscottl	printf("unknown linux ioctl 0x%lx\n", cmd);
728153409Sscottl	error = ENOIOCTL;
729153409Sscottl	break;
730153409Sscottl    }
731153409Sscottl
732153409Sscottl    /*
733153409Sscottl     * At this point, we know that there is a lock held and that these
734153409Sscottl     * objects have been allocated.
735153409Sscottl     */
736153409Sscottl    mtx_lock(&sc->amr_list_lock);
737153409Sscottl    if (ac != NULL)
738153409Sscottl	amr_releasecmd(ac);
739153409Sscottl    mtx_unlock(&sc->amr_list_lock);
740153409Sscottl    if (dp != NULL)
741174194Sscottl	free(dp, M_AMR);
742153409Sscottl    return(error);
743153409Sscottl}
744153409Sscottl
745104094Sphkstatic int
746192450Simpamr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
74751974Smsmith{
74865245Smsmith    struct amr_softc		*sc = (struct amr_softc *)dev->si_drv1;
749133870Sambrisko    union {
750133870Sambrisko	void			*_p;
751133870Sambrisko	struct amr_user_ioctl	*au;
752133870Sambrisko#ifdef AMR_IO_COMMAND32
753133870Sambrisko	struct amr_user_ioctl32	*au32;
754133870Sambrisko#endif
755133870Sambrisko	int			*result;
756133870Sambrisko    } arg;
75765245Smsmith    struct amr_command		*ac;
75865245Smsmith    struct amr_mailbox_ioctl	*mbi;
759133870Sambrisko    void			*dp, *au_buffer;
760234501Sjhb    unsigned long		au_length, real_length;
761133870Sambrisko    unsigned char		*au_cmd;
762269711Simp    int				*au_statusp;
763174544Sscottl    int				error;
764143121Sscottl    struct amr_passthrough	*ap;	/* 60 bytes */
765153409Sscottl    int				logical_drives_changed = 0;
76665245Smsmith
76765245Smsmith    debug_called(1);
76865245Smsmith
769133870Sambrisko    arg._p = (void *)addr;
770133870Sambrisko
771153409Sscottl    error = 0;
772153409Sscottl    dp = NULL;
773153409Sscottl    ac = NULL;
774153409Sscottl    ap = NULL;
775153409Sscottl
77651974Smsmith    switch(cmd) {
77765245Smsmith
77865245Smsmith    case AMR_IO_VERSION:
77965245Smsmith	debug(1, "AMR_IO_VERSION");
780133870Sambrisko	*arg.result = AMR_IO_VERSION_NUMBER;
781133870Sambrisko	return(0);
782133870Sambrisko
783133870Sambrisko#ifdef AMR_IO_COMMAND32
784133870Sambrisko    /*
785133870Sambrisko     * Accept ioctl-s from 32-bit binaries on non-32-bit
786133870Sambrisko     * platforms, such as AMD. LSI's MEGAMGR utility is
787133870Sambrisko     * the only example known today...	-mi
788133870Sambrisko     */
789133870Sambrisko    case AMR_IO_COMMAND32:
790133870Sambrisko	debug(1, "AMR_IO_COMMAND32 0x%x", arg.au32->au_cmd[0]);
791133870Sambrisko	au_cmd = arg.au32->au_cmd;
792133870Sambrisko	au_buffer = (void *)(u_int64_t)arg.au32->au_buffer;
793133870Sambrisko	au_length = arg.au32->au_length;
794133870Sambrisko	au_statusp = &arg.au32->au_status;
79565245Smsmith	break;
796133870Sambrisko#endif
79765245Smsmith
79865245Smsmith    case AMR_IO_COMMAND:
799133870Sambrisko	debug(1, "AMR_IO_COMMAND  0x%x", arg.au->au_cmd[0]);
800133870Sambrisko	au_cmd = arg.au->au_cmd;
801133870Sambrisko	au_buffer = (void *)arg.au->au_buffer;
802133870Sambrisko	au_length = arg.au->au_length;
803133870Sambrisko	au_statusp = &arg.au->au_status;
804133870Sambrisko	break;
80565245Smsmith
806153409Sscottl    case 0xc0046d00:
807153409Sscottl    case 0xc06e6d00:	/* Linux emulation */
808158267Sambrisko	{
809158267Sambrisko	    devclass_t			devclass;
810158267Sambrisko	    struct amr_linux_ioctl	ali;
811158267Sambrisko	    int				adapter, error;
812153409Sscottl
813158267Sambrisko	    devclass = devclass_find("amr");
814158267Sambrisko	    if (devclass == NULL)
815158267Sambrisko		return (ENOENT);
816158267Sambrisko
817158267Sambrisko	    error = copyin(addr, &ali, sizeof(ali));
818158267Sambrisko	    if (error)
819158267Sambrisko		return (error);
820158267Sambrisko	    if (ali.ui.fcs.opcode == 0x82)
821158267Sambrisko		adapter = 0;
822158267Sambrisko	    else
823158267Sambrisko		adapter = (ali.ui.fcs.adapno) ^ 'm' << 8;
824158267Sambrisko
825158267Sambrisko	    sc = devclass_get_softc(devclass, adapter);
826158267Sambrisko	    if (sc == NULL)
827158267Sambrisko		return (ENOENT);
828158267Sambrisko
829175622Sscottl	    return (amr_linux_ioctl_int(sc->amr_dev_t, cmd, addr, 0, td));
830158267Sambrisko	}
831133870Sambrisko    default:
832133870Sambrisko	debug(1, "unknown ioctl 0x%lx", cmd);
833133870Sambrisko	return(ENOIOCTL);
834133870Sambrisko    }
83565245Smsmith
836153409Sscottl    if ((au_cmd[0] == FC_DEL_LOGDRV && au_cmd[1] == OP_DEL_LOGDRV) ||	/* delete */
837153409Sscottl	(au_cmd[0] == AMR_CMD_CONFIG && au_cmd[1] == 0x0d)) {		/* create */
838153409Sscottl	if (sc->amr_allow_vol_config == 0) {
839153409Sscottl	    error = EPERM;
840153409Sscottl	    goto out;
841153409Sscottl	}
842153409Sscottl	logical_drives_changed = 1;
843153409Sscottl#ifdef LSI
844153409Sscottl	if ((error = amr_prepare_ld_delete(sc)) != 0)
845153409Sscottl	    return (error);
846153409Sscottl#endif
847153409Sscottl    }
84865245Smsmith
849133870Sambrisko    /* handle inbound data buffer */
850234501Sjhb    real_length = amr_ioctl_buffer_length(au_length);
851240692Sjhb    dp = malloc(real_length, M_AMR, M_WAITOK|M_ZERO);
852140688Sscottl    if (au_length != 0 && au_cmd[0] != 0x06) {
853140340Sscottl	if ((error = copyin(au_buffer, dp, au_length)) != 0) {
854174194Sscottl	    free(dp, M_AMR);
855140340Sscottl	    return (error);
856140340Sscottl	}
857133870Sambrisko	debug(2, "copyin %ld bytes from %p -> %p", au_length, au_buffer, dp);
858133870Sambrisko    }
85965245Smsmith
860143488Sscottl    /* Allocate this now before the mutex gets held */
861143488Sscottl
862153409Sscottl    mtx_lock(&sc->amr_list_lock);
863153409Sscottl    while ((ac = amr_alloccmd(sc)) == NULL)
864153409Sscottl	msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz);
86565245Smsmith
866133870Sambrisko    /* handle SCSI passthrough command */
867133870Sambrisko    if (au_cmd[0] == AMR_CMD_PASS) {
868140340Sscottl        int len;
86965245Smsmith
870174544Sscottl	ap = &ac->ac_ccb->ccb_pthru;
871174544Sscottl	bzero(ap, sizeof(struct amr_passthrough));
872174544Sscottl
873133870Sambrisko	/* copy cdb */
874140340Sscottl        len = au_cmd[2];
875143121Sscottl	ap->ap_cdb_length = len;
876143121Sscottl	bcopy(au_cmd + 3, ap->ap_cdb, len);
87765245Smsmith
878133870Sambrisko	/* build passthrough */
879143121Sscottl	ap->ap_timeout		= au_cmd[len + 3] & 0x07;
880143121Sscottl	ap->ap_ars		= (au_cmd[len + 3] & 0x08) ? 1 : 0;
881143121Sscottl	ap->ap_islogical		= (au_cmd[len + 3] & 0x80) ? 1 : 0;
882143121Sscottl	ap->ap_logical_drive_no	= au_cmd[len + 4];
883143121Sscottl	ap->ap_channel		= au_cmd[len + 5];
884143121Sscottl	ap->ap_scsi_id 		= au_cmd[len + 6];
885143121Sscottl	ap->ap_request_sense_length	= 14;
886143121Sscottl	ap->ap_data_transfer_length	= au_length;
887133870Sambrisko	/* XXX what about the request-sense area? does the caller want it? */
88865245Smsmith
889133870Sambrisko	/* build command */
890133870Sambrisko	ac->ac_mailbox.mb_command = AMR_CMD_PASS;
891174544Sscottl	ac->ac_flags = AMR_CMD_CCB;
89265245Smsmith
893133870Sambrisko    } else {
894133870Sambrisko	/* direct command to controller */
895133870Sambrisko	mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
896105419Semoore
897133870Sambrisko	/* copy pertinent mailbox items */
898133870Sambrisko	mbi->mb_command = au_cmd[0];
899133870Sambrisko	mbi->mb_channel = au_cmd[1];
900133870Sambrisko	mbi->mb_param = au_cmd[2];
901133870Sambrisko	mbi->mb_pad[0] = au_cmd[3];
902133870Sambrisko	mbi->mb_drive = au_cmd[4];
903174544Sscottl	ac->ac_flags = 0;
90451974Smsmith    }
90551974Smsmith
906174544Sscottl    /* build the command */
907174544Sscottl    ac->ac_data = dp;
908234501Sjhb    ac->ac_length = real_length;
909174544Sscottl    ac->ac_flags |= AMR_CMD_DATAIN|AMR_CMD_DATAOUT;
910153409Sscottl
911133870Sambrisko    /* run the command */
912157586Sscottl    error = amr_wait_command(ac);
913157586Sscottl    mtx_unlock(&sc->amr_list_lock);
914157586Sscottl    if (error)
915133870Sambrisko	goto out;
916133870Sambrisko
917133870Sambrisko    /* copy out data and set status */
918140340Sscottl    if (au_length != 0) {
919133870Sambrisko	error = copyout(dp, au_buffer, au_length);
920140340Sscottl    }
921133870Sambrisko    debug(2, "copyout %ld bytes from %p -> %p", au_length, dp, au_buffer);
922240692Sjhb    debug(2, "%p status 0x%x", dp, ac->ac_status);
923133870Sambrisko    *au_statusp = ac->ac_status;
924133870Sambrisko
925133870Sambriskoout:
926140340Sscottl    /*
927140340Sscottl     * At this point, we know that there is a lock held and that these
928140340Sscottl     * objects have been allocated.
929140340Sscottl     */
930153409Sscottl    mtx_lock(&sc->amr_list_lock);
931143488Sscottl    if (ac != NULL)
932143488Sscottl	amr_releasecmd(ac);
933153409Sscottl    mtx_unlock(&sc->amr_list_lock);
934143488Sscottl    if (dp != NULL)
935174194Sscottl	free(dp, M_AMR);
936153409Sscottl
937153409Sscottl#ifndef LSI
938153409Sscottl    if (logical_drives_changed)
939153409Sscottl	amr_rescan_drives(dev);
940153409Sscottl#endif
941153409Sscottl
94265245Smsmith    return(error);
94351974Smsmith}
94451974Smsmith
94551974Smsmith/********************************************************************************
94651974Smsmith ********************************************************************************
94751974Smsmith                                                                 Command Wrappers
94851974Smsmith ********************************************************************************
94951974Smsmith ********************************************************************************/
95051974Smsmith
95151974Smsmith/********************************************************************************
95251974Smsmith * Interrogate the controller for the operational parameters we require.
95351974Smsmith */
95451974Smsmithstatic int
95551974Smsmithamr_query_controller(struct amr_softc *sc)
95651974Smsmith{
95765245Smsmith    struct amr_enquiry3	*aex;
95865245Smsmith    struct amr_prodinfo	*ap;
95965245Smsmith    struct amr_enquiry	*ae;
96065245Smsmith    int			ldrv;
961153409Sscottl    int			status;
96251974Smsmith
963106225Semoore    /*
964106225Semoore     * Greater than 10 byte cdb support
965106225Semoore     */
966106225Semoore    sc->support_ext_cdb = amr_support_ext_cdb(sc);
967106225Semoore
968106225Semoore    if(sc->support_ext_cdb) {
969106225Semoore	debug(2,"supports extended CDBs.");
970106225Semoore    }
971106225Semoore
97265245Smsmith    /*
97365245Smsmith     * Try to issue an ENQUIRY3 command
97465245Smsmith     */
97565245Smsmith    if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
976153409Sscottl			   AMR_CONFIG_ENQ3_SOLICITED_FULL, &status)) != NULL) {
97751974Smsmith
97865245Smsmith	/*
97965245Smsmith	 * Fetch current state of logical drives.
98065245Smsmith	 */
98165245Smsmith	for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) {
98265245Smsmith	    sc->amr_drive[ldrv].al_size       = aex->ae_drivesize[ldrv];
98365245Smsmith	    sc->amr_drive[ldrv].al_state      = aex->ae_drivestate[ldrv];
98465245Smsmith	    sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[ldrv];
98565245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
98665245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
98751974Smsmith	}
988174194Sscottl	free(aex, M_AMR);
98958883Smsmith
99065245Smsmith	/*
99165245Smsmith	 * Get product info for channel count.
99258883Smsmith	 */
993153409Sscottl	if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) == NULL) {
99465245Smsmith	    device_printf(sc->amr_dev, "can't obtain product data from controller\n");
99565245Smsmith	    return(1);
99665245Smsmith	}
99765245Smsmith	sc->amr_maxdrives = 40;
99865245Smsmith	sc->amr_maxchan = ap->ap_nschan;
99965245Smsmith	sc->amr_maxio = ap->ap_maxio;
100065245Smsmith	sc->amr_type |= AMR_TYPE_40LD;
1001174194Sscottl	free(ap, M_AMR);
100258883Smsmith
1003153409Sscottl	ap = amr_enquiry(sc, 0, FC_DEL_LOGDRV, OP_SUP_DEL_LOGDRV, 0, &status);
1004154156Sscottl	if (ap != NULL)
1005174194Sscottl	    free(ap, M_AMR);
1006153409Sscottl	if (!status) {
1007153409Sscottl	    sc->amr_ld_del_supported = 1;
1008153409Sscottl	    device_printf(sc->amr_dev, "delete logical drives supported by controller\n");
1009153409Sscottl	}
101065245Smsmith    } else {
101165245Smsmith
101265245Smsmith	/* failed, try the 8LD ENQUIRY commands */
1013153409Sscottl	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) == NULL) {
1014153409Sscottl	    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) == NULL) {
101565245Smsmith		device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
101665245Smsmith		return(1);
101765245Smsmith	    }
101865245Smsmith	    ae->ae_signature = 0;
101951974Smsmith	}
102065245Smsmith
102158883Smsmith	/*
102265245Smsmith	 * Fetch current state of logical drives.
102358883Smsmith	 */
102465245Smsmith	for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) {
102565245Smsmith	    sc->amr_drive[ldrv].al_size       = ae->ae_ldrv.al_size[ldrv];
102665245Smsmith	    sc->amr_drive[ldrv].al_state      = ae->ae_ldrv.al_state[ldrv];
102765245Smsmith	    sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv];
102865245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
102965245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
103051974Smsmith	}
103165245Smsmith
103265245Smsmith	sc->amr_maxdrives = 8;
103365245Smsmith	sc->amr_maxchan = ae->ae_adapter.aa_channels;
103465245Smsmith	sc->amr_maxio = ae->ae_adapter.aa_maxio;
1035174194Sscottl	free(ae, M_AMR);
103651974Smsmith    }
103765245Smsmith
103865245Smsmith    /*
103965245Smsmith     * Mark remaining drives as unused.
104065245Smsmith     */
104165245Smsmith    for (; ldrv < AMR_MAXLD; ldrv++)
104265245Smsmith	sc->amr_drive[ldrv].al_size = 0xffffffff;
104365245Smsmith
104465245Smsmith    /*
104565245Smsmith     * Cap the maximum number of outstanding I/Os.  AMI's Linux driver doesn't trust
104665245Smsmith     * the controller's reported value, and lockups have been seen when we do.
104765245Smsmith     */
104865245Smsmith    sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD);
104965245Smsmith
105051974Smsmith    return(0);
105151974Smsmith}
105251974Smsmith
105351974Smsmith/********************************************************************************
105451974Smsmith * Run a generic enquiry-style command.
105551974Smsmith */
105651974Smsmithstatic void *
1057153409Sscottlamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status)
105851974Smsmith{
105951974Smsmith    struct amr_command	*ac;
106051974Smsmith    void		*result;
106151974Smsmith    u_int8_t		*mbox;
106251974Smsmith    int			error;
106351974Smsmith
106465245Smsmith    debug_called(1);
106551974Smsmith
106651974Smsmith    error = 1;
106751974Smsmith    result = NULL;
106851974Smsmith
106951974Smsmith    /* get ourselves a command buffer */
1070153409Sscottl    mtx_lock(&sc->amr_list_lock);
1071153409Sscottl    ac = amr_alloccmd(sc);
1072153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1073153409Sscottl    if (ac == NULL)
107451974Smsmith	goto out;
107551974Smsmith    /* allocate the response structure */
1076174194Sscottl    if ((result = malloc(bufsize, M_AMR, M_ZERO|M_NOWAIT)) == NULL)
107751974Smsmith	goto out;
107865245Smsmith    /* set command flags */
1079138422Sscottl
1080135236Sscottl    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAIN;
108151974Smsmith
108265245Smsmith    /* point the command at our data */
108351974Smsmith    ac->ac_data = result;
108451974Smsmith    ac->ac_length = bufsize;
108551974Smsmith
108651974Smsmith    /* build the command proper */
108751974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
108851974Smsmith    mbox[0] = cmd;
108951974Smsmith    mbox[2] = cmdsub;
109051974Smsmith    mbox[3] = cmdqual;
1091153409Sscottl    *status = 0;
109251974Smsmith
109358883Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
1094107756Semoore    if (sc->amr_poll_command(ac))
109551974Smsmith	goto out;
109651974Smsmith    error = ac->ac_status;
1097153409Sscottl    *status = ac->ac_status;
109851974Smsmith
109951974Smsmith out:
1100153409Sscottl    mtx_lock(&sc->amr_list_lock);
110151974Smsmith    if (ac != NULL)
110251974Smsmith	amr_releasecmd(ac);
1103153409Sscottl    mtx_unlock(&sc->amr_list_lock);
110451974Smsmith    if ((error != 0) && (result != NULL)) {
1105174194Sscottl	free(result, M_AMR);
110651974Smsmith	result = NULL;
110751974Smsmith    }
110851974Smsmith    return(result);
110951974Smsmith}
111051974Smsmith
111151974Smsmith/********************************************************************************
111251974Smsmith * Flush the controller's internal cache, return status.
111351974Smsmith */
111465245Smsmithint
111551974Smsmithamr_flush(struct amr_softc *sc)
111651974Smsmith{
111751974Smsmith    struct amr_command	*ac;
111851974Smsmith    int			error;
111951974Smsmith
112051974Smsmith    /* get ourselves a command buffer */
112151974Smsmith    error = 1;
1122153409Sscottl    mtx_lock(&sc->amr_list_lock);
1123153409Sscottl    ac = amr_alloccmd(sc);
1124153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1125153409Sscottl    if (ac == NULL)
112651974Smsmith	goto out;
112765245Smsmith    /* set command flags */
112851974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
112951974Smsmith
113051974Smsmith    /* build the command proper */
113151974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
113251974Smsmith
113358883Smsmith    /* we have to poll, as the system may be going down or otherwise damaged */
1134107756Semoore    if (sc->amr_poll_command(ac))
113551974Smsmith	goto out;
113651974Smsmith    error = ac->ac_status;
113751974Smsmith
113851974Smsmith out:
1139153409Sscottl    mtx_lock(&sc->amr_list_lock);
114051974Smsmith    if (ac != NULL)
114151974Smsmith	amr_releasecmd(ac);
1142153409Sscottl    mtx_unlock(&sc->amr_list_lock);
114351974Smsmith    return(error);
114451974Smsmith}
114551974Smsmith
114651974Smsmith/********************************************************************************
1147106225Semoore * Detect extented cdb >> greater than 10 byte cdb support
1148106225Semoore * returns '1' means this support exist
1149106225Semoore * returns '0' means this support doesn't exist
1150106225Semoore */
1151106225Semoorestatic int
1152106225Semooreamr_support_ext_cdb(struct amr_softc *sc)
1153106225Semoore{
1154106225Semoore    struct amr_command	*ac;
1155106225Semoore    u_int8_t		*mbox;
1156106225Semoore    int			error;
1157106225Semoore
1158106225Semoore    /* get ourselves a command buffer */
1159106225Semoore    error = 0;
1160153409Sscottl    mtx_lock(&sc->amr_list_lock);
1161153409Sscottl    ac = amr_alloccmd(sc);
1162153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1163153409Sscottl    if (ac == NULL)
1164106225Semoore	goto out;
1165106225Semoore    /* set command flags */
1166106225Semoore    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
1167106225Semoore
1168106225Semoore    /* build the command proper */
1169106225Semoore    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
1170106225Semoore    mbox[0] = 0xA4;
1171106225Semoore    mbox[2] = 0x16;
1172106225Semoore
1173106225Semoore
1174106225Semoore    /* we have to poll, as the system may be going down or otherwise damaged */
1175107756Semoore    if (sc->amr_poll_command(ac))
1176106225Semoore	goto out;
1177106225Semoore    if( ac->ac_status == AMR_STATUS_SUCCESS ) {
1178106225Semoore	    error = 1;
1179106225Semoore    }
1180106225Semoore
1181106225Semooreout:
1182153409Sscottl    mtx_lock(&sc->amr_list_lock);
1183106225Semoore    if (ac != NULL)
1184106225Semoore	amr_releasecmd(ac);
1185153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1186106225Semoore    return(error);
1187106225Semoore}
1188106225Semoore
1189106225Semoore/********************************************************************************
119065245Smsmith * Try to find I/O work for the controller from one or more of the work queues.
119151974Smsmith *
119265245Smsmith * We make the assumption that if the controller is not ready to take a command
119365245Smsmith * at some given time, it will generate an interrupt at some later time when
119465245Smsmith * it is.
119551974Smsmith */
119665245Smsmithvoid
119751974Smsmithamr_startio(struct amr_softc *sc)
119851974Smsmith{
119951974Smsmith    struct amr_command	*ac;
120051974Smsmith
120151974Smsmith    /* spin until something prevents us from doing any work */
120251974Smsmith    for (;;) {
120351974Smsmith
1204138422Sscottl	/* Don't bother to queue commands no bounce buffers are available. */
1205138422Sscottl	if (sc->amr_state & AMR_STATE_QUEUE_FRZN)
1206138422Sscottl	    break;
1207138422Sscottl
120865245Smsmith	/* try to get a ready command */
120965245Smsmith	ac = amr_dequeue_ready(sc);
121051974Smsmith
121165245Smsmith	/* if that failed, build a command from a bio */
121265245Smsmith	if (ac == NULL)
121365245Smsmith	    (void)amr_bio_command(sc, &ac);
121451974Smsmith
121565245Smsmith	/* if that failed, build a command from a ccb */
1216184573Sscottl	if ((ac == NULL) && (sc->amr_cam_command != NULL))
1217184573Sscottl	    sc->amr_cam_command(sc, &ac);
1218105419Semoore
121965245Smsmith	/* if we don't have anything to do, give up */
122065245Smsmith	if (ac == NULL)
122165245Smsmith	    break;
122251974Smsmith
122365245Smsmith	/* try to give the command to the controller; if this fails save it for later and give up */
122465245Smsmith	if (amr_start(ac)) {
122565245Smsmith	    debug(2, "controller busy, command deferred");
122665245Smsmith	    amr_requeue_ready(ac);	/* XXX schedule retry very soon? */
122765245Smsmith	    break;
122851974Smsmith	}
122951974Smsmith    }
123051974Smsmith}
123151974Smsmith
123251974Smsmith/********************************************************************************
123351974Smsmith * Handle completion of an I/O command.
123451974Smsmith */
123551974Smsmithstatic void
123651974Smsmithamr_completeio(struct amr_command *ac)
123751974Smsmith{
1238153409Sscottl    struct amrd_softc		*sc = ac->ac_bio->bio_disk->d_drv1;
1239153409Sscottl    static struct timeval	lastfail;
1240153409Sscottl    static int			curfail;
1241140340Sscottl
124251974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
124365245Smsmith	ac->ac_bio->bio_error = EIO;
124465245Smsmith	ac->ac_bio->bio_flags |= BIO_ERROR;
124551974Smsmith
1246153409Sscottl	if (ppsratecheck(&lastfail, &curfail, 1))
1247153409Sscottl	    device_printf(sc->amrd_dev, "I/O error - 0x%x\n", ac->ac_status);
124865245Smsmith/*	amr_printcommand(ac);*/
124951974Smsmith    }
125065245Smsmith    amrd_intr(ac->ac_bio);
1251153409Sscottl    mtx_lock(&ac->ac_sc->amr_list_lock);
125265245Smsmith    amr_releasecmd(ac);
1253153409Sscottl    mtx_unlock(&ac->ac_sc->amr_list_lock);
125451974Smsmith}
125551974Smsmith
125651974Smsmith/********************************************************************************
125751974Smsmith ********************************************************************************
125851974Smsmith                                                               Command Processing
125951974Smsmith ********************************************************************************
126051974Smsmith ********************************************************************************/
126151974Smsmith
126251974Smsmith/********************************************************************************
126365245Smsmith * Convert a bio off the top of the bio queue into a command.
126465245Smsmith */
126565245Smsmithstatic int
126665245Smsmithamr_bio_command(struct amr_softc *sc, struct amr_command **acp)
126765245Smsmith{
126865245Smsmith    struct amr_command	*ac;
126965245Smsmith    struct amrd_softc	*amrd;
127065245Smsmith    struct bio		*bio;
127165245Smsmith    int			error;
127265245Smsmith    int			blkcount;
127365245Smsmith    int			driveno;
127465245Smsmith    int			cmd;
127565245Smsmith
1276175622Sscottl    ac = NULL;
127765245Smsmith    error = 0;
127865245Smsmith
1279140340Sscottl    /* get a command */
1280140340Sscottl    if ((ac = amr_alloccmd(sc)) == NULL)
1281140340Sscottl	return (ENOMEM);
1282140340Sscottl
128365245Smsmith    /* get a bio to work on */
1284140340Sscottl    if ((bio = amr_dequeue_bio(sc)) == NULL) {
1285140340Sscottl	amr_releasecmd(ac);
1286140340Sscottl	return (0);
1287140340Sscottl    }
128865245Smsmith
128965245Smsmith    /* connect the bio to the command */
129065245Smsmith    ac->ac_complete = amr_completeio;
129165245Smsmith    ac->ac_bio = bio;
129265245Smsmith    ac->ac_data = bio->bio_data;
129365245Smsmith    ac->ac_length = bio->bio_bcount;
1294163834Spjd    cmd = 0;
1295163834Spjd    switch (bio->bio_cmd) {
1296163834Spjd    case BIO_READ:
129765245Smsmith	ac->ac_flags |= AMR_CMD_DATAIN;
1298153409Sscottl	if (AMR_IS_SG64(sc)) {
1299153409Sscottl	    cmd = AMR_CMD_LREAD64;
1300153409Sscottl	    ac->ac_flags |= AMR_CMD_SG64;
1301153409Sscottl	} else
1302153409Sscottl	    cmd = AMR_CMD_LREAD;
1303163834Spjd	break;
1304163834Spjd    case BIO_WRITE:
130565245Smsmith	ac->ac_flags |= AMR_CMD_DATAOUT;
1306153409Sscottl	if (AMR_IS_SG64(sc)) {
1307153409Sscottl	    cmd = AMR_CMD_LWRITE64;
1308153409Sscottl	    ac->ac_flags |= AMR_CMD_SG64;
1309153409Sscottl	} else
1310153409Sscottl	    cmd = AMR_CMD_LWRITE;
1311163834Spjd	break;
1312163834Spjd    case BIO_FLUSH:
1313163834Spjd	ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
1314163834Spjd	cmd = AMR_CMD_FLUSH;
1315163834Spjd	break;
131665245Smsmith    }
1317111441Sphk    amrd = (struct amrd_softc *)bio->bio_disk->d_drv1;
131865245Smsmith    driveno = amrd->amrd_drive - sc->amr_drive;
1319298646Spfg    blkcount = howmany(bio->bio_bcount, AMR_BLKSIZE);
132065245Smsmith
132165245Smsmith    ac->ac_mailbox.mb_command = cmd;
1322296592Simp    if (bio->bio_cmd == BIO_READ || bio->bio_cmd == BIO_WRITE) {
1323163834Spjd	ac->ac_mailbox.mb_blkcount = blkcount;
1324163834Spjd	ac->ac_mailbox.mb_lba = bio->bio_pblkno;
1325163834Spjd	if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size) {
1326163834Spjd	    device_printf(sc->amr_dev,
1327163834Spjd			  "I/O beyond end of unit (%lld,%d > %lu)\n",
1328163834Spjd			  (long long)bio->bio_pblkno, blkcount,
1329163834Spjd			  (u_long)sc->amr_drive[driveno].al_size);
1330163834Spjd	}
1331163834Spjd    }
133265245Smsmith    ac->ac_mailbox.mb_drive = driveno;
1333153409Sscottl    if (sc->amr_state & AMR_STATE_REMAP_LD)
1334153409Sscottl	ac->ac_mailbox.mb_drive |= 0x80;
1335153409Sscottl
133665245Smsmith    /* we fill in the s/g related data when the command is mapped */
133765245Smsmith
1338175622Sscottl
133965245Smsmith    *acp = ac;
134065245Smsmith    return(error);
134165245Smsmith}
134265245Smsmith
134365245Smsmith/********************************************************************************
134451974Smsmith * Take a command, submit it to the controller and sleep until it completes
134551974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
134651974Smsmith */
134751974Smsmithstatic int
134851974Smsmithamr_wait_command(struct amr_command *ac)
134951974Smsmith{
1350148498Sps    int			error = 0;
1351157585Sps    struct amr_softc	*sc = ac->ac_sc;
1352157585Sps
135365245Smsmith    debug_called(1);
135451974Smsmith
135551974Smsmith    ac->ac_complete = NULL;
135665245Smsmith    ac->ac_flags |= AMR_CMD_SLEEP;
1357153409Sscottl    if ((error = amr_start(ac)) != 0) {
135851974Smsmith	return(error);
1359153409Sscottl    }
136051974Smsmith
1361148498Sps    while ((ac->ac_flags & AMR_CMD_BUSY) && (error != EWOULDBLOCK)) {
1362157586Sscottl	error = msleep(ac,&sc->amr_list_lock, PRIBIO, "amrwcmd", 0);
136351974Smsmith    }
1364157585Sps
1365148498Sps    return(error);
136651974Smsmith}
136751974Smsmith
136851974Smsmith/********************************************************************************
136951974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
137051974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
137151974Smsmith */
137251974Smsmithstatic int
1373107756Semooreamr_std_poll_command(struct amr_command *ac)
137451974Smsmith{
137551974Smsmith    struct amr_softc	*sc = ac->ac_sc;
137665245Smsmith    int			error, count;
137751974Smsmith
137865245Smsmith    debug_called(2);
137951974Smsmith
138051974Smsmith    ac->ac_complete = NULL;
138151974Smsmith    if ((error = amr_start(ac)) != 0)
138251974Smsmith	return(error);
138351974Smsmith
138451974Smsmith    count = 0;
138551974Smsmith    do {
138651974Smsmith	/*
138751974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
138851974Smsmith	 * Note that the timeout here is somewhat arbitrary.
138951974Smsmith	 */
139051974Smsmith	amr_done(sc);
139165245Smsmith	DELAY(1000);
139265245Smsmith    } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000));
139365245Smsmith    if (!(ac->ac_flags & AMR_CMD_BUSY)) {
139451974Smsmith	error = 0;
139551974Smsmith    } else {
139665245Smsmith	/* XXX the slot is now marked permanently busy */
139751974Smsmith	error = EIO;
139865245Smsmith	device_printf(sc->amr_dev, "polled command timeout\n");
139951974Smsmith    }
140051974Smsmith    return(error);
140151974Smsmith}
140251974Smsmith
1403138422Sscottlstatic void
1404138422Sscottlamr_setup_polled_dmamap(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
1405138422Sscottl{
1406138422Sscottl    struct amr_command *ac = arg;
1407138422Sscottl    struct amr_softc *sc = ac->ac_sc;
1408174544Sscottl    int mb_channel;
1409138422Sscottl
1410175622Sscottl    if (err) {
1411175622Sscottl	device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
1412175622Sscottl	ac->ac_status = AMR_STATUS_ABORTED;
1413175622Sscottl	return;
1414175622Sscottl    }
1415175622Sscottl
1416174544Sscottl    amr_setup_sg(arg, segs, nsegs, err);
1417153409Sscottl
1418174544Sscottl    /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */
1419174544Sscottl    mb_channel = ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel;
1420174544Sscottl    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG &&
1421174544Sscottl        ((mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG) ||
1422174544Sscottl        (mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)))
1423174544Sscottl	((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param = ac->ac_nsegments;
1424174544Sscottl
1425174544Sscottl    ac->ac_mailbox.mb_nsgelem = ac->ac_nsegments;
1426174544Sscottl    ac->ac_mailbox.mb_physaddr = ac->ac_mb_physaddr;
1427153409Sscottl    if (AC_IS_SG64(ac)) {
1428174544Sscottl	ac->ac_sg64_hi = 0;
1429174544Sscottl	ac->ac_sg64_lo = ac->ac_sgbusaddr;
1430147536Sps    }
1431174544Sscottl
1432138422Sscottl    sc->amr_poll_command1(sc, ac);
1433138422Sscottl}
1434138422Sscottl
143551974Smsmith/********************************************************************************
1436107756Semoore * Take a command, submit it to the controller and busy-wait for it to return.
1437107756Semoore * Returns nonzero on error.  Can be safely called with interrupts enabled.
1438107756Semoore */
1439107756Semoorestatic int
1440107756Semooreamr_quartz_poll_command(struct amr_command *ac)
1441107756Semoore{
1442107756Semoore    struct amr_softc	*sc = ac->ac_sc;
1443152119Sscottl    int			error;
1444107756Semoore
1445107756Semoore    debug_called(2);
1446107756Semoore
1447138422Sscottl    error = 0;
1448138422Sscottl
1449153409Sscottl    if (AC_IS_SG64(ac)) {
1450174544Sscottl	ac->ac_tag = sc->amr_buffer64_dmat;
1451174544Sscottl	ac->ac_datamap = ac->ac_dma64map;
1452153409Sscottl    } else {
1453174544Sscottl	ac->ac_tag = sc->amr_buffer_dmat;
1454174544Sscottl	ac->ac_datamap = ac->ac_dmamap;
1455153409Sscottl    }
1456153409Sscottl
1457107756Semoore    /* now we have a slot, we can map the command (unmapped in amr_complete) */
1458138422Sscottl    if (ac->ac_data != 0) {
1459174544Sscottl	if (bus_dmamap_load(ac->ac_tag, ac->ac_datamap, ac->ac_data,
1460174544Sscottl	    ac->ac_length, amr_setup_polled_dmamap, ac, BUS_DMA_NOWAIT) != 0) {
1461138422Sscottl	    error = 1;
1462138422Sscottl	}
1463138422Sscottl    } else {
1464138422Sscottl	error = amr_quartz_poll_command1(sc, ac);
1465138422Sscottl    }
1466107756Semoore
1467138422Sscottl    return (error);
1468138422Sscottl}
1469107756Semoore
1470138422Sscottlstatic int
1471138422Sscottlamr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac)
1472138422Sscottl{
1473138422Sscottl    int count, error;
1474138422Sscottl
1475153409Sscottl    mtx_lock(&sc->amr_hw_lock);
1476140688Sscottl    if ((sc->amr_state & AMR_STATE_INTEN) == 0) {
1477120988Sps	count=0;
1478120988Sps	while (sc->amr_busyslots) {
1479153409Sscottl	    msleep(sc, &sc->amr_hw_lock, PRIBIO | PCATCH, "amrpoll", hz);
1480120988Sps	    if(count++>10) {
1481120988Sps		break;
1482120988Sps	    }
1483109031Semoore	}
1484109031Semoore
1485120988Sps	if(sc->amr_busyslots) {
1486120988Sps	    device_printf(sc->amr_dev, "adapter is busy\n");
1487153409Sscottl	    mtx_unlock(&sc->amr_hw_lock);
1488153409Sscottl	    if (ac->ac_data != NULL) {
1489174544Sscottl		bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
1490153409Sscottl	    }
1491120988Sps    	    ac->ac_status=0;
1492120988Sps	    return(1);
1493120988Sps	}
1494107756Semoore    }
1495107756Semoore
1496107756Semoore    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
1497107756Semoore
1498107756Semoore    /* clear the poll/ack fields in the mailbox */
1499107756Semoore    sc->amr_mailbox->mb_ident = 0xFE;
1500107756Semoore    sc->amr_mailbox->mb_nstatus = 0xFF;
1501107756Semoore    sc->amr_mailbox->mb_status = 0xFF;
1502107756Semoore    sc->amr_mailbox->mb_poll = 0;
1503107756Semoore    sc->amr_mailbox->mb_ack = 0;
1504109031Semoore    sc->amr_mailbox->mb_busy = 1;
1505107756Semoore
1506107756Semoore    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
1507107756Semoore
1508155222Sps    while(sc->amr_mailbox->mb_nstatus == 0xFF)
1509155222Sps	DELAY(1);
1510155222Sps    while(sc->amr_mailbox->mb_status == 0xFF)
1511155222Sps	DELAY(1);
1512107827Semoore    ac->ac_status=sc->amr_mailbox->mb_status;
1513107827Semoore    error = (ac->ac_status !=AMR_STATUS_SUCCESS) ? 1:0;
1514155222Sps    while(sc->amr_mailbox->mb_poll != 0x77)
1515155222Sps	DELAY(1);
1516107756Semoore    sc->amr_mailbox->mb_poll = 0;
1517107756Semoore    sc->amr_mailbox->mb_ack = 0x77;
1518107756Semoore
1519107756Semoore    /* acknowledge that we have the commands */
1520107756Semoore    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
1521155222Sps    while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
1522155222Sps	DELAY(1);
1523153409Sscottl    mtx_unlock(&sc->amr_hw_lock);
1524107756Semoore
1525107756Semoore    /* unmap the command's data buffer */
1526147536Sps    if (ac->ac_flags & AMR_CMD_DATAIN) {
1527174544Sscottl	bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, BUS_DMASYNC_POSTREAD);
1528147536Sps    }
1529147536Sps    if (ac->ac_flags & AMR_CMD_DATAOUT) {
1530174544Sscottl	bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, BUS_DMASYNC_POSTWRITE);
1531147536Sps    }
1532174544Sscottl    bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
1533107756Semoore
1534107827Semoore    return(error);
1535107756Semoore}
1536107756Semoore
1537153409Sscottlstatic __inline int
1538153409Sscottlamr_freeslot(struct amr_command *ac)
153951974Smsmith{
1540153409Sscottl    struct amr_softc *sc = ac->ac_sc;
1541153409Sscottl    int			slot;
154251974Smsmith
154365245Smsmith    debug_called(3);
154465245Smsmith
1545140340Sscottl    slot = ac->ac_slot;
1546153409Sscottl    if (sc->amr_busycmd[slot] == NULL)
1547153409Sscottl	panic("amr: slot %d not busy?\n", slot);
154865245Smsmith
1549153409Sscottl    sc->amr_busycmd[slot] = NULL;
1550153409Sscottl    atomic_subtract_int(&sc->amr_busyslots, 1);
155151974Smsmith
1552140340Sscottl    return (0);
155351974Smsmith}
155451974Smsmith
155551974Smsmith/********************************************************************************
155665245Smsmith * Map/unmap (ac)'s data in the controller's addressable space as required.
155765245Smsmith *
155865245Smsmith * These functions may be safely called multiple times on a given command.
155951974Smsmith */
156051974Smsmithstatic void
1561174544Sscottlamr_setup_sg(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
156251974Smsmith{
156351974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
156451974Smsmith    struct amr_sgentry	*sg;
1565174544Sscottl    struct amr_sg64entry *sg64;
1566174544Sscottl    int flags, i;
156751974Smsmith
156865245Smsmith    debug_called(3);
156951974Smsmith
1570153409Sscottl    /* get base address of s/g table */
1571153409Sscottl    sg = ac->ac_sg.sg32;
1572174544Sscottl    sg64 = ac->ac_sg.sg64;
157365245Smsmith
1574174544Sscottl    if (AC_IS_SG64(ac)) {
1575174544Sscottl	ac->ac_nsegments = nsegments;
1576174544Sscottl	ac->ac_mb_physaddr = 0xffffffff;
1577174544Sscottl	for (i = 0; i < nsegments; i++, sg64++) {
1578174544Sscottl	    sg64->sg_addr = segs[i].ds_addr;
1579174544Sscottl	    sg64->sg_count = segs[i].ds_len;
1580106225Semoore	}
1581105419Semoore    } else {
1582174544Sscottl	/* decide whether we need to populate the s/g table */
1583106225Semoore	if (nsegments < 2) {
1584174544Sscottl	    ac->ac_nsegments = 0;
1585174544Sscottl	    ac->ac_mb_physaddr = segs[0].ds_addr;
1586106225Semoore	} else {
1587174544Sscottl            ac->ac_nsegments = nsegments;
1588174544Sscottl	    ac->ac_mb_physaddr = ac->ac_sgbusaddr;
1589106225Semoore	    for (i = 0; i < nsegments; i++, sg++) {
1590105419Semoore		sg->sg_addr = segs[i].ds_addr;
1591105419Semoore		sg->sg_count = segs[i].ds_len;
1592106225Semoore	    }
1593105419Semoore	}
1594105419Semoore    }
1595138422Sscottl
1596174544Sscottl    flags = 0;
1597138422Sscottl    if (ac->ac_flags & AMR_CMD_DATAIN)
1598174544Sscottl	flags |= BUS_DMASYNC_PREREAD;
1599138422Sscottl    if (ac->ac_flags & AMR_CMD_DATAOUT)
1600174544Sscottl	flags |= BUS_DMASYNC_PREWRITE;
1601174544Sscottl    bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, flags);
1602138422Sscottl    ac->ac_flags |= AMR_CMD_MAPPED;
160351974Smsmith}
160451974Smsmith
1605153409Sscottlstatic void
1606174544Sscottlamr_setup_data(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
1607153409Sscottl{
1608174544Sscottl    struct amr_command *ac = arg;
1609174544Sscottl    struct amr_softc *sc = ac->ac_sc;
1610174544Sscottl    int mb_channel;
1611153409Sscottl
1612175622Sscottl    if (err) {
1613175622Sscottl	device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
1614175622Sscottl	amr_abort_load(ac);
1615175622Sscottl	return;
1616175622Sscottl    }
1617175622Sscottl
1618174544Sscottl    amr_setup_sg(arg, segs, nsegs, err);
1619153409Sscottl
1620174544Sscottl    /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */
1621174544Sscottl    mb_channel = ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel;
1622174544Sscottl    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG &&
1623174544Sscottl        ((mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG) ||
1624174544Sscottl        (mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)))
1625174544Sscottl	((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param = ac->ac_nsegments;
1626174544Sscottl
1627174544Sscottl    ac->ac_mailbox.mb_nsgelem = ac->ac_nsegments;
1628174544Sscottl    ac->ac_mailbox.mb_physaddr = ac->ac_mb_physaddr;
1629174544Sscottl    if (AC_IS_SG64(ac)) {
1630174544Sscottl	ac->ac_sg64_hi = 0;
1631174544Sscottl	ac->ac_sg64_lo = ac->ac_sgbusaddr;
1632153409Sscottl    }
1633153409Sscottl
1634155222Sps    if (sc->amr_submit_command(ac) == EBUSY) {
1635153409Sscottl	amr_freeslot(ac);
1636153409Sscottl	amr_requeue_ready(ac);
1637153409Sscottl    }
1638153409Sscottl}
1639174544Sscottl
1640160443Sjhbstatic void
1641174544Sscottlamr_setup_ccb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
1642160443Sjhb{
1643174544Sscottl    struct amr_command *ac = arg;
1644174544Sscottl    struct amr_softc *sc = ac->ac_sc;
1645174544Sscottl    struct amr_passthrough *ap = &ac->ac_ccb->ccb_pthru;
1646174544Sscottl    struct amr_ext_passthrough *aep = &ac->ac_ccb->ccb_epthru;
1647160443Sjhb
1648175622Sscottl    if (err) {
1649175622Sscottl	device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
1650175622Sscottl	amr_abort_load(ac);
1651175622Sscottl	return;
1652175622Sscottl    }
1653175622Sscottl
1654174544Sscottl    /* Set up the mailbox portion of the command to point at the ccb */
1655174544Sscottl    ac->ac_mailbox.mb_nsgelem = 0;
1656174544Sscottl    ac->ac_mailbox.mb_physaddr = ac->ac_ccb_busaddr;
1657160443Sjhb
1658174544Sscottl    amr_setup_sg(arg, segs, nsegs, err);
1659174544Sscottl
1660174544Sscottl    switch (ac->ac_mailbox.mb_command) {
1661174544Sscottl    case AMR_CMD_EXTPASS:
1662174544Sscottl	aep->ap_no_sg_elements = ac->ac_nsegments;
1663174544Sscottl	aep->ap_data_transfer_address = ac->ac_mb_physaddr;
1664174544Sscottl        break;
1665174544Sscottl    case AMR_CMD_PASS:
1666174544Sscottl	ap->ap_no_sg_elements = ac->ac_nsegments;
1667174544Sscottl	ap->ap_data_transfer_address = ac->ac_mb_physaddr;
1668174544Sscottl	break;
1669174544Sscottl    default:
1670174544Sscottl	panic("Unknown ccb command");
1671160443Sjhb    }
1672160443Sjhb
1673174544Sscottl    if (sc->amr_submit_command(ac) == EBUSY) {
1674174544Sscottl	amr_freeslot(ac);
1675174544Sscottl	amr_requeue_ready(ac);
1676160443Sjhb    }
1677160443Sjhb}
1678160443Sjhb
1679138422Sscottlstatic int
168051974Smsmithamr_mapcmd(struct amr_command *ac)
168151974Smsmith{
1682153409Sscottl    bus_dmamap_callback_t *cb;
168351974Smsmith    struct amr_softc	*sc = ac->ac_sc;
168451974Smsmith
168569319Smsmith    debug_called(3);
168651974Smsmith
1687153409Sscottl    if (AC_IS_SG64(ac)) {
1688174544Sscottl	ac->ac_tag = sc->amr_buffer64_dmat;
1689174544Sscottl	ac->ac_datamap = ac->ac_dma64map;
1690153409Sscottl    } else {
1691174544Sscottl	ac->ac_tag = sc->amr_buffer_dmat;
1692174544Sscottl	ac->ac_datamap = ac->ac_dmamap;
1693153409Sscottl    }
1694153409Sscottl
1695174544Sscottl    if (ac->ac_flags & AMR_CMD_CCB)
1696174544Sscottl	cb = amr_setup_ccb;
1697174544Sscottl    else
1698174544Sscottl	cb = amr_setup_data;
1699174544Sscottl
170065245Smsmith    /* if the command involves data at all, and hasn't been mapped */
1701138422Sscottl    if ((ac->ac_flags & AMR_CMD_MAPPED) == 0 && (ac->ac_data != NULL)) {
1702160443Sjhb	/* map the data buffers into bus space and build the s/g list */
1703174544Sscottl	if (bus_dmamap_load(ac->ac_tag, ac->ac_datamap, ac->ac_data,
1704174544Sscottl	     ac->ac_length, cb, ac, 0) == EINPROGRESS) {
1705160443Sjhb	    sc->amr_state |= AMR_STATE_QUEUE_FRZN;
1706160443Sjhb	}
1707153409Sscottl   } else {
1708155222Sps    	if (sc->amr_submit_command(ac) == EBUSY) {
1709153409Sscottl	    amr_freeslot(ac);
1710153409Sscottl	    amr_requeue_ready(ac);
1711153409Sscottl	}
1712143121Sscottl   }
1713143121Sscottl
1714138422Sscottl    return (0);
171551974Smsmith}
171651974Smsmith
171751974Smsmithstatic void
171851974Smsmithamr_unmapcmd(struct amr_command *ac)
171951974Smsmith{
1720153409Sscottl    int			flag;
172151974Smsmith
172269319Smsmith    debug_called(3);
172351974Smsmith
172465245Smsmith    /* if the command involved data at all and was mapped */
172565245Smsmith    if (ac->ac_flags & AMR_CMD_MAPPED) {
172651974Smsmith
172765245Smsmith	if (ac->ac_data != NULL) {
1728153409Sscottl
1729153409Sscottl	    flag = 0;
173065245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
1731153409Sscottl		flag |= BUS_DMASYNC_POSTREAD;
173265245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
1733153409Sscottl		flag |= BUS_DMASYNC_POSTWRITE;
1734153409Sscottl
1735174544Sscottl	    bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, flag);
1736174544Sscottl	    bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
173765245Smsmith	}
173865245Smsmith
173965245Smsmith	ac->ac_flags &= ~AMR_CMD_MAPPED;
174051974Smsmith    }
174151974Smsmith}
174251974Smsmith
1743175622Sscottlstatic void
1744175622Sscottlamr_abort_load(struct amr_command *ac)
1745175622Sscottl{
1746175622Sscottl    ac_qhead_t head;
1747175622Sscottl    struct amr_softc *sc = ac->ac_sc;
1748175622Sscottl
1749175622Sscottl    mtx_assert(&sc->amr_list_lock, MA_OWNED);
1750175622Sscottl
1751175622Sscottl    ac->ac_status = AMR_STATUS_ABORTED;
1752175622Sscottl    amr_init_qhead(&head);
1753175622Sscottl    amr_enqueue_completed(ac, &head);
1754175622Sscottl
1755175622Sscottl    mtx_unlock(&sc->amr_list_lock);
1756175622Sscottl    amr_complete(sc, &head);
1757175622Sscottl    mtx_lock(&sc->amr_list_lock);
1758175622Sscottl}
1759175622Sscottl
176051974Smsmith/********************************************************************************
176165245Smsmith * Take a command and give it to the controller, returns 0 if successful, or
176265245Smsmith * EBUSY if the command should be retried later.
176351974Smsmith */
176451974Smsmithstatic int
176551974Smsmithamr_start(struct amr_command *ac)
176651974Smsmith{
1767138422Sscottl    struct amr_softc *sc;
1768138422Sscottl    int error = 0;
1769153409Sscottl    int slot;
1770105419Semoore
177169319Smsmith    debug_called(3);
177251974Smsmith
177365245Smsmith    /* mark command as busy so that polling consumer can tell */
1774138422Sscottl    sc = ac->ac_sc;
177565245Smsmith    ac->ac_flags |= AMR_CMD_BUSY;
177665245Smsmith
177765245Smsmith    /* get a command slot (freed in amr_done) */
1778153409Sscottl    slot = ac->ac_slot;
1779153409Sscottl    if (sc->amr_busycmd[slot] != NULL)
1780153409Sscottl	panic("amr: slot %d busy?\n", slot);
1781153409Sscottl    sc->amr_busycmd[slot] = ac;
1782153409Sscottl    atomic_add_int(&sc->amr_busyslots, 1);
178365245Smsmith
1784138422Sscottl    /* Now we have a slot, we can map the command (unmapped in amr_complete). */
1785138422Sscottl    if ((error = amr_mapcmd(ac)) == ENOMEM) {
1786138422Sscottl	/*
1787298955Spfg	 * Memory resources are short, so free the slot and let this be tried
1788138422Sscottl	 * later.
1789138422Sscottl	 */
1790153409Sscottl	amr_freeslot(ac);
1791138422Sscottl    }
179265245Smsmith
1793138422Sscottl    return (error);
1794138422Sscottl}
1795138422Sscottl
179651974Smsmith/********************************************************************************
179751974Smsmith * Extract one or more completed commands from the controller (sc)
179851974Smsmith *
179952543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
180051974Smsmith */
1801140340Sscottl
180265245Smsmithint
180351974Smsmithamr_done(struct amr_softc *sc)
180451974Smsmith{
1805175622Sscottl    ac_qhead_t		head;
180651974Smsmith    struct amr_command	*ac;
180751974Smsmith    struct amr_mailbox	mbox;
180865245Smsmith    int			i, idx, result;
180951974Smsmith
181069319Smsmith    debug_called(3);
181151974Smsmith
181251974Smsmith    /* See if there's anything for us to do */
181351974Smsmith    result = 0;
1814175622Sscottl    amr_init_qhead(&head);
181551974Smsmith
181658883Smsmith    /* loop collecting completed commands */
181758883Smsmith    for (;;) {
181858883Smsmith	/* poll for a completed command's identifier and status */
181958883Smsmith	if (sc->amr_get_work(sc, &mbox)) {
182058883Smsmith	    result = 1;
182158883Smsmith
182258883Smsmith	    /* iterate over completed commands in this result */
182358883Smsmith	    for (i = 0; i < mbox.mb_nstatus; i++) {
182458883Smsmith		/* get pointer to busy command */
182558883Smsmith		idx = mbox.mb_completed[i] - 1;
182658883Smsmith		ac = sc->amr_busycmd[idx];
182751974Smsmith
182858883Smsmith		/* really a busy command? */
182958883Smsmith		if (ac != NULL) {
183058883Smsmith
183158883Smsmith		    /* pull the command from the busy index */
1832153409Sscottl		    amr_freeslot(ac);
183351974Smsmith
183465245Smsmith		    /* save status for later use */
183565245Smsmith		    ac->ac_status = mbox.mb_status;
1836175622Sscottl		    amr_enqueue_completed(ac, &head);
183765245Smsmith		    debug(3, "completed command with status %x", mbox.mb_status);
183865245Smsmith		} else {
183965245Smsmith		    device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
184051974Smsmith		}
184151974Smsmith	    }
1842153409Sscottl	} else
184365245Smsmith	    break;	/* no work */
184451974Smsmith    }
1845138422Sscottl
184658883Smsmith    /* handle completion and timeouts */
1847175622Sscottl    amr_complete(sc, &head);
1848138422Sscottl
184951974Smsmith    return(result);
185051974Smsmith}
185151974Smsmith
185251974Smsmith/********************************************************************************
185351974Smsmith * Do completion processing on done commands on (sc)
185451974Smsmith */
1855140340Sscottl
185651974Smsmithstatic void
1857175622Sscottlamr_complete(void *context, ac_qhead_t *head)
185851974Smsmith{
185965245Smsmith    struct amr_softc	*sc = (struct amr_softc *)context;
186065245Smsmith    struct amr_command	*ac;
186151974Smsmith
186269319Smsmith    debug_called(3);
186351974Smsmith
186465245Smsmith    /* pull completed commands off the queue */
186565245Smsmith    for (;;) {
1866175622Sscottl	ac = amr_dequeue_completed(sc, head);
186765245Smsmith	if (ac == NULL)
186865245Smsmith	    break;
186958883Smsmith
187065245Smsmith	/* unmap the command's data buffer */
187165245Smsmith	amr_unmapcmd(ac);
187251974Smsmith
187365245Smsmith	/*
187465245Smsmith	 * Is there a completion handler?
187565245Smsmith	 */
187665245Smsmith	if (ac->ac_complete != NULL) {
1877157585Sps	    /* unbusy the command */
1878157585Sps	    ac->ac_flags &= ~AMR_CMD_BUSY;
187965245Smsmith	    ac->ac_complete(ac);
188065245Smsmith
188151974Smsmith	    /*
188265245Smsmith	     * Is someone sleeping on this one?
188351974Smsmith	     */
1884157585Sps	} else {
1885157586Sscottl	    mtx_lock(&sc->amr_list_lock);
1886157585Sps	    ac->ac_flags &= ~AMR_CMD_BUSY;
1887157586Sscottl	    if (ac->ac_flags & AMR_CMD_SLEEP) {
1888157586Sscottl		/* unbusy the command */
1889157586Sscottl		wakeup(ac);
1890157586Sscottl	    }
1891157586Sscottl	    mtx_unlock(&sc->amr_list_lock);
1892157586Sscottl	}
1893109031Semoore
1894109031Semoore	if(!sc->amr_busyslots) {
1895109031Semoore	    wakeup(sc);
1896109031Semoore	}
189751974Smsmith    }
1898140340Sscottl
1899153409Sscottl    mtx_lock(&sc->amr_list_lock);
1900140340Sscottl    sc->amr_state &= ~AMR_STATE_QUEUE_FRZN;
1901140340Sscottl    amr_startio(sc);
1902153409Sscottl    mtx_unlock(&sc->amr_list_lock);
190351974Smsmith}
190451974Smsmith
190551974Smsmith/********************************************************************************
190651974Smsmith ********************************************************************************
190751974Smsmith                                                        Command Buffer Management
190851974Smsmith ********************************************************************************
190951974Smsmith ********************************************************************************/
191051974Smsmith
191151974Smsmith/********************************************************************************
191251974Smsmith * Get a new command buffer.
191351974Smsmith *
191451974Smsmith * This may return NULL in low-memory cases.
191551974Smsmith *
191651974Smsmith * If possible, we recycle a command buffer that's been used before.
191751974Smsmith */
191865245Smsmithstruct amr_command *
191951974Smsmithamr_alloccmd(struct amr_softc *sc)
192051974Smsmith{
192151974Smsmith    struct amr_command	*ac;
192251974Smsmith
192365245Smsmith    debug_called(3);
192451974Smsmith
192565245Smsmith    ac = amr_dequeue_free(sc);
192651974Smsmith    if (ac == NULL) {
1927140340Sscottl	sc->amr_state |= AMR_STATE_QUEUE_FRZN;
192865245Smsmith	return(NULL);
1929140340Sscottl    }
193065245Smsmith
193165245Smsmith    /* clear out significant fields */
193265245Smsmith    ac->ac_status = 0;
193351974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
193465245Smsmith    ac->ac_flags = 0;
193565245Smsmith    ac->ac_bio = NULL;
193665245Smsmith    ac->ac_data = NULL;
193765245Smsmith    ac->ac_complete = NULL;
1938175622Sscottl    ac->ac_retries = 0;
1939174544Sscottl    ac->ac_tag = NULL;
1940174544Sscottl    ac->ac_datamap = NULL;
194151974Smsmith    return(ac);
194251974Smsmith}
194351974Smsmith
194451974Smsmith/********************************************************************************
194551974Smsmith * Release a command buffer for recycling.
194651974Smsmith */
194765245Smsmithvoid
194851974Smsmithamr_releasecmd(struct amr_command *ac)
194951974Smsmith{
195065245Smsmith    debug_called(3);
195151974Smsmith
195265245Smsmith    amr_enqueue_free(ac);
195351974Smsmith}
195451974Smsmith
195551974Smsmith/********************************************************************************
195665245Smsmith * Allocate a new command cluster and initialise it.
195751974Smsmith */
1958104094Sphkstatic void
195965245Smsmithamr_alloccmd_cluster(struct amr_softc *sc)
196051974Smsmith{
196165245Smsmith    struct amr_command_cluster	*acc;
196265245Smsmith    struct amr_command		*ac;
1963152119Sscottl    int				i, nextslot;
196451974Smsmith
1965175622Sscottl    /*
1966175622Sscottl     * If we haven't found the real limit yet, let us have a couple of
1967175622Sscottl     * commands in order to be able to probe.
1968175622Sscottl     */
1969175622Sscottl    if (sc->amr_maxio == 0)
1970175622Sscottl	sc->amr_maxio = 2;
1971175622Sscottl
1972140340Sscottl    if (sc->amr_nextslot > sc->amr_maxio)
1973140340Sscottl	return;
1974174194Sscottl    acc = malloc(AMR_CMD_CLUSTERSIZE, M_AMR, M_NOWAIT | M_ZERO);
197565245Smsmith    if (acc != NULL) {
1976140340Sscottl	nextslot = sc->amr_nextslot;
1977175622Sscottl	mtx_lock(&sc->amr_list_lock);
197865245Smsmith	TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
1979175622Sscottl	mtx_unlock(&sc->amr_list_lock);
198065245Smsmith	for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
198165245Smsmith	    ac = &acc->acc_command[i];
198265245Smsmith	    ac->ac_sc = sc;
1983140340Sscottl	    ac->ac_slot = nextslot;
1984153409Sscottl
1985153409Sscottl	    /*
1986153409Sscottl	     * The SG table for each slot is a fixed size and is assumed to
1987153409Sscottl	     * to hold 64-bit s/g objects when the driver is configured to do
1988153409Sscottl	     * 64-bit DMA.  32-bit DMA commands still use the same table, but
1989153409Sscottl	     * cast down to 32-bit objects.
1990153409Sscottl	     */
1991153409Sscottl	    if (AMR_IS_SG64(sc)) {
1992153409Sscottl		ac->ac_sgbusaddr = sc->amr_sgbusaddr +
1993153409Sscottl		    (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sg64entry));
1994153409Sscottl	        ac->ac_sg.sg64 = sc->amr_sg64table + (ac->ac_slot * AMR_NSEG);
1995153409Sscottl	    } else {
1996153409Sscottl		ac->ac_sgbusaddr = sc->amr_sgbusaddr +
1997153409Sscottl		    (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
1998153409Sscottl	        ac->ac_sg.sg32 = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
1999153409Sscottl	    }
2000153409Sscottl
2001174544Sscottl	    ac->ac_ccb = sc->amr_ccb + ac->ac_slot;
2002174544Sscottl	    ac->ac_ccb_busaddr = sc->amr_ccb_busaddr +
2003174544Sscottl		(ac->ac_slot * sizeof(union amr_ccb));
2004174544Sscottl
2005174544Sscottl	    if (bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap))
2006174544Sscottl		break;
2007174544Sscottl	    if (AMR_IS_SG64(sc) &&
2008174544Sscottl		(bus_dmamap_create(sc->amr_buffer64_dmat, 0,&ac->ac_dma64map)))
2009174544Sscottl		break;
2010153409Sscottl	    amr_releasecmd(ac);
2011140340Sscottl	    if (++nextslot > sc->amr_maxio)
2012140340Sscottl		break;
201365245Smsmith	}
2014140340Sscottl	sc->amr_nextslot = nextslot;
201565245Smsmith    }
201651974Smsmith}
201751974Smsmith
201851974Smsmith/********************************************************************************
201965245Smsmith * Free a command cluster
202065245Smsmith */
2021104094Sphkstatic void
202265245Smsmithamr_freecmd_cluster(struct amr_command_cluster *acc)
202365245Smsmith{
202465245Smsmith    struct amr_softc	*sc = acc->acc_command[0].ac_sc;
202565245Smsmith    int			i;
202665245Smsmith
2027153409Sscottl    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
2028175622Sscottl	if (acc->acc_command[i].ac_sc == NULL)
2029175622Sscottl	    break;
203065245Smsmith	bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
2031153409Sscottl	if (AMR_IS_SG64(sc))
2032153409Sscottl		bus_dmamap_destroy(sc->amr_buffer64_dmat, acc->acc_command[i].ac_dma64map);
2033153409Sscottl    }
2034174194Sscottl    free(acc, M_AMR);
203565245Smsmith}
203665245Smsmith
203765245Smsmith/********************************************************************************
203851974Smsmith ********************************************************************************
203951974Smsmith                                                         Interface-specific Shims
204051974Smsmith ********************************************************************************
204151974Smsmith ********************************************************************************/
204251974Smsmith
204351974Smsmith/********************************************************************************
204451974Smsmith * Tell the controller that the mailbox contains a valid command
204551974Smsmith */
204665245Smsmithstatic int
2047155222Spsamr_quartz_submit_command(struct amr_command *ac)
204851974Smsmith{
2049155222Sps    struct amr_softc	*sc = ac->ac_sc;
2050175622Sscottl    static struct timeval lastfail;
2051175622Sscottl    static int		curfail;
2052155222Sps    int			i = 0;
2053155222Sps
2054155222Sps    mtx_lock(&sc->amr_hw_lock);
2055180633Sscottl    while (sc->amr_mailbox->mb_busy && (i++ < 10)) {
2056155222Sps        DELAY(1);
2057180633Sscottl	/* This is a no-op read that flushes pending mailbox updates */
2058180633Sscottl	AMR_QGET_ODB(sc);
2059180633Sscottl    }
2060155222Sps    if (sc->amr_mailbox->mb_busy) {
2061155222Sps	mtx_unlock(&sc->amr_hw_lock);
2062175622Sscottl	if (ac->ac_retries++ > 1000) {
2063175622Sscottl	    if (ppsratecheck(&lastfail, &curfail, 1))
2064175622Sscottl		device_printf(sc->amr_dev, "Too many retries on command %p.  "
2065175622Sscottl			      "Controller is likely dead\n", ac);
2066175622Sscottl	    ac->ac_retries = 0;
2067175622Sscottl	}
2068155222Sps	return (EBUSY);
2069155222Sps    }
207051974Smsmith
2071155222Sps    /*
2072155222Sps     * Save the slot number so that we can locate this command when complete.
2073155222Sps     * Note that ident = 0 seems to be special, so we don't use it.
2074155222Sps     */
2075155222Sps    ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */
2076155222Sps    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14);
2077155222Sps    sc->amr_mailbox->mb_busy = 1;
2078155222Sps    sc->amr_mailbox->mb_poll = 0;
2079155222Sps    sc->amr_mailbox->mb_ack  = 0;
2080155222Sps    sc->amr_mailbox64->sg64_hi = ac->ac_sg64_hi;
2081155222Sps    sc->amr_mailbox64->sg64_lo = ac->ac_sg64_lo;
2082155222Sps
208351974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
2084155222Sps    mtx_unlock(&sc->amr_hw_lock);
208565245Smsmith    return(0);
208651974Smsmith}
208751974Smsmith
208865245Smsmithstatic int
2089155222Spsamr_std_submit_command(struct amr_command *ac)
209051974Smsmith{
2091155222Sps    struct amr_softc	*sc = ac->ac_sc;
2092175622Sscottl    static struct timeval lastfail;
2093175622Sscottl    static int		curfail;
2094155222Sps
2095155222Sps    mtx_lock(&sc->amr_hw_lock);
2096155222Sps    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG) {
2097155222Sps	mtx_unlock(&sc->amr_hw_lock);
2098175622Sscottl	if (ac->ac_retries++ > 1000) {
2099175622Sscottl	    if (ppsratecheck(&lastfail, &curfail, 1))
2100175622Sscottl		device_printf(sc->amr_dev, "Too many retries on command %p.  "
2101175622Sscottl			      "Controller is likely dead\n", ac);
2102175622Sscottl	    ac->ac_retries = 0;
2103175622Sscottl	}
2104155222Sps	return (EBUSY);
2105155222Sps    }
210651974Smsmith
2107155222Sps    /*
2108155222Sps     * Save the slot number so that we can locate this command when complete.
2109155222Sps     * Note that ident = 0 seems to be special, so we don't use it.
2110155222Sps     */
2111155222Sps    ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */
2112155222Sps    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14);
2113155222Sps    sc->amr_mailbox->mb_busy = 1;
2114155222Sps    sc->amr_mailbox->mb_poll = 0;
2115155222Sps    sc->amr_mailbox->mb_ack  = 0;
2116155222Sps
211751974Smsmith    AMR_SPOST_COMMAND(sc);
2118155222Sps    mtx_unlock(&sc->amr_hw_lock);
211965245Smsmith    return(0);
212051974Smsmith}
212151974Smsmith
212251974Smsmith/********************************************************************************
212351974Smsmith * Claim any work that the controller has completed; acknowledge completion,
212451974Smsmith * save details of the completion in (mbsave)
212551974Smsmith */
212651974Smsmithstatic int
212751974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
212851974Smsmith{
2129153409Sscottl    int		worked, i;
213051974Smsmith    u_int32_t	outd;
2131154876Sambrisko    u_int8_t	nstatus;
2132153409Sscottl    u_int8_t	completed[46];
213351974Smsmith
213465245Smsmith    debug_called(3);
213565245Smsmith
213651974Smsmith    worked = 0;
213751974Smsmith
213851974Smsmith    /* work waiting for us? */
213951974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
214051974Smsmith
2141140340Sscottl	/* acknowledge interrupt */
2142140340Sscottl	AMR_QPUT_ODB(sc, AMR_QODB_READY);
2143140340Sscottl
2144140340Sscottl	while ((nstatus = sc->amr_mailbox->mb_nstatus) == 0xff)
2145155222Sps	    DELAY(1);
2146140340Sscottl	sc->amr_mailbox->mb_nstatus = 0xff;
2147140340Sscottl
2148153409Sscottl	/* wait until fw wrote out all completions */
2149153409Sscottl	for (i = 0; i < nstatus; i++) {
2150153409Sscottl	    while ((completed[i] = sc->amr_mailbox->mb_completed[i]) == 0xff)
2151155222Sps		DELAY(1);
2152153409Sscottl	    sc->amr_mailbox->mb_completed[i] = 0xff;
2153153409Sscottl	}
2154153409Sscottl
2155154876Sambrisko	/* Save information for later processing */
2156154876Sambrisko	mbsave->mb_nstatus = nstatus;
2157154876Sambrisko	mbsave->mb_status = sc->amr_mailbox->mb_status;
2158153409Sscottl	sc->amr_mailbox->mb_status = 0xff;
2159153409Sscottl
2160153409Sscottl	for (i = 0; i < nstatus; i++)
2161153409Sscottl	    mbsave->mb_completed[i] = completed[i];
216251974Smsmith
216351974Smsmith	/* acknowledge that we have the commands */
2164140340Sscottl	AMR_QPUT_IDB(sc, AMR_QIDB_ACK);
216565245Smsmith
2166153409Sscottl#if 0
216765763Smsmith#ifndef AMR_QUARTZ_GOFASTER
216865245Smsmith	/*
216965245Smsmith	 * This waits for the controller to notice that we've taken the
217065245Smsmith	 * command from it.  It's very inefficient, and we shouldn't do it,
217165245Smsmith	 * but if we remove this code, we stop completing commands under
217265245Smsmith	 * load.
217365245Smsmith	 *
217465245Smsmith	 * Peter J says we shouldn't do this.  The documentation says we
217565245Smsmith	 * should.  Who is right?
217665245Smsmith	 */
217751974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
217851974Smsmith	    ;				/* XXX aiee! what if it dies? */
217965245Smsmith#endif
2180153409Sscottl#endif
218165245Smsmith
218251974Smsmith	worked = 1;			/* got some work */
218351974Smsmith    }
218451974Smsmith
218551974Smsmith    return(worked);
218651974Smsmith}
218751974Smsmith
218851974Smsmithstatic int
218951974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
219051974Smsmith{
2191152119Sscottl    int		worked;
219251974Smsmith    u_int8_t	istat;
219351974Smsmith
219465245Smsmith    debug_called(3);
219551974Smsmith
219651974Smsmith    worked = 0;
219751974Smsmith
219851974Smsmith    /* check for valid interrupt status */
219951974Smsmith    istat = AMR_SGET_ISTAT(sc);
220051974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
220151974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
220251974Smsmith
220351974Smsmith	/* save mailbox, which contains a list of completed commands */
220465245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
220551974Smsmith
220651974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
220751974Smsmith	worked = 1;
220851974Smsmith    }
220951974Smsmith
221051974Smsmith    return(worked);
221151974Smsmith}
221251974Smsmith
221351974Smsmith/********************************************************************************
221451974Smsmith * Notify the controller of the mailbox location.
221551974Smsmith */
221651974Smsmithstatic void
221751974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
221851974Smsmith{
221951974Smsmith
222051974Smsmith    /* program the mailbox physical address */
222151974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
222251974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
222351974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
222451974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
222551974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
222651974Smsmith
222751974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
222851974Smsmith    AMR_SACK_INTERRUPT(sc);
222951974Smsmith    AMR_SENABLE_INTR(sc);
223051974Smsmith}
223151974Smsmith
223265245Smsmith#ifdef AMR_BOARD_INIT
223351974Smsmith/********************************************************************************
223465245Smsmith * Initialise the controller
223565245Smsmith */
223665245Smsmithstatic int
223765245Smsmithamr_quartz_init(struct amr_softc *sc)
223865245Smsmith{
223965245Smsmith    int		status, ostatus;
224065245Smsmith
224165245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc));
224265245Smsmith
224365245Smsmith    AMR_QRESET(sc);
224465245Smsmith
224565245Smsmith    ostatus = 0xff;
224665245Smsmith    while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) {
224765245Smsmith	if (status != ostatus) {
224865245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status));
224965245Smsmith	    ostatus = status;
225065245Smsmith	}
225165245Smsmith	switch (status) {
225265245Smsmith	case AMR_QINIT_NOMEM:
225365245Smsmith	    return(ENOMEM);
225465245Smsmith
225565245Smsmith	case AMR_QINIT_SCAN:
225665245Smsmith	    /* XXX we could print channel/target here */
225765245Smsmith	    break;
225865245Smsmith	}
225965245Smsmith    }
226065245Smsmith    return(0);
226165245Smsmith}
226265245Smsmith
226365245Smsmithstatic int
226465245Smsmithamr_std_init(struct amr_softc *sc)
226565245Smsmith{
226665245Smsmith    int		status, ostatus;
226765245Smsmith
226865245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc));
226965245Smsmith
227065245Smsmith    AMR_SRESET(sc);
227165245Smsmith
227265245Smsmith    ostatus = 0xff;
227365245Smsmith    while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) {
227465245Smsmith	if (status != ostatus) {
227565245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status));
227665245Smsmith	    ostatus = status;
227765245Smsmith	}
227865245Smsmith	switch (status) {
227965245Smsmith	case AMR_SINIT_NOMEM:
228065245Smsmith	    return(ENOMEM);
228165245Smsmith
228265245Smsmith	case AMR_SINIT_INPROG:
228365245Smsmith	    /* XXX we could print channel/target here? */
228465245Smsmith	    break;
228565245Smsmith	}
228665245Smsmith    }
228765245Smsmith    return(0);
228865245Smsmith}
228965245Smsmith#endif
229065245Smsmith
229165245Smsmith/********************************************************************************
229251974Smsmith ********************************************************************************
229351974Smsmith                                                                        Debugging
229451974Smsmith ********************************************************************************
229551974Smsmith ********************************************************************************/
229651974Smsmith
229751974Smsmith/********************************************************************************
229865245Smsmith * Identify the controller and print some information about it.
229965245Smsmith */
230065245Smsmithstatic void
230165245Smsmithamr_describe_controller(struct amr_softc *sc)
230265245Smsmith{
230365245Smsmith    struct amr_prodinfo	*ap;
230465245Smsmith    struct amr_enquiry	*ae;
230565245Smsmith    char		*prod;
2306153409Sscottl    int			status;
230765245Smsmith
230865245Smsmith    /*
230965245Smsmith     * Try to get 40LD product info, which tells us what the card is labelled as.
231065245Smsmith     */
2311153409Sscottl    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) != NULL) {
2312105419Semoore	device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
231365245Smsmith		      ap->ap_product, ap->ap_firmware, ap->ap_bios,
231465245Smsmith		      ap->ap_memsize);
231565245Smsmith
2316174194Sscottl	free(ap, M_AMR);
231765245Smsmith	return;
231865245Smsmith    }
231965245Smsmith
232065245Smsmith    /*
232165245Smsmith     * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
232265245Smsmith     */
2323153409Sscottl    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) != NULL) {
232465245Smsmith	prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
232565245Smsmith
2326153409Sscottl    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) != NULL) {
232765245Smsmith
232865245Smsmith	/*
232965245Smsmith	 * Try to work it out based on the PCI signatures.
233065245Smsmith	 */
233165245Smsmith	switch (pci_get_device(sc->amr_dev)) {
233265245Smsmith	case 0x9010:
233365245Smsmith	    prod = "Series 428";
233465245Smsmith	    break;
233565245Smsmith	case 0x9060:
233665245Smsmith	    prod = "Series 434";
233765245Smsmith	    break;
233865245Smsmith	default:
233965245Smsmith	    prod = "unknown controller";
234065245Smsmith	    break;
234165245Smsmith	}
234265245Smsmith    } else {
2343144369Ssam	device_printf(sc->amr_dev, "<unsupported controller>\n");
2344144369Ssam	return;
234565245Smsmith    }
234674936Shm
234774936Shm    /*
234874936Shm     * HP NetRaid controllers have a special encoding of the firmware and
234974936Shm     * BIOS versions. The AMI version seems to have it as strings whereas
235074936Shm     * the HP version does it with a leading uppercase character and two
235174936Shm     * binary numbers.
235274936Shm     */
235374936Shm
235474936Shm    if(ae->ae_adapter.aa_firmware[2] >= 'A' &&
235574936Shm       ae->ae_adapter.aa_firmware[2] <= 'Z' &&
235674936Shm       ae->ae_adapter.aa_firmware[1] <  ' ' &&
235774936Shm       ae->ae_adapter.aa_firmware[0] <  ' ' &&
235874936Shm       ae->ae_adapter.aa_bios[2] >= 'A'     &&
235974936Shm       ae->ae_adapter.aa_bios[2] <= 'Z'     &&
236074936Shm       ae->ae_adapter.aa_bios[1] <  ' '     &&
236174936Shm       ae->ae_adapter.aa_bios[0] <  ' ') {
236274936Shm
236374936Shm	/* this looks like we have an HP NetRaid version of the MegaRaid */
236474936Shm
236574936Shm    	if(ae->ae_signature == AMR_SIG_438) {
2366108470Sschweikh    		/* the AMI 438 is a NetRaid 3si in HP-land */
236774936Shm    		prod = "HP NetRaid 3si";
236874936Shm    	}
236974936Shm
237074936Shm	device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n",
237174936Shm		      prod, ae->ae_adapter.aa_firmware[2],
237274936Shm		      ae->ae_adapter.aa_firmware[1],
237374936Shm		      ae->ae_adapter.aa_firmware[0],
237474936Shm		      ae->ae_adapter.aa_bios[2],
237574936Shm		      ae->ae_adapter.aa_bios[1],
237674936Shm		      ae->ae_adapter.aa_bios[0],
237774936Shm		      ae->ae_adapter.aa_memorysize);
237874936Shm    } else {
237974936Shm	device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n",
238074936Shm		      prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
238174936Shm		      ae->ae_adapter.aa_memorysize);
238274936Shm    }
2383174194Sscottl    free(ae, M_AMR);
238465245Smsmith}
238565245Smsmith
2386120988Spsint
2387120988Spsamr_dump_blocks(struct amr_softc *sc, int unit, u_int32_t lba, void *data, int blks)
2388120988Sps{
2389120988Sps    struct amr_command	*ac;
2390120988Sps    int			error = EIO;
2391120988Sps
2392120988Sps    debug_called(1);
2393120988Sps
2394140688Sscottl    sc->amr_state |= AMR_STATE_INTEN;
2395120988Sps
2396120988Sps    /* get ourselves a command buffer */
2397120988Sps    if ((ac = amr_alloccmd(sc)) == NULL)
2398120988Sps	goto out;
2399120988Sps    /* set command flags */
2400120988Sps    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
2401120988Sps
2402120988Sps    /* point the command at our data */
2403120988Sps    ac->ac_data = data;
2404120988Sps    ac->ac_length = blks * AMR_BLKSIZE;
2405120988Sps
2406120988Sps    /* build the command proper */
2407120988Sps    ac->ac_mailbox.mb_command 	= AMR_CMD_LWRITE;
2408120988Sps    ac->ac_mailbox.mb_blkcount	= blks;
2409120988Sps    ac->ac_mailbox.mb_lba	= lba;
2410120988Sps    ac->ac_mailbox.mb_drive	= unit;
2411120988Sps
2412120988Sps    /* can't assume that interrupts are going to work here, so play it safe */
2413120988Sps    if (sc->amr_poll_command(ac))
2414120988Sps	goto out;
2415120988Sps    error = ac->ac_status;
2416120988Sps
2417120988Sps out:
2418120988Sps    if (ac != NULL)
2419120988Sps	amr_releasecmd(ac);
2420120988Sps
2421140688Sscottl    sc->amr_state &= ~AMR_STATE_INTEN;
2422120988Sps    return (error);
2423120988Sps}
2424120988Sps
2425120988Sps
2426120988Sps
242765245Smsmith#ifdef AMR_DEBUG
242865245Smsmith/********************************************************************************
242951974Smsmith * Print the command (ac) in human-readable format
243051974Smsmith */
2431107756Semoore#if 0
243251974Smsmithstatic void
243351974Smsmithamr_printcommand(struct amr_command *ac)
243451974Smsmith{
243551974Smsmith    struct amr_softc	*sc = ac->ac_sc;
243651974Smsmith    struct amr_sgentry	*sg;
243751974Smsmith    int			i;
243851974Smsmith
243951974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
244051974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
244151974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
244251974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
244354512Speter    device_printf(sc->amr_dev, "virtaddr %p  length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
244458883Smsmith    device_printf(sc->amr_dev, "sg physaddr %08x  nsg %d\n",
244551974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
244665245Smsmith    device_printf(sc->amr_dev, "ccb %p  bio %p\n", ac->ac_ccb_data, ac->ac_bio);
244751974Smsmith
244851974Smsmith    /* get base address of s/g table */
244951974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
245051974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
245151974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
245251974Smsmith}
245365245Smsmith#endif
2454107756Semoore#endif
2455