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: releng/10.3/sys/dev/amr/amr.c 241228 2012-10-05 15:52:31Z jhb $");
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,
528234501Sjhb * even though they expect data to be transfered 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;
566153409Sscottl    int				adapter, 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	adapter = (ali.ui.fcs.adapno) ^ 'm' << 8;
610153409Sscottl
611153409Sscottl	mb = (void *)&ali.mbox[0];
612153409Sscottl
613153409Sscottl	if ((ali.mbox[0] == FC_DEL_LOGDRV  && ali.mbox[2] == OP_DEL_LOGDRV) ||	/* delete */
614153409Sscottl	    (ali.mbox[0] == AMR_CMD_CONFIG && ali.mbox[2] == 0x0d)) {		/* create */
615153409Sscottl	    if (sc->amr_allow_vol_config == 0) {
616153409Sscottl		error = EPERM;
617153409Sscottl		break;
618153409Sscottl	    }
619153409Sscottl	    logical_drives_changed = 1;
620153409Sscottl	}
621153409Sscottl
622153409Sscottl	if (ali.mbox[0] == AMR_CMD_PASS) {
623174544Sscottl	    mtx_lock(&sc->amr_list_lock);
624174544Sscottl	    while ((ac = amr_alloccmd(sc)) == NULL)
625174544Sscottl		msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz);
626174544Sscottl	    mtx_unlock(&sc->amr_list_lock);
627174544Sscottl	    ap = &ac->ac_ccb->ccb_pthru;
628174544Sscottl
629153409Sscottl	    error = copyin((void *)(uintptr_t)mb->mb_physaddr, ap,
630153409Sscottl		sizeof(struct amr_passthrough));
631153409Sscottl	    if (error)
632153409Sscottl		break;
633153409Sscottl
634153409Sscottl	    if (ap->ap_data_transfer_length)
635174194Sscottl		dp = malloc(ap->ap_data_transfer_length, M_AMR,
636153409Sscottl		    M_WAITOK | M_ZERO);
637153409Sscottl
638153409Sscottl	    if (ali.inlen) {
639153409Sscottl		error = copyin((void *)(uintptr_t)ap->ap_data_transfer_address,
640153409Sscottl		    dp, ap->ap_data_transfer_length);
641153409Sscottl		if (error)
642153409Sscottl		    break;
643153409Sscottl	    }
644153409Sscottl
645174544Sscottl	    ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT|AMR_CMD_CCB;
646153409Sscottl	    bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox));
647153409Sscottl	    ac->ac_mailbox.mb_command = AMR_CMD_PASS;
648153409Sscottl	    ac->ac_flags = ac_flags;
649153409Sscottl
650174544Sscottl	    ac->ac_data = dp;
651174544Sscottl	    ac->ac_length = ap->ap_data_transfer_length;
652153409Sscottl	    temp = (void *)(uintptr_t)ap->ap_data_transfer_address;
653153409Sscottl
654174544Sscottl	    mtx_lock(&sc->amr_list_lock);
655153409Sscottl	    error = amr_wait_command(ac);
656157586Sscottl	    mtx_unlock(&sc->amr_list_lock);
657153409Sscottl	    if (error)
658153409Sscottl		break;
659153409Sscottl
660153409Sscottl	    status = ac->ac_status;
661153409Sscottl	    error = copyout(&status, &((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_scsi_status, sizeof(status));
662153409Sscottl	    if (error)
663153409Sscottl		break;
664153409Sscottl
665153409Sscottl	    if (ali.outlen) {
666153409Sscottl		error = copyout(dp, temp, ap->ap_data_transfer_length);
667153409Sscottl	        if (error)
668153409Sscottl		    break;
669153409Sscottl	    }
670153409Sscottl	    error = copyout(ap->ap_request_sense_area, ((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_request_sense_area, ap->ap_request_sense_length);
671153409Sscottl	    if (error)
672153409Sscottl		break;
673153409Sscottl
674153409Sscottl	    error = 0;
675153409Sscottl	    break;
676153409Sscottl	} else if (ali.mbox[0] == AMR_CMD_PASS_64) {
677153409Sscottl	    printf("No AMR_CMD_PASS_64\n");
678153409Sscottl	    error = ENOIOCTL;
679153409Sscottl	    break;
680153409Sscottl	} else if (ali.mbox[0] == AMR_CMD_EXTPASS) {
681153409Sscottl	    printf("No AMR_CMD_EXTPASS\n");
682153409Sscottl	    error = ENOIOCTL;
683153409Sscottl	    break;
684153409Sscottl	} else {
685234501Sjhb	    len = amr_ioctl_buffer_length(imax(ali.inlen, ali.outlen));
686153409Sscottl
687175622Sscottl	    dp = malloc(len, M_AMR, M_WAITOK | M_ZERO);
688175622Sscottl
689153409Sscottl	    if (ali.inlen) {
690153409Sscottl		error = copyin((void *)(uintptr_t)mb->mb_physaddr, dp, len);
691153409Sscottl		if (error)
692153409Sscottl		    break;
693153409Sscottl	    }
694153409Sscottl
695153409Sscottl	    mtx_lock(&sc->amr_list_lock);
696153409Sscottl	    while ((ac = amr_alloccmd(sc)) == NULL)
697153409Sscottl		msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz);
698153409Sscottl
699153409Sscottl	    ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT;
700153409Sscottl	    bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox));
701153409Sscottl	    bcopy(&ali.mbox[0], &ac->ac_mailbox, sizeof(ali.mbox));
702153409Sscottl
703153409Sscottl	    ac->ac_length = len;
704153409Sscottl	    ac->ac_data = dp;
705153409Sscottl	    ac->ac_flags = ac_flags;
706153409Sscottl
707153409Sscottl	    error = amr_wait_command(ac);
708157586Sscottl	    mtx_unlock(&sc->amr_list_lock);
709153409Sscottl	    if (error)
710153409Sscottl		break;
711153409Sscottl
712153409Sscottl	    status = ac->ac_status;
713153409Sscottl	    error = copyout(&status, &((struct amr_mailbox *)&((struct amr_linux_ioctl *)addr)->mbox[0])->mb_status, sizeof(status));
714153409Sscottl	    if (ali.outlen) {
715234501Sjhb		error = copyout(dp, (void *)(uintptr_t)mb->mb_physaddr, ali.outlen);
716153409Sscottl		if (error)
717153409Sscottl		    break;
718153409Sscottl	    }
719153409Sscottl
720153409Sscottl	    error = 0;
721153409Sscottl	    if (logical_drives_changed)
722153409Sscottl		amr_rescan_drives(dev);
723153409Sscottl	    break;
724153409Sscottl	}
725153409Sscottl	break;
726153409Sscottl
727153409Sscottl    default:
728153409Sscottl	debug(1, "unknown linux ioctl 0x%lx", cmd);
729153409Sscottl	printf("unknown linux ioctl 0x%lx\n", cmd);
730153409Sscottl	error = ENOIOCTL;
731153409Sscottl	break;
732153409Sscottl    }
733153409Sscottl
734153409Sscottl    /*
735153409Sscottl     * At this point, we know that there is a lock held and that these
736153409Sscottl     * objects have been allocated.
737153409Sscottl     */
738153409Sscottl    mtx_lock(&sc->amr_list_lock);
739153409Sscottl    if (ac != NULL)
740153409Sscottl	amr_releasecmd(ac);
741153409Sscottl    mtx_unlock(&sc->amr_list_lock);
742153409Sscottl    if (dp != NULL)
743174194Sscottl	free(dp, M_AMR);
744153409Sscottl    return(error);
745153409Sscottl}
746153409Sscottl
747104094Sphkstatic int
748192450Simpamr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
74951974Smsmith{
75065245Smsmith    struct amr_softc		*sc = (struct amr_softc *)dev->si_drv1;
751133870Sambrisko    union {
752133870Sambrisko	void			*_p;
753133870Sambrisko	struct amr_user_ioctl	*au;
754133870Sambrisko#ifdef AMR_IO_COMMAND32
755133870Sambrisko	struct amr_user_ioctl32	*au32;
756133870Sambrisko#endif
757133870Sambrisko	int			*result;
758133870Sambrisko    } arg;
75965245Smsmith    struct amr_command		*ac;
76065245Smsmith    struct amr_mailbox_ioctl	*mbi;
761133870Sambrisko    void			*dp, *au_buffer;
762234501Sjhb    unsigned long		au_length, real_length;
763133870Sambrisko    unsigned char		*au_cmd;
764133870Sambrisko    int				*au_statusp, au_direction;
765174544Sscottl    int				error;
766143121Sscottl    struct amr_passthrough	*ap;	/* 60 bytes */
767153409Sscottl    int				logical_drives_changed = 0;
76865245Smsmith
76965245Smsmith    debug_called(1);
77065245Smsmith
771133870Sambrisko    arg._p = (void *)addr;
772133870Sambrisko
773153409Sscottl    error = 0;
774153409Sscottl    dp = NULL;
775153409Sscottl    ac = NULL;
776153409Sscottl    ap = NULL;
777153409Sscottl
77851974Smsmith    switch(cmd) {
77965245Smsmith
78065245Smsmith    case AMR_IO_VERSION:
78165245Smsmith	debug(1, "AMR_IO_VERSION");
782133870Sambrisko	*arg.result = AMR_IO_VERSION_NUMBER;
783133870Sambrisko	return(0);
784133870Sambrisko
785133870Sambrisko#ifdef AMR_IO_COMMAND32
786133870Sambrisko    /*
787133870Sambrisko     * Accept ioctl-s from 32-bit binaries on non-32-bit
788133870Sambrisko     * platforms, such as AMD. LSI's MEGAMGR utility is
789133870Sambrisko     * the only example known today...	-mi
790133870Sambrisko     */
791133870Sambrisko    case AMR_IO_COMMAND32:
792133870Sambrisko	debug(1, "AMR_IO_COMMAND32 0x%x", arg.au32->au_cmd[0]);
793133870Sambrisko	au_cmd = arg.au32->au_cmd;
794133870Sambrisko	au_buffer = (void *)(u_int64_t)arg.au32->au_buffer;
795133870Sambrisko	au_length = arg.au32->au_length;
796133870Sambrisko	au_direction = arg.au32->au_direction;
797133870Sambrisko	au_statusp = &arg.au32->au_status;
79865245Smsmith	break;
799133870Sambrisko#endif
80065245Smsmith
80165245Smsmith    case AMR_IO_COMMAND:
802133870Sambrisko	debug(1, "AMR_IO_COMMAND  0x%x", arg.au->au_cmd[0]);
803133870Sambrisko	au_cmd = arg.au->au_cmd;
804133870Sambrisko	au_buffer = (void *)arg.au->au_buffer;
805133870Sambrisko	au_length = arg.au->au_length;
806133870Sambrisko	au_direction = arg.au->au_direction;
807133870Sambrisko	au_statusp = &arg.au->au_status;
808133870Sambrisko	break;
80965245Smsmith
810153409Sscottl    case 0xc0046d00:
811153409Sscottl    case 0xc06e6d00:	/* Linux emulation */
812158267Sambrisko	{
813158267Sambrisko	    devclass_t			devclass;
814158267Sambrisko	    struct amr_linux_ioctl	ali;
815158267Sambrisko	    int				adapter, error;
816153409Sscottl
817158267Sambrisko	    devclass = devclass_find("amr");
818158267Sambrisko	    if (devclass == NULL)
819158267Sambrisko		return (ENOENT);
820158267Sambrisko
821158267Sambrisko	    error = copyin(addr, &ali, sizeof(ali));
822158267Sambrisko	    if (error)
823158267Sambrisko		return (error);
824158267Sambrisko	    if (ali.ui.fcs.opcode == 0x82)
825158267Sambrisko		adapter = 0;
826158267Sambrisko	    else
827158267Sambrisko		adapter = (ali.ui.fcs.adapno) ^ 'm' << 8;
828158267Sambrisko
829158267Sambrisko	    sc = devclass_get_softc(devclass, adapter);
830158267Sambrisko	    if (sc == NULL)
831158267Sambrisko		return (ENOENT);
832158267Sambrisko
833175622Sscottl	    return (amr_linux_ioctl_int(sc->amr_dev_t, cmd, addr, 0, td));
834158267Sambrisko	}
835133870Sambrisko    default:
836133870Sambrisko	debug(1, "unknown ioctl 0x%lx", cmd);
837133870Sambrisko	return(ENOIOCTL);
838133870Sambrisko    }
83965245Smsmith
840153409Sscottl    if ((au_cmd[0] == FC_DEL_LOGDRV && au_cmd[1] == OP_DEL_LOGDRV) ||	/* delete */
841153409Sscottl	(au_cmd[0] == AMR_CMD_CONFIG && au_cmd[1] == 0x0d)) {		/* create */
842153409Sscottl	if (sc->amr_allow_vol_config == 0) {
843153409Sscottl	    error = EPERM;
844153409Sscottl	    goto out;
845153409Sscottl	}
846153409Sscottl	logical_drives_changed = 1;
847153409Sscottl#ifdef LSI
848153409Sscottl	if ((error = amr_prepare_ld_delete(sc)) != 0)
849153409Sscottl	    return (error);
850153409Sscottl#endif
851153409Sscottl    }
85265245Smsmith
853133870Sambrisko    /* handle inbound data buffer */
854234501Sjhb    real_length = amr_ioctl_buffer_length(au_length);
855240692Sjhb    dp = malloc(real_length, M_AMR, M_WAITOK|M_ZERO);
856140688Sscottl    if (au_length != 0 && au_cmd[0] != 0x06) {
857140340Sscottl	if ((error = copyin(au_buffer, dp, au_length)) != 0) {
858174194Sscottl	    free(dp, M_AMR);
859140340Sscottl	    return (error);
860140340Sscottl	}
861133870Sambrisko	debug(2, "copyin %ld bytes from %p -> %p", au_length, au_buffer, dp);
862133870Sambrisko    }
86365245Smsmith
864143488Sscottl    /* Allocate this now before the mutex gets held */
865143488Sscottl
866153409Sscottl    mtx_lock(&sc->amr_list_lock);
867153409Sscottl    while ((ac = amr_alloccmd(sc)) == NULL)
868153409Sscottl	msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz);
86965245Smsmith
870133870Sambrisko    /* handle SCSI passthrough command */
871133870Sambrisko    if (au_cmd[0] == AMR_CMD_PASS) {
872140340Sscottl        int len;
87365245Smsmith
874174544Sscottl	ap = &ac->ac_ccb->ccb_pthru;
875174544Sscottl	bzero(ap, sizeof(struct amr_passthrough));
876174544Sscottl
877133870Sambrisko	/* copy cdb */
878140340Sscottl        len = au_cmd[2];
879143121Sscottl	ap->ap_cdb_length = len;
880143121Sscottl	bcopy(au_cmd + 3, ap->ap_cdb, len);
88165245Smsmith
882133870Sambrisko	/* build passthrough */
883143121Sscottl	ap->ap_timeout		= au_cmd[len + 3] & 0x07;
884143121Sscottl	ap->ap_ars		= (au_cmd[len + 3] & 0x08) ? 1 : 0;
885143121Sscottl	ap->ap_islogical		= (au_cmd[len + 3] & 0x80) ? 1 : 0;
886143121Sscottl	ap->ap_logical_drive_no	= au_cmd[len + 4];
887143121Sscottl	ap->ap_channel		= au_cmd[len + 5];
888143121Sscottl	ap->ap_scsi_id 		= au_cmd[len + 6];
889143121Sscottl	ap->ap_request_sense_length	= 14;
890143121Sscottl	ap->ap_data_transfer_length	= au_length;
891133870Sambrisko	/* XXX what about the request-sense area? does the caller want it? */
89265245Smsmith
893133870Sambrisko	/* build command */
894133870Sambrisko	ac->ac_mailbox.mb_command = AMR_CMD_PASS;
895174544Sscottl	ac->ac_flags = AMR_CMD_CCB;
89665245Smsmith
897133870Sambrisko    } else {
898133870Sambrisko	/* direct command to controller */
899133870Sambrisko	mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
900105419Semoore
901133870Sambrisko	/* copy pertinent mailbox items */
902133870Sambrisko	mbi->mb_command = au_cmd[0];
903133870Sambrisko	mbi->mb_channel = au_cmd[1];
904133870Sambrisko	mbi->mb_param = au_cmd[2];
905133870Sambrisko	mbi->mb_pad[0] = au_cmd[3];
906133870Sambrisko	mbi->mb_drive = au_cmd[4];
907174544Sscottl	ac->ac_flags = 0;
90851974Smsmith    }
90951974Smsmith
910174544Sscottl    /* build the command */
911174544Sscottl    ac->ac_data = dp;
912234501Sjhb    ac->ac_length = real_length;
913174544Sscottl    ac->ac_flags |= AMR_CMD_DATAIN|AMR_CMD_DATAOUT;
914153409Sscottl
915133870Sambrisko    /* run the command */
916157586Sscottl    error = amr_wait_command(ac);
917157586Sscottl    mtx_unlock(&sc->amr_list_lock);
918157586Sscottl    if (error)
919133870Sambrisko	goto out;
920133870Sambrisko
921133870Sambrisko    /* copy out data and set status */
922140340Sscottl    if (au_length != 0) {
923133870Sambrisko	error = copyout(dp, au_buffer, au_length);
924140340Sscottl    }
925133870Sambrisko    debug(2, "copyout %ld bytes from %p -> %p", au_length, dp, au_buffer);
926240692Sjhb    debug(2, "%p status 0x%x", dp, ac->ac_status);
927133870Sambrisko    *au_statusp = ac->ac_status;
928133870Sambrisko
929133870Sambriskoout:
930140340Sscottl    /*
931140340Sscottl     * At this point, we know that there is a lock held and that these
932140340Sscottl     * objects have been allocated.
933140340Sscottl     */
934153409Sscottl    mtx_lock(&sc->amr_list_lock);
935143488Sscottl    if (ac != NULL)
936143488Sscottl	amr_releasecmd(ac);
937153409Sscottl    mtx_unlock(&sc->amr_list_lock);
938143488Sscottl    if (dp != NULL)
939174194Sscottl	free(dp, M_AMR);
940153409Sscottl
941153409Sscottl#ifndef LSI
942153409Sscottl    if (logical_drives_changed)
943153409Sscottl	amr_rescan_drives(dev);
944153409Sscottl#endif
945153409Sscottl
94665245Smsmith    return(error);
94751974Smsmith}
94851974Smsmith
94951974Smsmith/********************************************************************************
95051974Smsmith ********************************************************************************
95151974Smsmith                                                                 Command Wrappers
95251974Smsmith ********************************************************************************
95351974Smsmith ********************************************************************************/
95451974Smsmith
95551974Smsmith/********************************************************************************
95651974Smsmith * Interrogate the controller for the operational parameters we require.
95751974Smsmith */
95851974Smsmithstatic int
95951974Smsmithamr_query_controller(struct amr_softc *sc)
96051974Smsmith{
96165245Smsmith    struct amr_enquiry3	*aex;
96265245Smsmith    struct amr_prodinfo	*ap;
96365245Smsmith    struct amr_enquiry	*ae;
96465245Smsmith    int			ldrv;
965153409Sscottl    int			status;
96651974Smsmith
967106225Semoore    /*
968106225Semoore     * Greater than 10 byte cdb support
969106225Semoore     */
970106225Semoore    sc->support_ext_cdb = amr_support_ext_cdb(sc);
971106225Semoore
972106225Semoore    if(sc->support_ext_cdb) {
973106225Semoore	debug(2,"supports extended CDBs.");
974106225Semoore    }
975106225Semoore
97665245Smsmith    /*
97765245Smsmith     * Try to issue an ENQUIRY3 command
97865245Smsmith     */
97965245Smsmith    if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3,
980153409Sscottl			   AMR_CONFIG_ENQ3_SOLICITED_FULL, &status)) != NULL) {
98151974Smsmith
98265245Smsmith	/*
98365245Smsmith	 * Fetch current state of logical drives.
98465245Smsmith	 */
98565245Smsmith	for (ldrv = 0; ldrv < aex->ae_numldrives; ldrv++) {
98665245Smsmith	    sc->amr_drive[ldrv].al_size       = aex->ae_drivesize[ldrv];
98765245Smsmith	    sc->amr_drive[ldrv].al_state      = aex->ae_drivestate[ldrv];
98865245Smsmith	    sc->amr_drive[ldrv].al_properties = aex->ae_driveprop[ldrv];
98965245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
99065245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
99151974Smsmith	}
992174194Sscottl	free(aex, M_AMR);
99358883Smsmith
99465245Smsmith	/*
99565245Smsmith	 * Get product info for channel count.
99658883Smsmith	 */
997153409Sscottl	if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) == NULL) {
99865245Smsmith	    device_printf(sc->amr_dev, "can't obtain product data from controller\n");
99965245Smsmith	    return(1);
100065245Smsmith	}
100165245Smsmith	sc->amr_maxdrives = 40;
100265245Smsmith	sc->amr_maxchan = ap->ap_nschan;
100365245Smsmith	sc->amr_maxio = ap->ap_maxio;
100465245Smsmith	sc->amr_type |= AMR_TYPE_40LD;
1005174194Sscottl	free(ap, M_AMR);
100658883Smsmith
1007153409Sscottl	ap = amr_enquiry(sc, 0, FC_DEL_LOGDRV, OP_SUP_DEL_LOGDRV, 0, &status);
1008154156Sscottl	if (ap != NULL)
1009174194Sscottl	    free(ap, M_AMR);
1010153409Sscottl	if (!status) {
1011153409Sscottl	    sc->amr_ld_del_supported = 1;
1012153409Sscottl	    device_printf(sc->amr_dev, "delete logical drives supported by controller\n");
1013153409Sscottl	}
101465245Smsmith    } else {
101565245Smsmith
101665245Smsmith	/* failed, try the 8LD ENQUIRY commands */
1017153409Sscottl	if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) == NULL) {
1018153409Sscottl	    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) == NULL) {
101965245Smsmith		device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
102065245Smsmith		return(1);
102165245Smsmith	    }
102265245Smsmith	    ae->ae_signature = 0;
102351974Smsmith	}
102465245Smsmith
102558883Smsmith	/*
102665245Smsmith	 * Fetch current state of logical drives.
102758883Smsmith	 */
102865245Smsmith	for (ldrv = 0; ldrv < ae->ae_ldrv.al_numdrives; ldrv++) {
102965245Smsmith	    sc->amr_drive[ldrv].al_size       = ae->ae_ldrv.al_size[ldrv];
103065245Smsmith	    sc->amr_drive[ldrv].al_state      = ae->ae_ldrv.al_state[ldrv];
103165245Smsmith	    sc->amr_drive[ldrv].al_properties = ae->ae_ldrv.al_properties[ldrv];
103265245Smsmith	    debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
103365245Smsmith		  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
103451974Smsmith	}
103565245Smsmith
103665245Smsmith	sc->amr_maxdrives = 8;
103765245Smsmith	sc->amr_maxchan = ae->ae_adapter.aa_channels;
103865245Smsmith	sc->amr_maxio = ae->ae_adapter.aa_maxio;
1039174194Sscottl	free(ae, M_AMR);
104051974Smsmith    }
104165245Smsmith
104265245Smsmith    /*
104365245Smsmith     * Mark remaining drives as unused.
104465245Smsmith     */
104565245Smsmith    for (; ldrv < AMR_MAXLD; ldrv++)
104665245Smsmith	sc->amr_drive[ldrv].al_size = 0xffffffff;
104765245Smsmith
104865245Smsmith    /*
104965245Smsmith     * Cap the maximum number of outstanding I/Os.  AMI's Linux driver doesn't trust
105065245Smsmith     * the controller's reported value, and lockups have been seen when we do.
105165245Smsmith     */
105265245Smsmith    sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD);
105365245Smsmith
105451974Smsmith    return(0);
105551974Smsmith}
105651974Smsmith
105751974Smsmith/********************************************************************************
105851974Smsmith * Run a generic enquiry-style command.
105951974Smsmith */
106051974Smsmithstatic void *
1061153409Sscottlamr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status)
106251974Smsmith{
106351974Smsmith    struct amr_command	*ac;
106451974Smsmith    void		*result;
106551974Smsmith    u_int8_t		*mbox;
106651974Smsmith    int			error;
106751974Smsmith
106865245Smsmith    debug_called(1);
106951974Smsmith
107051974Smsmith    error = 1;
107151974Smsmith    result = NULL;
107251974Smsmith
107351974Smsmith    /* get ourselves a command buffer */
1074153409Sscottl    mtx_lock(&sc->amr_list_lock);
1075153409Sscottl    ac = amr_alloccmd(sc);
1076153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1077153409Sscottl    if (ac == NULL)
107851974Smsmith	goto out;
107951974Smsmith    /* allocate the response structure */
1080174194Sscottl    if ((result = malloc(bufsize, M_AMR, M_ZERO|M_NOWAIT)) == NULL)
108151974Smsmith	goto out;
108265245Smsmith    /* set command flags */
1083138422Sscottl
1084135236Sscottl    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAIN;
108551974Smsmith
108665245Smsmith    /* point the command at our data */
108751974Smsmith    ac->ac_data = result;
108851974Smsmith    ac->ac_length = bufsize;
108951974Smsmith
109051974Smsmith    /* build the command proper */
109151974Smsmith    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
109251974Smsmith    mbox[0] = cmd;
109351974Smsmith    mbox[2] = cmdsub;
109451974Smsmith    mbox[3] = cmdqual;
1095153409Sscottl    *status = 0;
109651974Smsmith
109758883Smsmith    /* can't assume that interrupts are going to work here, so play it safe */
1098107756Semoore    if (sc->amr_poll_command(ac))
109951974Smsmith	goto out;
110051974Smsmith    error = ac->ac_status;
1101153409Sscottl    *status = ac->ac_status;
110251974Smsmith
110351974Smsmith out:
1104153409Sscottl    mtx_lock(&sc->amr_list_lock);
110551974Smsmith    if (ac != NULL)
110651974Smsmith	amr_releasecmd(ac);
1107153409Sscottl    mtx_unlock(&sc->amr_list_lock);
110851974Smsmith    if ((error != 0) && (result != NULL)) {
1109174194Sscottl	free(result, M_AMR);
111051974Smsmith	result = NULL;
111151974Smsmith    }
111251974Smsmith    return(result);
111351974Smsmith}
111451974Smsmith
111551974Smsmith/********************************************************************************
111651974Smsmith * Flush the controller's internal cache, return status.
111751974Smsmith */
111865245Smsmithint
111951974Smsmithamr_flush(struct amr_softc *sc)
112051974Smsmith{
112151974Smsmith    struct amr_command	*ac;
112251974Smsmith    int			error;
112351974Smsmith
112451974Smsmith    /* get ourselves a command buffer */
112551974Smsmith    error = 1;
1126153409Sscottl    mtx_lock(&sc->amr_list_lock);
1127153409Sscottl    ac = amr_alloccmd(sc);
1128153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1129153409Sscottl    if (ac == NULL)
113051974Smsmith	goto out;
113165245Smsmith    /* set command flags */
113251974Smsmith    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
113351974Smsmith
113451974Smsmith    /* build the command proper */
113551974Smsmith    ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
113651974Smsmith
113758883Smsmith    /* we have to poll, as the system may be going down or otherwise damaged */
1138107756Semoore    if (sc->amr_poll_command(ac))
113951974Smsmith	goto out;
114051974Smsmith    error = ac->ac_status;
114151974Smsmith
114251974Smsmith out:
1143153409Sscottl    mtx_lock(&sc->amr_list_lock);
114451974Smsmith    if (ac != NULL)
114551974Smsmith	amr_releasecmd(ac);
1146153409Sscottl    mtx_unlock(&sc->amr_list_lock);
114751974Smsmith    return(error);
114851974Smsmith}
114951974Smsmith
115051974Smsmith/********************************************************************************
1151106225Semoore * Detect extented cdb >> greater than 10 byte cdb support
1152106225Semoore * returns '1' means this support exist
1153106225Semoore * returns '0' means this support doesn't exist
1154106225Semoore */
1155106225Semoorestatic int
1156106225Semooreamr_support_ext_cdb(struct amr_softc *sc)
1157106225Semoore{
1158106225Semoore    struct amr_command	*ac;
1159106225Semoore    u_int8_t		*mbox;
1160106225Semoore    int			error;
1161106225Semoore
1162106225Semoore    /* get ourselves a command buffer */
1163106225Semoore    error = 0;
1164153409Sscottl    mtx_lock(&sc->amr_list_lock);
1165153409Sscottl    ac = amr_alloccmd(sc);
1166153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1167153409Sscottl    if (ac == NULL)
1168106225Semoore	goto out;
1169106225Semoore    /* set command flags */
1170106225Semoore    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
1171106225Semoore
1172106225Semoore    /* build the command proper */
1173106225Semoore    mbox = (u_int8_t *)&ac->ac_mailbox;		/* XXX want a real structure for this? */
1174106225Semoore    mbox[0] = 0xA4;
1175106225Semoore    mbox[2] = 0x16;
1176106225Semoore
1177106225Semoore
1178106225Semoore    /* we have to poll, as the system may be going down or otherwise damaged */
1179107756Semoore    if (sc->amr_poll_command(ac))
1180106225Semoore	goto out;
1181106225Semoore    if( ac->ac_status == AMR_STATUS_SUCCESS ) {
1182106225Semoore	    error = 1;
1183106225Semoore    }
1184106225Semoore
1185106225Semooreout:
1186153409Sscottl    mtx_lock(&sc->amr_list_lock);
1187106225Semoore    if (ac != NULL)
1188106225Semoore	amr_releasecmd(ac);
1189153409Sscottl    mtx_unlock(&sc->amr_list_lock);
1190106225Semoore    return(error);
1191106225Semoore}
1192106225Semoore
1193106225Semoore/********************************************************************************
119465245Smsmith * Try to find I/O work for the controller from one or more of the work queues.
119551974Smsmith *
119665245Smsmith * We make the assumption that if the controller is not ready to take a command
119765245Smsmith * at some given time, it will generate an interrupt at some later time when
119865245Smsmith * it is.
119951974Smsmith */
120065245Smsmithvoid
120151974Smsmithamr_startio(struct amr_softc *sc)
120251974Smsmith{
120351974Smsmith    struct amr_command	*ac;
120451974Smsmith
120551974Smsmith    /* spin until something prevents us from doing any work */
120651974Smsmith    for (;;) {
120751974Smsmith
1208138422Sscottl	/* Don't bother to queue commands no bounce buffers are available. */
1209138422Sscottl	if (sc->amr_state & AMR_STATE_QUEUE_FRZN)
1210138422Sscottl	    break;
1211138422Sscottl
121265245Smsmith	/* try to get a ready command */
121365245Smsmith	ac = amr_dequeue_ready(sc);
121451974Smsmith
121565245Smsmith	/* if that failed, build a command from a bio */
121665245Smsmith	if (ac == NULL)
121765245Smsmith	    (void)amr_bio_command(sc, &ac);
121851974Smsmith
121965245Smsmith	/* if that failed, build a command from a ccb */
1220184573Sscottl	if ((ac == NULL) && (sc->amr_cam_command != NULL))
1221184573Sscottl	    sc->amr_cam_command(sc, &ac);
1222105419Semoore
122365245Smsmith	/* if we don't have anything to do, give up */
122465245Smsmith	if (ac == NULL)
122565245Smsmith	    break;
122651974Smsmith
122765245Smsmith	/* try to give the command to the controller; if this fails save it for later and give up */
122865245Smsmith	if (amr_start(ac)) {
122965245Smsmith	    debug(2, "controller busy, command deferred");
123065245Smsmith	    amr_requeue_ready(ac);	/* XXX schedule retry very soon? */
123165245Smsmith	    break;
123251974Smsmith	}
123351974Smsmith    }
123451974Smsmith}
123551974Smsmith
123651974Smsmith/********************************************************************************
123751974Smsmith * Handle completion of an I/O command.
123851974Smsmith */
123951974Smsmithstatic void
124051974Smsmithamr_completeio(struct amr_command *ac)
124151974Smsmith{
1242153409Sscottl    struct amrd_softc		*sc = ac->ac_bio->bio_disk->d_drv1;
1243153409Sscottl    static struct timeval	lastfail;
1244153409Sscottl    static int			curfail;
1245140340Sscottl
124651974Smsmith    if (ac->ac_status != AMR_STATUS_SUCCESS) {	/* could be more verbose here? */
124765245Smsmith	ac->ac_bio->bio_error = EIO;
124865245Smsmith	ac->ac_bio->bio_flags |= BIO_ERROR;
124951974Smsmith
1250153409Sscottl	if (ppsratecheck(&lastfail, &curfail, 1))
1251153409Sscottl	    device_printf(sc->amrd_dev, "I/O error - 0x%x\n", ac->ac_status);
125265245Smsmith/*	amr_printcommand(ac);*/
125351974Smsmith    }
125465245Smsmith    amrd_intr(ac->ac_bio);
1255153409Sscottl    mtx_lock(&ac->ac_sc->amr_list_lock);
125665245Smsmith    amr_releasecmd(ac);
1257153409Sscottl    mtx_unlock(&ac->ac_sc->amr_list_lock);
125851974Smsmith}
125951974Smsmith
126051974Smsmith/********************************************************************************
126151974Smsmith ********************************************************************************
126251974Smsmith                                                               Command Processing
126351974Smsmith ********************************************************************************
126451974Smsmith ********************************************************************************/
126551974Smsmith
126651974Smsmith/********************************************************************************
126765245Smsmith * Convert a bio off the top of the bio queue into a command.
126865245Smsmith */
126965245Smsmithstatic int
127065245Smsmithamr_bio_command(struct amr_softc *sc, struct amr_command **acp)
127165245Smsmith{
127265245Smsmith    struct amr_command	*ac;
127365245Smsmith    struct amrd_softc	*amrd;
127465245Smsmith    struct bio		*bio;
127565245Smsmith    int			error;
127665245Smsmith    int			blkcount;
127765245Smsmith    int			driveno;
127865245Smsmith    int			cmd;
127965245Smsmith
1280175622Sscottl    ac = NULL;
128165245Smsmith    error = 0;
128265245Smsmith
1283140340Sscottl    /* get a command */
1284140340Sscottl    if ((ac = amr_alloccmd(sc)) == NULL)
1285140340Sscottl	return (ENOMEM);
1286140340Sscottl
128765245Smsmith    /* get a bio to work on */
1288140340Sscottl    if ((bio = amr_dequeue_bio(sc)) == NULL) {
1289140340Sscottl	amr_releasecmd(ac);
1290140340Sscottl	return (0);
1291140340Sscottl    }
129265245Smsmith
129365245Smsmith    /* connect the bio to the command */
129465245Smsmith    ac->ac_complete = amr_completeio;
129565245Smsmith    ac->ac_bio = bio;
129665245Smsmith    ac->ac_data = bio->bio_data;
129765245Smsmith    ac->ac_length = bio->bio_bcount;
1298163834Spjd    cmd = 0;
1299163834Spjd    switch (bio->bio_cmd) {
1300163834Spjd    case BIO_READ:
130165245Smsmith	ac->ac_flags |= AMR_CMD_DATAIN;
1302153409Sscottl	if (AMR_IS_SG64(sc)) {
1303153409Sscottl	    cmd = AMR_CMD_LREAD64;
1304153409Sscottl	    ac->ac_flags |= AMR_CMD_SG64;
1305153409Sscottl	} else
1306153409Sscottl	    cmd = AMR_CMD_LREAD;
1307163834Spjd	break;
1308163834Spjd    case BIO_WRITE:
130965245Smsmith	ac->ac_flags |= AMR_CMD_DATAOUT;
1310153409Sscottl	if (AMR_IS_SG64(sc)) {
1311153409Sscottl	    cmd = AMR_CMD_LWRITE64;
1312153409Sscottl	    ac->ac_flags |= AMR_CMD_SG64;
1313153409Sscottl	} else
1314153409Sscottl	    cmd = AMR_CMD_LWRITE;
1315163834Spjd	break;
1316163834Spjd    case BIO_FLUSH:
1317163834Spjd	ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
1318163834Spjd	cmd = AMR_CMD_FLUSH;
1319163834Spjd	break;
132065245Smsmith    }
1321111441Sphk    amrd = (struct amrd_softc *)bio->bio_disk->d_drv1;
132265245Smsmith    driveno = amrd->amrd_drive - sc->amr_drive;
132365245Smsmith    blkcount = (bio->bio_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
132465245Smsmith
132565245Smsmith    ac->ac_mailbox.mb_command = cmd;
1326163834Spjd    if (bio->bio_cmd & (BIO_READ|BIO_WRITE)) {
1327163834Spjd	ac->ac_mailbox.mb_blkcount = blkcount;
1328163834Spjd	ac->ac_mailbox.mb_lba = bio->bio_pblkno;
1329163834Spjd	if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size) {
1330163834Spjd	    device_printf(sc->amr_dev,
1331163834Spjd			  "I/O beyond end of unit (%lld,%d > %lu)\n",
1332163834Spjd			  (long long)bio->bio_pblkno, blkcount,
1333163834Spjd			  (u_long)sc->amr_drive[driveno].al_size);
1334163834Spjd	}
1335163834Spjd    }
133665245Smsmith    ac->ac_mailbox.mb_drive = driveno;
1337153409Sscottl    if (sc->amr_state & AMR_STATE_REMAP_LD)
1338153409Sscottl	ac->ac_mailbox.mb_drive |= 0x80;
1339153409Sscottl
134065245Smsmith    /* we fill in the s/g related data when the command is mapped */
134165245Smsmith
1342175622Sscottl
134365245Smsmith    *acp = ac;
134465245Smsmith    return(error);
134565245Smsmith}
134665245Smsmith
134765245Smsmith/********************************************************************************
134851974Smsmith * Take a command, submit it to the controller and sleep until it completes
134951974Smsmith * or fails.  Interrupts must be enabled, returns nonzero on error.
135051974Smsmith */
135151974Smsmithstatic int
135251974Smsmithamr_wait_command(struct amr_command *ac)
135351974Smsmith{
1354148498Sps    int			error = 0;
1355157585Sps    struct amr_softc	*sc = ac->ac_sc;
1356157585Sps
135765245Smsmith    debug_called(1);
135851974Smsmith
135951974Smsmith    ac->ac_complete = NULL;
136065245Smsmith    ac->ac_flags |= AMR_CMD_SLEEP;
1361153409Sscottl    if ((error = amr_start(ac)) != 0) {
136251974Smsmith	return(error);
1363153409Sscottl    }
136451974Smsmith
1365148498Sps    while ((ac->ac_flags & AMR_CMD_BUSY) && (error != EWOULDBLOCK)) {
1366157586Sscottl	error = msleep(ac,&sc->amr_list_lock, PRIBIO, "amrwcmd", 0);
136751974Smsmith    }
1368157585Sps
1369148498Sps    return(error);
137051974Smsmith}
137151974Smsmith
137251974Smsmith/********************************************************************************
137351974Smsmith * Take a command, submit it to the controller and busy-wait for it to return.
137451974Smsmith * Returns nonzero on error.  Can be safely called with interrupts enabled.
137551974Smsmith */
137651974Smsmithstatic int
1377107756Semooreamr_std_poll_command(struct amr_command *ac)
137851974Smsmith{
137951974Smsmith    struct amr_softc	*sc = ac->ac_sc;
138065245Smsmith    int			error, count;
138151974Smsmith
138265245Smsmith    debug_called(2);
138351974Smsmith
138451974Smsmith    ac->ac_complete = NULL;
138551974Smsmith    if ((error = amr_start(ac)) != 0)
138651974Smsmith	return(error);
138751974Smsmith
138851974Smsmith    count = 0;
138951974Smsmith    do {
139051974Smsmith	/*
139151974Smsmith	 * Poll for completion, although the interrupt handler may beat us to it.
139251974Smsmith	 * Note that the timeout here is somewhat arbitrary.
139351974Smsmith	 */
139451974Smsmith	amr_done(sc);
139565245Smsmith	DELAY(1000);
139665245Smsmith    } while ((ac->ac_flags & AMR_CMD_BUSY) && (count++ < 1000));
139765245Smsmith    if (!(ac->ac_flags & AMR_CMD_BUSY)) {
139851974Smsmith	error = 0;
139951974Smsmith    } else {
140065245Smsmith	/* XXX the slot is now marked permanently busy */
140151974Smsmith	error = EIO;
140265245Smsmith	device_printf(sc->amr_dev, "polled command timeout\n");
140351974Smsmith    }
140451974Smsmith    return(error);
140551974Smsmith}
140651974Smsmith
1407138422Sscottlstatic void
1408138422Sscottlamr_setup_polled_dmamap(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
1409138422Sscottl{
1410138422Sscottl    struct amr_command *ac = arg;
1411138422Sscottl    struct amr_softc *sc = ac->ac_sc;
1412174544Sscottl    int mb_channel;
1413138422Sscottl
1414175622Sscottl    if (err) {
1415175622Sscottl	device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
1416175622Sscottl	ac->ac_status = AMR_STATUS_ABORTED;
1417175622Sscottl	return;
1418175622Sscottl    }
1419175622Sscottl
1420174544Sscottl    amr_setup_sg(arg, segs, nsegs, err);
1421153409Sscottl
1422174544Sscottl    /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */
1423174544Sscottl    mb_channel = ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel;
1424174544Sscottl    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG &&
1425174544Sscottl        ((mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG) ||
1426174544Sscottl        (mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)))
1427174544Sscottl	((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param = ac->ac_nsegments;
1428174544Sscottl
1429174544Sscottl    ac->ac_mailbox.mb_nsgelem = ac->ac_nsegments;
1430174544Sscottl    ac->ac_mailbox.mb_physaddr = ac->ac_mb_physaddr;
1431153409Sscottl    if (AC_IS_SG64(ac)) {
1432174544Sscottl	ac->ac_sg64_hi = 0;
1433174544Sscottl	ac->ac_sg64_lo = ac->ac_sgbusaddr;
1434147536Sps    }
1435174544Sscottl
1436138422Sscottl    sc->amr_poll_command1(sc, ac);
1437138422Sscottl}
1438138422Sscottl
143951974Smsmith/********************************************************************************
1440107756Semoore * Take a command, submit it to the controller and busy-wait for it to return.
1441107756Semoore * Returns nonzero on error.  Can be safely called with interrupts enabled.
1442107756Semoore */
1443107756Semoorestatic int
1444107756Semooreamr_quartz_poll_command(struct amr_command *ac)
1445107756Semoore{
1446107756Semoore    struct amr_softc	*sc = ac->ac_sc;
1447152119Sscottl    int			error;
1448107756Semoore
1449107756Semoore    debug_called(2);
1450107756Semoore
1451138422Sscottl    error = 0;
1452138422Sscottl
1453153409Sscottl    if (AC_IS_SG64(ac)) {
1454174544Sscottl	ac->ac_tag = sc->amr_buffer64_dmat;
1455174544Sscottl	ac->ac_datamap = ac->ac_dma64map;
1456153409Sscottl    } else {
1457174544Sscottl	ac->ac_tag = sc->amr_buffer_dmat;
1458174544Sscottl	ac->ac_datamap = ac->ac_dmamap;
1459153409Sscottl    }
1460153409Sscottl
1461107756Semoore    /* now we have a slot, we can map the command (unmapped in amr_complete) */
1462138422Sscottl    if (ac->ac_data != 0) {
1463174544Sscottl	if (bus_dmamap_load(ac->ac_tag, ac->ac_datamap, ac->ac_data,
1464174544Sscottl	    ac->ac_length, amr_setup_polled_dmamap, ac, BUS_DMA_NOWAIT) != 0) {
1465138422Sscottl	    error = 1;
1466138422Sscottl	}
1467138422Sscottl    } else {
1468138422Sscottl	error = amr_quartz_poll_command1(sc, ac);
1469138422Sscottl    }
1470107756Semoore
1471138422Sscottl    return (error);
1472138422Sscottl}
1473107756Semoore
1474138422Sscottlstatic int
1475138422Sscottlamr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac)
1476138422Sscottl{
1477138422Sscottl    int count, error;
1478138422Sscottl
1479153409Sscottl    mtx_lock(&sc->amr_hw_lock);
1480140688Sscottl    if ((sc->amr_state & AMR_STATE_INTEN) == 0) {
1481120988Sps	count=0;
1482120988Sps	while (sc->amr_busyslots) {
1483153409Sscottl	    msleep(sc, &sc->amr_hw_lock, PRIBIO | PCATCH, "amrpoll", hz);
1484120988Sps	    if(count++>10) {
1485120988Sps		break;
1486120988Sps	    }
1487109031Semoore	}
1488109031Semoore
1489120988Sps	if(sc->amr_busyslots) {
1490120988Sps	    device_printf(sc->amr_dev, "adapter is busy\n");
1491153409Sscottl	    mtx_unlock(&sc->amr_hw_lock);
1492153409Sscottl	    if (ac->ac_data != NULL) {
1493174544Sscottl		bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
1494153409Sscottl	    }
1495120988Sps    	    ac->ac_status=0;
1496120988Sps	    return(1);
1497120988Sps	}
1498107756Semoore    }
1499107756Semoore
1500107756Semoore    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
1501107756Semoore
1502107756Semoore    /* clear the poll/ack fields in the mailbox */
1503107756Semoore    sc->amr_mailbox->mb_ident = 0xFE;
1504107756Semoore    sc->amr_mailbox->mb_nstatus = 0xFF;
1505107756Semoore    sc->amr_mailbox->mb_status = 0xFF;
1506107756Semoore    sc->amr_mailbox->mb_poll = 0;
1507107756Semoore    sc->amr_mailbox->mb_ack = 0;
1508109031Semoore    sc->amr_mailbox->mb_busy = 1;
1509107756Semoore
1510107756Semoore    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
1511107756Semoore
1512155222Sps    while(sc->amr_mailbox->mb_nstatus == 0xFF)
1513155222Sps	DELAY(1);
1514155222Sps    while(sc->amr_mailbox->mb_status == 0xFF)
1515155222Sps	DELAY(1);
1516107827Semoore    ac->ac_status=sc->amr_mailbox->mb_status;
1517107827Semoore    error = (ac->ac_status !=AMR_STATUS_SUCCESS) ? 1:0;
1518155222Sps    while(sc->amr_mailbox->mb_poll != 0x77)
1519155222Sps	DELAY(1);
1520107756Semoore    sc->amr_mailbox->mb_poll = 0;
1521107756Semoore    sc->amr_mailbox->mb_ack = 0x77;
1522107756Semoore
1523107756Semoore    /* acknowledge that we have the commands */
1524107756Semoore    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
1525155222Sps    while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
1526155222Sps	DELAY(1);
1527153409Sscottl    mtx_unlock(&sc->amr_hw_lock);
1528107756Semoore
1529107756Semoore    /* unmap the command's data buffer */
1530147536Sps    if (ac->ac_flags & AMR_CMD_DATAIN) {
1531174544Sscottl	bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, BUS_DMASYNC_POSTREAD);
1532147536Sps    }
1533147536Sps    if (ac->ac_flags & AMR_CMD_DATAOUT) {
1534174544Sscottl	bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, BUS_DMASYNC_POSTWRITE);
1535147536Sps    }
1536174544Sscottl    bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
1537107756Semoore
1538107827Semoore    return(error);
1539107756Semoore}
1540107756Semoore
1541153409Sscottlstatic __inline int
1542153409Sscottlamr_freeslot(struct amr_command *ac)
154351974Smsmith{
1544153409Sscottl    struct amr_softc *sc = ac->ac_sc;
1545153409Sscottl    int			slot;
154651974Smsmith
154765245Smsmith    debug_called(3);
154865245Smsmith
1549140340Sscottl    slot = ac->ac_slot;
1550153409Sscottl    if (sc->amr_busycmd[slot] == NULL)
1551153409Sscottl	panic("amr: slot %d not busy?\n", slot);
155265245Smsmith
1553153409Sscottl    sc->amr_busycmd[slot] = NULL;
1554153409Sscottl    atomic_subtract_int(&sc->amr_busyslots, 1);
155551974Smsmith
1556140340Sscottl    return (0);
155751974Smsmith}
155851974Smsmith
155951974Smsmith/********************************************************************************
156065245Smsmith * Map/unmap (ac)'s data in the controller's addressable space as required.
156165245Smsmith *
156265245Smsmith * These functions may be safely called multiple times on a given command.
156351974Smsmith */
156451974Smsmithstatic void
1565174544Sscottlamr_setup_sg(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
156651974Smsmith{
156751974Smsmith    struct amr_command	*ac = (struct amr_command *)arg;
156851974Smsmith    struct amr_sgentry	*sg;
1569174544Sscottl    struct amr_sg64entry *sg64;
1570174544Sscottl    int flags, i;
157151974Smsmith
157265245Smsmith    debug_called(3);
157351974Smsmith
1574153409Sscottl    /* get base address of s/g table */
1575153409Sscottl    sg = ac->ac_sg.sg32;
1576174544Sscottl    sg64 = ac->ac_sg.sg64;
157765245Smsmith
1578174544Sscottl    if (AC_IS_SG64(ac)) {
1579174544Sscottl	ac->ac_nsegments = nsegments;
1580174544Sscottl	ac->ac_mb_physaddr = 0xffffffff;
1581174544Sscottl	for (i = 0; i < nsegments; i++, sg64++) {
1582174544Sscottl	    sg64->sg_addr = segs[i].ds_addr;
1583174544Sscottl	    sg64->sg_count = segs[i].ds_len;
1584106225Semoore	}
1585105419Semoore    } else {
1586174544Sscottl	/* decide whether we need to populate the s/g table */
1587106225Semoore	if (nsegments < 2) {
1588174544Sscottl	    ac->ac_nsegments = 0;
1589174544Sscottl	    ac->ac_mb_physaddr = segs[0].ds_addr;
1590106225Semoore	} else {
1591174544Sscottl            ac->ac_nsegments = nsegments;
1592174544Sscottl	    ac->ac_mb_physaddr = ac->ac_sgbusaddr;
1593106225Semoore	    for (i = 0; i < nsegments; i++, sg++) {
1594105419Semoore		sg->sg_addr = segs[i].ds_addr;
1595105419Semoore		sg->sg_count = segs[i].ds_len;
1596106225Semoore	    }
1597105419Semoore	}
1598105419Semoore    }
1599138422Sscottl
1600174544Sscottl    flags = 0;
1601138422Sscottl    if (ac->ac_flags & AMR_CMD_DATAIN)
1602174544Sscottl	flags |= BUS_DMASYNC_PREREAD;
1603138422Sscottl    if (ac->ac_flags & AMR_CMD_DATAOUT)
1604174544Sscottl	flags |= BUS_DMASYNC_PREWRITE;
1605174544Sscottl    bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, flags);
1606138422Sscottl    ac->ac_flags |= AMR_CMD_MAPPED;
160751974Smsmith}
160851974Smsmith
1609153409Sscottlstatic void
1610174544Sscottlamr_setup_data(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
1611153409Sscottl{
1612174544Sscottl    struct amr_command *ac = arg;
1613174544Sscottl    struct amr_softc *sc = ac->ac_sc;
1614174544Sscottl    int mb_channel;
1615153409Sscottl
1616175622Sscottl    if (err) {
1617175622Sscottl	device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
1618175622Sscottl	amr_abort_load(ac);
1619175622Sscottl	return;
1620175622Sscottl    }
1621175622Sscottl
1622174544Sscottl    amr_setup_sg(arg, segs, nsegs, err);
1623153409Sscottl
1624174544Sscottl    /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */
1625174544Sscottl    mb_channel = ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel;
1626174544Sscottl    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG &&
1627174544Sscottl        ((mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG) ||
1628174544Sscottl        (mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)))
1629174544Sscottl	((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param = ac->ac_nsegments;
1630174544Sscottl
1631174544Sscottl    ac->ac_mailbox.mb_nsgelem = ac->ac_nsegments;
1632174544Sscottl    ac->ac_mailbox.mb_physaddr = ac->ac_mb_physaddr;
1633174544Sscottl    if (AC_IS_SG64(ac)) {
1634174544Sscottl	ac->ac_sg64_hi = 0;
1635174544Sscottl	ac->ac_sg64_lo = ac->ac_sgbusaddr;
1636153409Sscottl    }
1637153409Sscottl
1638155222Sps    if (sc->amr_submit_command(ac) == EBUSY) {
1639153409Sscottl	amr_freeslot(ac);
1640153409Sscottl	amr_requeue_ready(ac);
1641153409Sscottl    }
1642153409Sscottl}
1643174544Sscottl
1644160443Sjhbstatic void
1645174544Sscottlamr_setup_ccb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
1646160443Sjhb{
1647174544Sscottl    struct amr_command *ac = arg;
1648174544Sscottl    struct amr_softc *sc = ac->ac_sc;
1649174544Sscottl    struct amr_passthrough *ap = &ac->ac_ccb->ccb_pthru;
1650174544Sscottl    struct amr_ext_passthrough *aep = &ac->ac_ccb->ccb_epthru;
1651160443Sjhb
1652175622Sscottl    if (err) {
1653175622Sscottl	device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
1654175622Sscottl	amr_abort_load(ac);
1655175622Sscottl	return;
1656175622Sscottl    }
1657175622Sscottl
1658174544Sscottl    /* Set up the mailbox portion of the command to point at the ccb */
1659174544Sscottl    ac->ac_mailbox.mb_nsgelem = 0;
1660174544Sscottl    ac->ac_mailbox.mb_physaddr = ac->ac_ccb_busaddr;
1661160443Sjhb
1662174544Sscottl    amr_setup_sg(arg, segs, nsegs, err);
1663174544Sscottl
1664174544Sscottl    switch (ac->ac_mailbox.mb_command) {
1665174544Sscottl    case AMR_CMD_EXTPASS:
1666174544Sscottl	aep->ap_no_sg_elements = ac->ac_nsegments;
1667174544Sscottl	aep->ap_data_transfer_address = ac->ac_mb_physaddr;
1668174544Sscottl        break;
1669174544Sscottl    case AMR_CMD_PASS:
1670174544Sscottl	ap->ap_no_sg_elements = ac->ac_nsegments;
1671174544Sscottl	ap->ap_data_transfer_address = ac->ac_mb_physaddr;
1672174544Sscottl	break;
1673174544Sscottl    default:
1674174544Sscottl	panic("Unknown ccb command");
1675160443Sjhb    }
1676160443Sjhb
1677174544Sscottl    if (sc->amr_submit_command(ac) == EBUSY) {
1678174544Sscottl	amr_freeslot(ac);
1679174544Sscottl	amr_requeue_ready(ac);
1680160443Sjhb    }
1681160443Sjhb}
1682160443Sjhb
1683138422Sscottlstatic int
168451974Smsmithamr_mapcmd(struct amr_command *ac)
168551974Smsmith{
1686153409Sscottl    bus_dmamap_callback_t *cb;
168751974Smsmith    struct amr_softc	*sc = ac->ac_sc;
168851974Smsmith
168969319Smsmith    debug_called(3);
169051974Smsmith
1691153409Sscottl    if (AC_IS_SG64(ac)) {
1692174544Sscottl	ac->ac_tag = sc->amr_buffer64_dmat;
1693174544Sscottl	ac->ac_datamap = ac->ac_dma64map;
1694153409Sscottl    } else {
1695174544Sscottl	ac->ac_tag = sc->amr_buffer_dmat;
1696174544Sscottl	ac->ac_datamap = ac->ac_dmamap;
1697153409Sscottl    }
1698153409Sscottl
1699174544Sscottl    if (ac->ac_flags & AMR_CMD_CCB)
1700174544Sscottl	cb = amr_setup_ccb;
1701174544Sscottl    else
1702174544Sscottl	cb = amr_setup_data;
1703174544Sscottl
170465245Smsmith    /* if the command involves data at all, and hasn't been mapped */
1705138422Sscottl    if ((ac->ac_flags & AMR_CMD_MAPPED) == 0 && (ac->ac_data != NULL)) {
1706160443Sjhb	/* map the data buffers into bus space and build the s/g list */
1707174544Sscottl	if (bus_dmamap_load(ac->ac_tag, ac->ac_datamap, ac->ac_data,
1708174544Sscottl	     ac->ac_length, cb, ac, 0) == EINPROGRESS) {
1709160443Sjhb	    sc->amr_state |= AMR_STATE_QUEUE_FRZN;
1710160443Sjhb	}
1711153409Sscottl   } else {
1712155222Sps    	if (sc->amr_submit_command(ac) == EBUSY) {
1713153409Sscottl	    amr_freeslot(ac);
1714153409Sscottl	    amr_requeue_ready(ac);
1715153409Sscottl	}
1716143121Sscottl   }
1717143121Sscottl
1718138422Sscottl    return (0);
171951974Smsmith}
172051974Smsmith
172151974Smsmithstatic void
172251974Smsmithamr_unmapcmd(struct amr_command *ac)
172351974Smsmith{
1724153409Sscottl    int			flag;
172551974Smsmith
172669319Smsmith    debug_called(3);
172751974Smsmith
172865245Smsmith    /* if the command involved data at all and was mapped */
172965245Smsmith    if (ac->ac_flags & AMR_CMD_MAPPED) {
173051974Smsmith
173165245Smsmith	if (ac->ac_data != NULL) {
1732153409Sscottl
1733153409Sscottl	    flag = 0;
173465245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAIN)
1735153409Sscottl		flag |= BUS_DMASYNC_POSTREAD;
173665245Smsmith	    if (ac->ac_flags & AMR_CMD_DATAOUT)
1737153409Sscottl		flag |= BUS_DMASYNC_POSTWRITE;
1738153409Sscottl
1739174544Sscottl	    bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, flag);
1740174544Sscottl	    bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
174165245Smsmith	}
174265245Smsmith
174365245Smsmith	ac->ac_flags &= ~AMR_CMD_MAPPED;
174451974Smsmith    }
174551974Smsmith}
174651974Smsmith
1747175622Sscottlstatic void
1748175622Sscottlamr_abort_load(struct amr_command *ac)
1749175622Sscottl{
1750175622Sscottl    ac_qhead_t head;
1751175622Sscottl    struct amr_softc *sc = ac->ac_sc;
1752175622Sscottl
1753175622Sscottl    mtx_assert(&sc->amr_list_lock, MA_OWNED);
1754175622Sscottl
1755175622Sscottl    ac->ac_status = AMR_STATUS_ABORTED;
1756175622Sscottl    amr_init_qhead(&head);
1757175622Sscottl    amr_enqueue_completed(ac, &head);
1758175622Sscottl
1759175622Sscottl    mtx_unlock(&sc->amr_list_lock);
1760175622Sscottl    amr_complete(sc, &head);
1761175622Sscottl    mtx_lock(&sc->amr_list_lock);
1762175622Sscottl}
1763175622Sscottl
176451974Smsmith/********************************************************************************
176565245Smsmith * Take a command and give it to the controller, returns 0 if successful, or
176665245Smsmith * EBUSY if the command should be retried later.
176751974Smsmith */
176851974Smsmithstatic int
176951974Smsmithamr_start(struct amr_command *ac)
177051974Smsmith{
1771138422Sscottl    struct amr_softc *sc;
1772138422Sscottl    int error = 0;
1773153409Sscottl    int slot;
1774105419Semoore
177569319Smsmith    debug_called(3);
177651974Smsmith
177765245Smsmith    /* mark command as busy so that polling consumer can tell */
1778138422Sscottl    sc = ac->ac_sc;
177965245Smsmith    ac->ac_flags |= AMR_CMD_BUSY;
178065245Smsmith
178165245Smsmith    /* get a command slot (freed in amr_done) */
1782153409Sscottl    slot = ac->ac_slot;
1783153409Sscottl    if (sc->amr_busycmd[slot] != NULL)
1784153409Sscottl	panic("amr: slot %d busy?\n", slot);
1785153409Sscottl    sc->amr_busycmd[slot] = ac;
1786153409Sscottl    atomic_add_int(&sc->amr_busyslots, 1);
178765245Smsmith
1788138422Sscottl    /* Now we have a slot, we can map the command (unmapped in amr_complete). */
1789138422Sscottl    if ((error = amr_mapcmd(ac)) == ENOMEM) {
1790138422Sscottl	/*
1791138422Sscottl	 * Memroy resources are short, so free the slot and let this be tried
1792138422Sscottl	 * later.
1793138422Sscottl	 */
1794153409Sscottl	amr_freeslot(ac);
1795138422Sscottl    }
179665245Smsmith
1797138422Sscottl    return (error);
1798138422Sscottl}
1799138422Sscottl
180051974Smsmith/********************************************************************************
180151974Smsmith * Extract one or more completed commands from the controller (sc)
180251974Smsmith *
180352543Smsmith * Returns nonzero if any commands on the work queue were marked as completed.
180451974Smsmith */
1805140340Sscottl
180665245Smsmithint
180751974Smsmithamr_done(struct amr_softc *sc)
180851974Smsmith{
1809175622Sscottl    ac_qhead_t		head;
181051974Smsmith    struct amr_command	*ac;
181151974Smsmith    struct amr_mailbox	mbox;
181265245Smsmith    int			i, idx, result;
181351974Smsmith
181469319Smsmith    debug_called(3);
181551974Smsmith
181651974Smsmith    /* See if there's anything for us to do */
181751974Smsmith    result = 0;
1818175622Sscottl    amr_init_qhead(&head);
181951974Smsmith
182058883Smsmith    /* loop collecting completed commands */
182158883Smsmith    for (;;) {
182258883Smsmith	/* poll for a completed command's identifier and status */
182358883Smsmith	if (sc->amr_get_work(sc, &mbox)) {
182458883Smsmith	    result = 1;
182558883Smsmith
182658883Smsmith	    /* iterate over completed commands in this result */
182758883Smsmith	    for (i = 0; i < mbox.mb_nstatus; i++) {
182858883Smsmith		/* get pointer to busy command */
182958883Smsmith		idx = mbox.mb_completed[i] - 1;
183058883Smsmith		ac = sc->amr_busycmd[idx];
183151974Smsmith
183258883Smsmith		/* really a busy command? */
183358883Smsmith		if (ac != NULL) {
183458883Smsmith
183558883Smsmith		    /* pull the command from the busy index */
1836153409Sscottl		    amr_freeslot(ac);
183751974Smsmith
183865245Smsmith		    /* save status for later use */
183965245Smsmith		    ac->ac_status = mbox.mb_status;
1840175622Sscottl		    amr_enqueue_completed(ac, &head);
184165245Smsmith		    debug(3, "completed command with status %x", mbox.mb_status);
184265245Smsmith		} else {
184365245Smsmith		    device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
184451974Smsmith		}
184551974Smsmith	    }
1846153409Sscottl	} else
184765245Smsmith	    break;	/* no work */
184851974Smsmith    }
1849138422Sscottl
185058883Smsmith    /* handle completion and timeouts */
1851175622Sscottl    amr_complete(sc, &head);
1852138422Sscottl
185351974Smsmith    return(result);
185451974Smsmith}
185551974Smsmith
185651974Smsmith/********************************************************************************
185751974Smsmith * Do completion processing on done commands on (sc)
185851974Smsmith */
1859140340Sscottl
186051974Smsmithstatic void
1861175622Sscottlamr_complete(void *context, ac_qhead_t *head)
186251974Smsmith{
186365245Smsmith    struct amr_softc	*sc = (struct amr_softc *)context;
186465245Smsmith    struct amr_command	*ac;
186551974Smsmith
186669319Smsmith    debug_called(3);
186751974Smsmith
186865245Smsmith    /* pull completed commands off the queue */
186965245Smsmith    for (;;) {
1870175622Sscottl	ac = amr_dequeue_completed(sc, head);
187165245Smsmith	if (ac == NULL)
187265245Smsmith	    break;
187358883Smsmith
187465245Smsmith	/* unmap the command's data buffer */
187565245Smsmith	amr_unmapcmd(ac);
187651974Smsmith
187765245Smsmith	/*
187865245Smsmith	 * Is there a completion handler?
187965245Smsmith	 */
188065245Smsmith	if (ac->ac_complete != NULL) {
1881157585Sps	    /* unbusy the command */
1882157585Sps	    ac->ac_flags &= ~AMR_CMD_BUSY;
188365245Smsmith	    ac->ac_complete(ac);
188465245Smsmith
188551974Smsmith	    /*
188665245Smsmith	     * Is someone sleeping on this one?
188751974Smsmith	     */
1888157585Sps	} else {
1889157586Sscottl	    mtx_lock(&sc->amr_list_lock);
1890157585Sps	    ac->ac_flags &= ~AMR_CMD_BUSY;
1891157586Sscottl	    if (ac->ac_flags & AMR_CMD_SLEEP) {
1892157586Sscottl		/* unbusy the command */
1893157586Sscottl		wakeup(ac);
1894157586Sscottl	    }
1895157586Sscottl	    mtx_unlock(&sc->amr_list_lock);
1896157586Sscottl	}
1897109031Semoore
1898109031Semoore	if(!sc->amr_busyslots) {
1899109031Semoore	    wakeup(sc);
1900109031Semoore	}
190151974Smsmith    }
1902140340Sscottl
1903153409Sscottl    mtx_lock(&sc->amr_list_lock);
1904140340Sscottl    sc->amr_state &= ~AMR_STATE_QUEUE_FRZN;
1905140340Sscottl    amr_startio(sc);
1906153409Sscottl    mtx_unlock(&sc->amr_list_lock);
190751974Smsmith}
190851974Smsmith
190951974Smsmith/********************************************************************************
191051974Smsmith ********************************************************************************
191151974Smsmith                                                        Command Buffer Management
191251974Smsmith ********************************************************************************
191351974Smsmith ********************************************************************************/
191451974Smsmith
191551974Smsmith/********************************************************************************
191651974Smsmith * Get a new command buffer.
191751974Smsmith *
191851974Smsmith * This may return NULL in low-memory cases.
191951974Smsmith *
192051974Smsmith * If possible, we recycle a command buffer that's been used before.
192151974Smsmith */
192265245Smsmithstruct amr_command *
192351974Smsmithamr_alloccmd(struct amr_softc *sc)
192451974Smsmith{
192551974Smsmith    struct amr_command	*ac;
192651974Smsmith
192765245Smsmith    debug_called(3);
192851974Smsmith
192965245Smsmith    ac = amr_dequeue_free(sc);
193051974Smsmith    if (ac == NULL) {
1931140340Sscottl	sc->amr_state |= AMR_STATE_QUEUE_FRZN;
193265245Smsmith	return(NULL);
1933140340Sscottl    }
193465245Smsmith
193565245Smsmith    /* clear out significant fields */
193665245Smsmith    ac->ac_status = 0;
193751974Smsmith    bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
193865245Smsmith    ac->ac_flags = 0;
193965245Smsmith    ac->ac_bio = NULL;
194065245Smsmith    ac->ac_data = NULL;
194165245Smsmith    ac->ac_complete = NULL;
1942175622Sscottl    ac->ac_retries = 0;
1943174544Sscottl    ac->ac_tag = NULL;
1944174544Sscottl    ac->ac_datamap = NULL;
194551974Smsmith    return(ac);
194651974Smsmith}
194751974Smsmith
194851974Smsmith/********************************************************************************
194951974Smsmith * Release a command buffer for recycling.
195051974Smsmith */
195165245Smsmithvoid
195251974Smsmithamr_releasecmd(struct amr_command *ac)
195351974Smsmith{
195465245Smsmith    debug_called(3);
195551974Smsmith
195665245Smsmith    amr_enqueue_free(ac);
195751974Smsmith}
195851974Smsmith
195951974Smsmith/********************************************************************************
196065245Smsmith * Allocate a new command cluster and initialise it.
196151974Smsmith */
1962104094Sphkstatic void
196365245Smsmithamr_alloccmd_cluster(struct amr_softc *sc)
196451974Smsmith{
196565245Smsmith    struct amr_command_cluster	*acc;
196665245Smsmith    struct amr_command		*ac;
1967152119Sscottl    int				i, nextslot;
196851974Smsmith
1969175622Sscottl    /*
1970175622Sscottl     * If we haven't found the real limit yet, let us have a couple of
1971175622Sscottl     * commands in order to be able to probe.
1972175622Sscottl     */
1973175622Sscottl    if (sc->amr_maxio == 0)
1974175622Sscottl	sc->amr_maxio = 2;
1975175622Sscottl
1976140340Sscottl    if (sc->amr_nextslot > sc->amr_maxio)
1977140340Sscottl	return;
1978174194Sscottl    acc = malloc(AMR_CMD_CLUSTERSIZE, M_AMR, M_NOWAIT | M_ZERO);
197965245Smsmith    if (acc != NULL) {
1980140340Sscottl	nextslot = sc->amr_nextslot;
1981175622Sscottl	mtx_lock(&sc->amr_list_lock);
198265245Smsmith	TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
1983175622Sscottl	mtx_unlock(&sc->amr_list_lock);
198465245Smsmith	for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
198565245Smsmith	    ac = &acc->acc_command[i];
198665245Smsmith	    ac->ac_sc = sc;
1987140340Sscottl	    ac->ac_slot = nextslot;
1988153409Sscottl
1989153409Sscottl	    /*
1990153409Sscottl	     * The SG table for each slot is a fixed size and is assumed to
1991153409Sscottl	     * to hold 64-bit s/g objects when the driver is configured to do
1992153409Sscottl	     * 64-bit DMA.  32-bit DMA commands still use the same table, but
1993153409Sscottl	     * cast down to 32-bit objects.
1994153409Sscottl	     */
1995153409Sscottl	    if (AMR_IS_SG64(sc)) {
1996153409Sscottl		ac->ac_sgbusaddr = sc->amr_sgbusaddr +
1997153409Sscottl		    (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sg64entry));
1998153409Sscottl	        ac->ac_sg.sg64 = sc->amr_sg64table + (ac->ac_slot * AMR_NSEG);
1999153409Sscottl	    } else {
2000153409Sscottl		ac->ac_sgbusaddr = sc->amr_sgbusaddr +
2001153409Sscottl		    (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
2002153409Sscottl	        ac->ac_sg.sg32 = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
2003153409Sscottl	    }
2004153409Sscottl
2005174544Sscottl	    ac->ac_ccb = sc->amr_ccb + ac->ac_slot;
2006174544Sscottl	    ac->ac_ccb_busaddr = sc->amr_ccb_busaddr +
2007174544Sscottl		(ac->ac_slot * sizeof(union amr_ccb));
2008174544Sscottl
2009174544Sscottl	    if (bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap))
2010174544Sscottl		break;
2011174544Sscottl	    if (AMR_IS_SG64(sc) &&
2012174544Sscottl		(bus_dmamap_create(sc->amr_buffer64_dmat, 0,&ac->ac_dma64map)))
2013174544Sscottl		break;
2014153409Sscottl	    amr_releasecmd(ac);
2015140340Sscottl	    if (++nextslot > sc->amr_maxio)
2016140340Sscottl		break;
201765245Smsmith	}
2018140340Sscottl	sc->amr_nextslot = nextslot;
201965245Smsmith    }
202051974Smsmith}
202151974Smsmith
202251974Smsmith/********************************************************************************
202365245Smsmith * Free a command cluster
202465245Smsmith */
2025104094Sphkstatic void
202665245Smsmithamr_freecmd_cluster(struct amr_command_cluster *acc)
202765245Smsmith{
202865245Smsmith    struct amr_softc	*sc = acc->acc_command[0].ac_sc;
202965245Smsmith    int			i;
203065245Smsmith
2031153409Sscottl    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
2032175622Sscottl	if (acc->acc_command[i].ac_sc == NULL)
2033175622Sscottl	    break;
203465245Smsmith	bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
2035153409Sscottl	if (AMR_IS_SG64(sc))
2036153409Sscottl		bus_dmamap_destroy(sc->amr_buffer64_dmat, acc->acc_command[i].ac_dma64map);
2037153409Sscottl    }
2038174194Sscottl    free(acc, M_AMR);
203965245Smsmith}
204065245Smsmith
204165245Smsmith/********************************************************************************
204251974Smsmith ********************************************************************************
204351974Smsmith                                                         Interface-specific Shims
204451974Smsmith ********************************************************************************
204551974Smsmith ********************************************************************************/
204651974Smsmith
204751974Smsmith/********************************************************************************
204851974Smsmith * Tell the controller that the mailbox contains a valid command
204951974Smsmith */
205065245Smsmithstatic int
2051155222Spsamr_quartz_submit_command(struct amr_command *ac)
205251974Smsmith{
2053155222Sps    struct amr_softc	*sc = ac->ac_sc;
2054175622Sscottl    static struct timeval lastfail;
2055175622Sscottl    static int		curfail;
2056155222Sps    int			i = 0;
2057155222Sps
2058155222Sps    mtx_lock(&sc->amr_hw_lock);
2059180633Sscottl    while (sc->amr_mailbox->mb_busy && (i++ < 10)) {
2060155222Sps        DELAY(1);
2061180633Sscottl	/* This is a no-op read that flushes pending mailbox updates */
2062180633Sscottl	AMR_QGET_ODB(sc);
2063180633Sscottl    }
2064155222Sps    if (sc->amr_mailbox->mb_busy) {
2065155222Sps	mtx_unlock(&sc->amr_hw_lock);
2066175622Sscottl	if (ac->ac_retries++ > 1000) {
2067175622Sscottl	    if (ppsratecheck(&lastfail, &curfail, 1))
2068175622Sscottl		device_printf(sc->amr_dev, "Too many retries on command %p.  "
2069175622Sscottl			      "Controller is likely dead\n", ac);
2070175622Sscottl	    ac->ac_retries = 0;
2071175622Sscottl	}
2072155222Sps	return (EBUSY);
2073155222Sps    }
207451974Smsmith
2075155222Sps    /*
2076155222Sps     * Save the slot number so that we can locate this command when complete.
2077155222Sps     * Note that ident = 0 seems to be special, so we don't use it.
2078155222Sps     */
2079155222Sps    ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */
2080155222Sps    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14);
2081155222Sps    sc->amr_mailbox->mb_busy = 1;
2082155222Sps    sc->amr_mailbox->mb_poll = 0;
2083155222Sps    sc->amr_mailbox->mb_ack  = 0;
2084155222Sps    sc->amr_mailbox64->sg64_hi = ac->ac_sg64_hi;
2085155222Sps    sc->amr_mailbox64->sg64_lo = ac->ac_sg64_lo;
2086155222Sps
208751974Smsmith    AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
2088155222Sps    mtx_unlock(&sc->amr_hw_lock);
208965245Smsmith    return(0);
209051974Smsmith}
209151974Smsmith
209265245Smsmithstatic int
2093155222Spsamr_std_submit_command(struct amr_command *ac)
209451974Smsmith{
2095155222Sps    struct amr_softc	*sc = ac->ac_sc;
2096175622Sscottl    static struct timeval lastfail;
2097175622Sscottl    static int		curfail;
2098155222Sps
2099155222Sps    mtx_lock(&sc->amr_hw_lock);
2100155222Sps    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG) {
2101155222Sps	mtx_unlock(&sc->amr_hw_lock);
2102175622Sscottl	if (ac->ac_retries++ > 1000) {
2103175622Sscottl	    if (ppsratecheck(&lastfail, &curfail, 1))
2104175622Sscottl		device_printf(sc->amr_dev, "Too many retries on command %p.  "
2105175622Sscottl			      "Controller is likely dead\n", ac);
2106175622Sscottl	    ac->ac_retries = 0;
2107175622Sscottl	}
2108155222Sps	return (EBUSY);
2109155222Sps    }
211051974Smsmith
2111155222Sps    /*
2112155222Sps     * Save the slot number so that we can locate this command when complete.
2113155222Sps     * Note that ident = 0 seems to be special, so we don't use it.
2114155222Sps     */
2115155222Sps    ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */
2116155222Sps    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14);
2117155222Sps    sc->amr_mailbox->mb_busy = 1;
2118155222Sps    sc->amr_mailbox->mb_poll = 0;
2119155222Sps    sc->amr_mailbox->mb_ack  = 0;
2120155222Sps
212151974Smsmith    AMR_SPOST_COMMAND(sc);
2122155222Sps    mtx_unlock(&sc->amr_hw_lock);
212365245Smsmith    return(0);
212451974Smsmith}
212551974Smsmith
212651974Smsmith/********************************************************************************
212751974Smsmith * Claim any work that the controller has completed; acknowledge completion,
212851974Smsmith * save details of the completion in (mbsave)
212951974Smsmith */
213051974Smsmithstatic int
213151974Smsmithamr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
213251974Smsmith{
2133153409Sscottl    int		worked, i;
213451974Smsmith    u_int32_t	outd;
2135154876Sambrisko    u_int8_t	nstatus;
2136153409Sscottl    u_int8_t	completed[46];
213751974Smsmith
213865245Smsmith    debug_called(3);
213965245Smsmith
214051974Smsmith    worked = 0;
214151974Smsmith
214251974Smsmith    /* work waiting for us? */
214351974Smsmith    if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
214451974Smsmith
2145140340Sscottl	/* acknowledge interrupt */
2146140340Sscottl	AMR_QPUT_ODB(sc, AMR_QODB_READY);
2147140340Sscottl
2148140340Sscottl	while ((nstatus = sc->amr_mailbox->mb_nstatus) == 0xff)
2149155222Sps	    DELAY(1);
2150140340Sscottl	sc->amr_mailbox->mb_nstatus = 0xff;
2151140340Sscottl
2152153409Sscottl	/* wait until fw wrote out all completions */
2153153409Sscottl	for (i = 0; i < nstatus; i++) {
2154153409Sscottl	    while ((completed[i] = sc->amr_mailbox->mb_completed[i]) == 0xff)
2155155222Sps		DELAY(1);
2156153409Sscottl	    sc->amr_mailbox->mb_completed[i] = 0xff;
2157153409Sscottl	}
2158153409Sscottl
2159154876Sambrisko	/* Save information for later processing */
2160154876Sambrisko	mbsave->mb_nstatus = nstatus;
2161154876Sambrisko	mbsave->mb_status = sc->amr_mailbox->mb_status;
2162153409Sscottl	sc->amr_mailbox->mb_status = 0xff;
2163153409Sscottl
2164153409Sscottl	for (i = 0; i < nstatus; i++)
2165153409Sscottl	    mbsave->mb_completed[i] = completed[i];
216651974Smsmith
216751974Smsmith	/* acknowledge that we have the commands */
2168140340Sscottl	AMR_QPUT_IDB(sc, AMR_QIDB_ACK);
216965245Smsmith
2170153409Sscottl#if 0
217165763Smsmith#ifndef AMR_QUARTZ_GOFASTER
217265245Smsmith	/*
217365245Smsmith	 * This waits for the controller to notice that we've taken the
217465245Smsmith	 * command from it.  It's very inefficient, and we shouldn't do it,
217565245Smsmith	 * but if we remove this code, we stop completing commands under
217665245Smsmith	 * load.
217765245Smsmith	 *
217865245Smsmith	 * Peter J says we shouldn't do this.  The documentation says we
217965245Smsmith	 * should.  Who is right?
218065245Smsmith	 */
218151974Smsmith	while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
218251974Smsmith	    ;				/* XXX aiee! what if it dies? */
218365245Smsmith#endif
2184153409Sscottl#endif
218565245Smsmith
218651974Smsmith	worked = 1;			/* got some work */
218751974Smsmith    }
218851974Smsmith
218951974Smsmith    return(worked);
219051974Smsmith}
219151974Smsmith
219251974Smsmithstatic int
219351974Smsmithamr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
219451974Smsmith{
2195152119Sscottl    int		worked;
219651974Smsmith    u_int8_t	istat;
219751974Smsmith
219865245Smsmith    debug_called(3);
219951974Smsmith
220051974Smsmith    worked = 0;
220151974Smsmith
220251974Smsmith    /* check for valid interrupt status */
220351974Smsmith    istat = AMR_SGET_ISTAT(sc);
220451974Smsmith    if ((istat & AMR_SINTR_VALID) != 0) {
220551974Smsmith	AMR_SPUT_ISTAT(sc, istat);	/* ack interrupt status */
220651974Smsmith
220751974Smsmith	/* save mailbox, which contains a list of completed commands */
220865245Smsmith	bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
220951974Smsmith
221051974Smsmith	AMR_SACK_INTERRUPT(sc);		/* acknowledge we have the mailbox */
221151974Smsmith	worked = 1;
221251974Smsmith    }
221351974Smsmith
221451974Smsmith    return(worked);
221551974Smsmith}
221651974Smsmith
221751974Smsmith/********************************************************************************
221851974Smsmith * Notify the controller of the mailbox location.
221951974Smsmith */
222051974Smsmithstatic void
222151974Smsmithamr_std_attach_mailbox(struct amr_softc *sc)
222251974Smsmith{
222351974Smsmith
222451974Smsmith    /* program the mailbox physical address */
222551974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_0, sc->amr_mailboxphys         & 0xff);
222651974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_1, (sc->amr_mailboxphys >>  8) & 0xff);
222751974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_2, (sc->amr_mailboxphys >> 16) & 0xff);
222851974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_3, (sc->amr_mailboxphys >> 24) & 0xff);
222951974Smsmith    AMR_SBYTE_SET(sc, AMR_SMBOX_ENABLE, AMR_SMBOX_ADDR);
223051974Smsmith
223151974Smsmith    /* clear any outstanding interrupt and enable interrupts proper */
223251974Smsmith    AMR_SACK_INTERRUPT(sc);
223351974Smsmith    AMR_SENABLE_INTR(sc);
223451974Smsmith}
223551974Smsmith
223665245Smsmith#ifdef AMR_BOARD_INIT
223751974Smsmith/********************************************************************************
223865245Smsmith * Initialise the controller
223965245Smsmith */
224065245Smsmithstatic int
224165245Smsmithamr_quartz_init(struct amr_softc *sc)
224265245Smsmith{
224365245Smsmith    int		status, ostatus;
224465245Smsmith
224565245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_QGET_INITSTATUS(sc));
224665245Smsmith
224765245Smsmith    AMR_QRESET(sc);
224865245Smsmith
224965245Smsmith    ostatus = 0xff;
225065245Smsmith    while ((status = AMR_QGET_INITSTATUS(sc)) != AMR_QINIT_DONE) {
225165245Smsmith	if (status != ostatus) {
225265245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_qinit, status));
225365245Smsmith	    ostatus = status;
225465245Smsmith	}
225565245Smsmith	switch (status) {
225665245Smsmith	case AMR_QINIT_NOMEM:
225765245Smsmith	    return(ENOMEM);
225865245Smsmith
225965245Smsmith	case AMR_QINIT_SCAN:
226065245Smsmith	    /* XXX we could print channel/target here */
226165245Smsmith	    break;
226265245Smsmith	}
226365245Smsmith    }
226465245Smsmith    return(0);
226565245Smsmith}
226665245Smsmith
226765245Smsmithstatic int
226865245Smsmithamr_std_init(struct amr_softc *sc)
226965245Smsmith{
227065245Smsmith    int		status, ostatus;
227165245Smsmith
227265245Smsmith    device_printf(sc->amr_dev, "initial init status %x\n", AMR_SGET_INITSTATUS(sc));
227365245Smsmith
227465245Smsmith    AMR_SRESET(sc);
227565245Smsmith
227665245Smsmith    ostatus = 0xff;
227765245Smsmith    while ((status = AMR_SGET_INITSTATUS(sc)) != AMR_SINIT_DONE) {
227865245Smsmith	if (status != ostatus) {
227965245Smsmith	    device_printf(sc->amr_dev, "(%x) %s\n", status, amr_describe_code(amr_table_sinit, status));
228065245Smsmith	    ostatus = status;
228165245Smsmith	}
228265245Smsmith	switch (status) {
228365245Smsmith	case AMR_SINIT_NOMEM:
228465245Smsmith	    return(ENOMEM);
228565245Smsmith
228665245Smsmith	case AMR_SINIT_INPROG:
228765245Smsmith	    /* XXX we could print channel/target here? */
228865245Smsmith	    break;
228965245Smsmith	}
229065245Smsmith    }
229165245Smsmith    return(0);
229265245Smsmith}
229365245Smsmith#endif
229465245Smsmith
229565245Smsmith/********************************************************************************
229651974Smsmith ********************************************************************************
229751974Smsmith                                                                        Debugging
229851974Smsmith ********************************************************************************
229951974Smsmith ********************************************************************************/
230051974Smsmith
230151974Smsmith/********************************************************************************
230265245Smsmith * Identify the controller and print some information about it.
230365245Smsmith */
230465245Smsmithstatic void
230565245Smsmithamr_describe_controller(struct amr_softc *sc)
230665245Smsmith{
230765245Smsmith    struct amr_prodinfo	*ap;
230865245Smsmith    struct amr_enquiry	*ae;
230965245Smsmith    char		*prod;
2310153409Sscottl    int			status;
231165245Smsmith
231265245Smsmith    /*
231365245Smsmith     * Try to get 40LD product info, which tells us what the card is labelled as.
231465245Smsmith     */
2315153409Sscottl    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) != NULL) {
2316105419Semoore	device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
231765245Smsmith		      ap->ap_product, ap->ap_firmware, ap->ap_bios,
231865245Smsmith		      ap->ap_memsize);
231965245Smsmith
2320174194Sscottl	free(ap, M_AMR);
232165245Smsmith	return;
232265245Smsmith    }
232365245Smsmith
232465245Smsmith    /*
232565245Smsmith     * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
232665245Smsmith     */
2327153409Sscottl    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) != NULL) {
232865245Smsmith	prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
232965245Smsmith
2330153409Sscottl    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) != NULL) {
233165245Smsmith
233265245Smsmith	/*
233365245Smsmith	 * Try to work it out based on the PCI signatures.
233465245Smsmith	 */
233565245Smsmith	switch (pci_get_device(sc->amr_dev)) {
233665245Smsmith	case 0x9010:
233765245Smsmith	    prod = "Series 428";
233865245Smsmith	    break;
233965245Smsmith	case 0x9060:
234065245Smsmith	    prod = "Series 434";
234165245Smsmith	    break;
234265245Smsmith	default:
234365245Smsmith	    prod = "unknown controller";
234465245Smsmith	    break;
234565245Smsmith	}
234665245Smsmith    } else {
2347144369Ssam	device_printf(sc->amr_dev, "<unsupported controller>\n");
2348144369Ssam	return;
234965245Smsmith    }
235074936Shm
235174936Shm    /*
235274936Shm     * HP NetRaid controllers have a special encoding of the firmware and
235374936Shm     * BIOS versions. The AMI version seems to have it as strings whereas
235474936Shm     * the HP version does it with a leading uppercase character and two
235574936Shm     * binary numbers.
235674936Shm     */
235774936Shm
235874936Shm    if(ae->ae_adapter.aa_firmware[2] >= 'A' &&
235974936Shm       ae->ae_adapter.aa_firmware[2] <= 'Z' &&
236074936Shm       ae->ae_adapter.aa_firmware[1] <  ' ' &&
236174936Shm       ae->ae_adapter.aa_firmware[0] <  ' ' &&
236274936Shm       ae->ae_adapter.aa_bios[2] >= 'A'     &&
236374936Shm       ae->ae_adapter.aa_bios[2] <= 'Z'     &&
236474936Shm       ae->ae_adapter.aa_bios[1] <  ' '     &&
236574936Shm       ae->ae_adapter.aa_bios[0] <  ' ') {
236674936Shm
236774936Shm	/* this looks like we have an HP NetRaid version of the MegaRaid */
236874936Shm
236974936Shm    	if(ae->ae_signature == AMR_SIG_438) {
2370108470Sschweikh    		/* the AMI 438 is a NetRaid 3si in HP-land */
237174936Shm    		prod = "HP NetRaid 3si";
237274936Shm    	}
237374936Shm
237474936Shm	device_printf(sc->amr_dev, "<%s> Firmware %c.%02d.%02d, BIOS %c.%02d.%02d, %dMB RAM\n",
237574936Shm		      prod, ae->ae_adapter.aa_firmware[2],
237674936Shm		      ae->ae_adapter.aa_firmware[1],
237774936Shm		      ae->ae_adapter.aa_firmware[0],
237874936Shm		      ae->ae_adapter.aa_bios[2],
237974936Shm		      ae->ae_adapter.aa_bios[1],
238074936Shm		      ae->ae_adapter.aa_bios[0],
238174936Shm		      ae->ae_adapter.aa_memorysize);
238274936Shm    } else {
238374936Shm	device_printf(sc->amr_dev, "<%s> Firmware %.4s, BIOS %.4s, %dMB RAM\n",
238474936Shm		      prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
238574936Shm		      ae->ae_adapter.aa_memorysize);
238674936Shm    }
2387174194Sscottl    free(ae, M_AMR);
238865245Smsmith}
238965245Smsmith
2390120988Spsint
2391120988Spsamr_dump_blocks(struct amr_softc *sc, int unit, u_int32_t lba, void *data, int blks)
2392120988Sps{
2393120988Sps    struct amr_command	*ac;
2394120988Sps    int			error = EIO;
2395120988Sps
2396120988Sps    debug_called(1);
2397120988Sps
2398140688Sscottl    sc->amr_state |= AMR_STATE_INTEN;
2399120988Sps
2400120988Sps    /* get ourselves a command buffer */
2401120988Sps    if ((ac = amr_alloccmd(sc)) == NULL)
2402120988Sps	goto out;
2403120988Sps    /* set command flags */
2404120988Sps    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
2405120988Sps
2406120988Sps    /* point the command at our data */
2407120988Sps    ac->ac_data = data;
2408120988Sps    ac->ac_length = blks * AMR_BLKSIZE;
2409120988Sps
2410120988Sps    /* build the command proper */
2411120988Sps    ac->ac_mailbox.mb_command 	= AMR_CMD_LWRITE;
2412120988Sps    ac->ac_mailbox.mb_blkcount	= blks;
2413120988Sps    ac->ac_mailbox.mb_lba	= lba;
2414120988Sps    ac->ac_mailbox.mb_drive	= unit;
2415120988Sps
2416120988Sps    /* can't assume that interrupts are going to work here, so play it safe */
2417120988Sps    if (sc->amr_poll_command(ac))
2418120988Sps	goto out;
2419120988Sps    error = ac->ac_status;
2420120988Sps
2421120988Sps out:
2422120988Sps    if (ac != NULL)
2423120988Sps	amr_releasecmd(ac);
2424120988Sps
2425140688Sscottl    sc->amr_state &= ~AMR_STATE_INTEN;
2426120988Sps    return (error);
2427120988Sps}
2428120988Sps
2429120988Sps
2430120988Sps
243165245Smsmith#ifdef AMR_DEBUG
243265245Smsmith/********************************************************************************
243351974Smsmith * Print the command (ac) in human-readable format
243451974Smsmith */
2435107756Semoore#if 0
243651974Smsmithstatic void
243751974Smsmithamr_printcommand(struct amr_command *ac)
243851974Smsmith{
243951974Smsmith    struct amr_softc	*sc = ac->ac_sc;
244051974Smsmith    struct amr_sgentry	*sg;
244151974Smsmith    int			i;
244251974Smsmith
244351974Smsmith    device_printf(sc->amr_dev, "cmd %x  ident %d  drive %d\n",
244451974Smsmith		  ac->ac_mailbox.mb_command, ac->ac_mailbox.mb_ident, ac->ac_mailbox.mb_drive);
244551974Smsmith    device_printf(sc->amr_dev, "blkcount %d  lba %d\n",
244651974Smsmith		  ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
244754512Speter    device_printf(sc->amr_dev, "virtaddr %p  length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
244858883Smsmith    device_printf(sc->amr_dev, "sg physaddr %08x  nsg %d\n",
244951974Smsmith		  ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
245065245Smsmith    device_printf(sc->amr_dev, "ccb %p  bio %p\n", ac->ac_ccb_data, ac->ac_bio);
245151974Smsmith
245251974Smsmith    /* get base address of s/g table */
245351974Smsmith    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
245451974Smsmith    for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
245551974Smsmith	device_printf(sc->amr_dev, "  %x/%d\n", sg->sg_addr, sg->sg_count);
245651974Smsmith}
245765245Smsmith#endif
2458107756Semoore#endif
2459