xref: /freebsd/sys/dev/aac/aac.c (revision 0b94a66e3f8a898cdf6481c428f6cedca31c3958)
135863739SMike Smith /*-
235863739SMike Smith  * Copyright (c) 2000 Michael Smith
335863739SMike Smith  * Copyright (c) 2000 BSDi
435863739SMike Smith  * All rights reserved.
535863739SMike Smith  *
635863739SMike Smith  * Redistribution and use in source and binary forms, with or without
735863739SMike Smith  * modification, are permitted provided that the following conditions
835863739SMike Smith  * are met:
935863739SMike Smith  * 1. Redistributions of source code must retain the above copyright
1035863739SMike Smith  *    notice, this list of conditions and the following disclaimer.
1135863739SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1235863739SMike Smith  *    notice, this list of conditions and the following disclaimer in the
1335863739SMike Smith  *    documentation and/or other materials provided with the distribution.
1435863739SMike Smith  *
1535863739SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1635863739SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1735863739SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1835863739SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1935863739SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2035863739SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2135863739SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2235863739SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2335863739SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2435863739SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2535863739SMike Smith  * SUCH DAMAGE.
2635863739SMike Smith  *
2735863739SMike Smith  *	$FreeBSD$
2835863739SMike Smith  */
2935863739SMike Smith 
3035863739SMike Smith /*
3135863739SMike Smith  * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
3235863739SMike Smith  */
3335863739SMike Smith 
3435863739SMike Smith #include <sys/param.h>
3535863739SMike Smith #include <sys/systm.h>
3635863739SMike Smith #include <sys/malloc.h>
3735863739SMike Smith #include <sys/kernel.h>
3835863739SMike Smith 
3935863739SMike Smith #include <dev/aac/aac_compat.h>
4035863739SMike Smith 
4135863739SMike Smith #include <sys/bus.h>
4235863739SMike Smith #include <sys/conf.h>
4335863739SMike Smith #include <sys/devicestat.h>
4435863739SMike Smith #include <sys/disk.h>
4535863739SMike Smith #include <sys/file.h>
4635863739SMike Smith #include <sys/signalvar.h>
470b94a66eSMike Smith #include <sys/time.h>
4835863739SMike Smith 
4935863739SMike Smith #include <machine/bus_memio.h>
5035863739SMike Smith #include <machine/bus.h>
5135863739SMike Smith #include <machine/resource.h>
5235863739SMike Smith 
5335863739SMike Smith #include <dev/aac/aacreg.h>
540b94a66eSMike Smith #include <dev/aac/aac_ioctl.h>
5535863739SMike Smith #include <dev/aac/aacvar.h>
5635863739SMike Smith #include <dev/aac/aac_tables.h>
5735863739SMike Smith 
5835863739SMike Smith devclass_t	aac_devclass;
5935863739SMike Smith 
6035863739SMike Smith static void	aac_startup(void *arg);
6135863739SMike Smith 
6235863739SMike Smith /* Command Processing */
6335863739SMike Smith static void	aac_startio(struct aac_softc *sc);
640b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
6535863739SMike Smith static int	aac_start(struct aac_command *cm);
6635863739SMike Smith static void	aac_complete(void *context, int pending);
6735863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
6835863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
6935863739SMike Smith static int	aac_wait_command(struct aac_command *cm, int timeout);
7035863739SMike Smith static void	aac_host_command(struct aac_softc *sc);
7135863739SMike Smith static void	aac_host_response(struct aac_softc *sc);
7235863739SMike Smith 
7335863739SMike Smith /* Command Buffer Management */
7435863739SMike Smith static int	aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp);
7535863739SMike Smith static void	aac_release_command(struct aac_command *cm);
760b94a66eSMike Smith static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
770b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
780b94a66eSMike Smith static void	aac_free_commands(struct aac_softc *sc);
7935863739SMike Smith static void	aac_map_command(struct aac_command *cm);
8035863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
8135863739SMike Smith 
8235863739SMike Smith /* Hardware Interface */
8335863739SMike Smith static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error);
8435863739SMike Smith static int	aac_init(struct aac_softc *sc);
8535863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
8635863739SMike Smith 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
8735863739SMike Smith 				 u_int32_t *sp);
8835863739SMike Smith static int	aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
8935863739SMike Smith 			     void *data, u_int16_t datasize,
9035863739SMike Smith 			     void *result, u_int16_t *resultsize);
9135863739SMike Smith static int	aac_enqueue_fib(struct aac_softc *sc, int queue, u_int32_t fib_size, u_int32_t fib_addr);
9235863739SMike Smith static int	aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, struct aac_fib **fib_addr);
9335863739SMike Smith 
9435863739SMike Smith /* StrongARM interface */
9535863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
9635863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
9735863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
9835863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
9935863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
10035863739SMike Smith 				   u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3);
10135863739SMike Smith static int	aac_sa_get_mailboxstatus(struct aac_softc *sc);
10235863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
10335863739SMike Smith 
10435863739SMike Smith struct aac_interface aac_sa_interface = {
10535863739SMike Smith     aac_sa_get_fwstatus,
10635863739SMike Smith     aac_sa_qnotify,
10735863739SMike Smith     aac_sa_get_istatus,
10835863739SMike Smith     aac_sa_clear_istatus,
10935863739SMike Smith     aac_sa_set_mailbox,
11035863739SMike Smith     aac_sa_get_mailboxstatus,
11135863739SMike Smith     aac_sa_set_interrupts
11235863739SMike Smith };
11335863739SMike Smith 
11435863739SMike Smith /* i960Rx interface */
11535863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
11635863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
11735863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
11835863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
11935863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
12035863739SMike Smith 				   u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3);
12135863739SMike Smith static int	aac_rx_get_mailboxstatus(struct aac_softc *sc);
12235863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
12335863739SMike Smith 
12435863739SMike Smith struct aac_interface aac_rx_interface = {
12535863739SMike Smith     aac_rx_get_fwstatus,
12635863739SMike Smith     aac_rx_qnotify,
12735863739SMike Smith     aac_rx_get_istatus,
12835863739SMike Smith     aac_rx_clear_istatus,
12935863739SMike Smith     aac_rx_set_mailbox,
13035863739SMike Smith     aac_rx_get_mailboxstatus,
13135863739SMike Smith     aac_rx_set_interrupts
13235863739SMike Smith };
13335863739SMike Smith 
13435863739SMike Smith /* Debugging and Diagnostics */
13535863739SMike Smith static void	aac_describe_controller(struct aac_softc *sc);
13635863739SMike Smith static char	*aac_describe_code(struct aac_code_lookup *table, u_int32_t code);
13735863739SMike Smith 
13835863739SMike Smith /* Management Interface */
13935863739SMike Smith static d_open_t		aac_open;
14035863739SMike Smith static d_close_t	aac_close;
14135863739SMike Smith static d_ioctl_t	aac_ioctl;
14235863739SMike Smith static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
14335863739SMike Smith static void		aac_handle_aif(struct aac_softc *sc, struct aac_aif_command *aif);
14435863739SMike Smith #ifdef AAC_COMPAT_LINUX
14535863739SMike Smith static int		aac_linux_rev_check(struct aac_softc *sc, caddr_t udata);
14635863739SMike Smith static int		aac_linux_getnext_aif(struct aac_softc *sc, caddr_t arg);
1470b94a66eSMike Smith static int		aac_linux_return_aif(struct aac_softc *sc, caddr_t uptr);
14835863739SMike Smith #endif
14935863739SMike Smith 
15035863739SMike Smith #define AAC_CDEV_MAJOR	150
15135863739SMike Smith 
15235863739SMike Smith static struct cdevsw aac_cdevsw = {
15335863739SMike Smith     aac_open,		/* open */
15435863739SMike Smith     aac_close,		/* close */
15535863739SMike Smith     noread,		/* read */
15635863739SMike Smith     nowrite,		/* write */
15735863739SMike Smith     aac_ioctl,		/* ioctl */
15835863739SMike Smith     nopoll,		/* poll */
15935863739SMike Smith     nommap,		/* mmap */
16035863739SMike Smith     nostrategy,		/* strategy */
16135863739SMike Smith     "aac",		/* name */
16235863739SMike Smith     AAC_CDEV_MAJOR,	/* major */
16335863739SMike Smith     nodump,		/* dump */
16435863739SMike Smith     nopsize,		/* psize */
16535863739SMike Smith     0,			/* flags */
16635863739SMike Smith     -1,			/* bmaj */
16735863739SMike Smith };
16835863739SMike Smith 
16935863739SMike Smith /********************************************************************************
17035863739SMike Smith  ********************************************************************************
17135863739SMike Smith                                                                  Device Interface
17235863739SMike Smith  ********************************************************************************
17335863739SMike Smith  ********************************************************************************/
17435863739SMike Smith 
17535863739SMike Smith /********************************************************************************
17635863739SMike Smith  * Initialise the controller and softc
17735863739SMike Smith  */
17835863739SMike Smith int
17935863739SMike Smith aac_attach(struct aac_softc *sc)
18035863739SMike Smith {
18135863739SMike Smith     int		error, unit;
18235863739SMike Smith 
18335863739SMike Smith     debug_called(1);
18435863739SMike Smith 
18535863739SMike Smith     /*
18635863739SMike Smith      * Initialise per-controller queues.
18735863739SMike Smith      */
1880b94a66eSMike Smith     aac_initq_free(sc);
1890b94a66eSMike Smith     aac_initq_ready(sc);
1900b94a66eSMike Smith     aac_initq_busy(sc);
1910b94a66eSMike Smith     aac_initq_complete(sc);
1920b94a66eSMike Smith     aac_initq_bio(sc);
19335863739SMike Smith 
19435863739SMike Smith #if __FreeBSD_version >= 500005
19535863739SMike Smith     /*
19635863739SMike Smith      * Initialise command-completion task.
19735863739SMike Smith      */
19835863739SMike Smith     TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
19935863739SMike Smith #endif
20035863739SMike Smith 
20135863739SMike Smith     /* disable interrupts before we enable anything */
20235863739SMike Smith     AAC_MASK_INTERRUPTS(sc);
20335863739SMike Smith 
20435863739SMike Smith     /* mark controller as suspended until we get ourselves organised */
20535863739SMike Smith     sc->aac_state |= AAC_STATE_SUSPEND;
20635863739SMike Smith 
20735863739SMike Smith     /*
2080b94a66eSMike Smith      * Allocate command structures.
2090b94a66eSMike Smith      */
2100b94a66eSMike Smith     if ((error = aac_alloc_commands(sc)) != 0)
2110b94a66eSMike Smith 	return(error);
2120b94a66eSMike Smith 
2130b94a66eSMike Smith     /*
21435863739SMike Smith      * Initialise the adapter.
21535863739SMike Smith      */
2160b94a66eSMike Smith     if ((error = aac_init(sc)) != 0)
21735863739SMike Smith 	return(error);
21835863739SMike Smith 
21935863739SMike Smith     /*
22035863739SMike Smith      * Print a little information about the controller.
22135863739SMike Smith      */
22235863739SMike Smith     aac_describe_controller(sc);
22335863739SMike Smith 
22435863739SMike Smith     /*
22535863739SMike Smith      * Register to probe our containers later.
22635863739SMike Smith      */
22735863739SMike Smith     sc->aac_ich.ich_func = aac_startup;
22835863739SMike Smith     sc->aac_ich.ich_arg = sc;
22935863739SMike Smith     if (config_intrhook_establish(&sc->aac_ich) != 0) {
23035863739SMike Smith         device_printf(sc->aac_dev, "can't establish configuration hook\n");
23135863739SMike Smith         return(ENXIO);
23235863739SMike Smith     }
23335863739SMike Smith 
23435863739SMike Smith     /*
23535863739SMike Smith      * Make the control device.
23635863739SMike Smith      */
23735863739SMike Smith     unit = device_get_unit(sc->aac_dev);
23835863739SMike Smith     sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_WHEEL, 0644, "aac%d", unit);
23935863739SMike Smith     sc->aac_dev_t->si_drv1 = sc;
24035863739SMike Smith 
24135863739SMike Smith     return(0);
24235863739SMike Smith }
24335863739SMike Smith 
24435863739SMike Smith /********************************************************************************
24535863739SMike Smith  * Probe for containers, create disks.
24635863739SMike Smith  */
24735863739SMike Smith static void
24835863739SMike Smith aac_startup(void *arg)
24935863739SMike Smith {
25035863739SMike Smith     struct aac_softc		*sc = (struct aac_softc *)arg;
25135863739SMike Smith     struct aac_mntinfo		mi;
25235863739SMike Smith     struct aac_mntinforesponse	mir;
25335863739SMike Smith     device_t			child;
25435863739SMike Smith     u_int16_t			rsize;
25535863739SMike Smith     int				i;
25635863739SMike Smith 
25735863739SMike Smith     debug_called(1);
25835863739SMike Smith 
25935863739SMike Smith     /* disconnect ourselves from the intrhook chain */
26035863739SMike Smith     config_intrhook_disestablish(&sc->aac_ich);
26135863739SMike Smith 
26235863739SMike Smith     /* loop over possible containers */
26335863739SMike Smith     mi.Command = VM_NameServe;
26435863739SMike Smith     mi.MntType = FT_FILESYS;
26535863739SMike Smith     for (i = 0; i < AAC_MAX_CONTAINERS; i++) {
26635863739SMike Smith 	/* request information on this container */
26735863739SMike Smith 	mi.MntCount = i;
26835863739SMike Smith 	if (aac_sync_fib(sc, ContainerCommand, 0, &mi, sizeof(struct aac_mntinfo), &mir, &rsize)) {
26935863739SMike Smith 	    debug(2, "error probing container %d", i);
27035863739SMike Smith 	    continue;
27135863739SMike Smith 	}
27235863739SMike Smith 	/* check response size */
27335863739SMike Smith 	if (rsize != sizeof(mir)) {
274bb9f4664SScott Long 	    debug(2, "container info response wrong size (%d should be %d)", rsize, sizeof(mir));
27535863739SMike Smith 	    continue;
27635863739SMike Smith 	}
27735863739SMike Smith 	/*
27835863739SMike Smith 	 * Check container volume type for validity.  Note that many of the possible types
27935863739SMike Smith 	 * may never show up.
28035863739SMike Smith 	 */
28135863739SMike Smith 	if ((mir.Status == ST_OK) && (mir.MntTable[0].VolType != CT_NONE)) {
28235863739SMike Smith 	    debug(1, "%d: id %x  name '%.16s'  size %u  type %d",
28335863739SMike Smith 		  i, mir.MntTable[0].ObjectId,
28435863739SMike Smith 		  mir.MntTable[0].FileSystemName, mir.MntTable[0].Capacity,
28535863739SMike Smith 		  mir.MntTable[0].VolType);
28635863739SMike Smith 
28735863739SMike Smith 	    if ((child = device_add_child(sc->aac_dev, NULL, -1)) == NULL) {
28835863739SMike Smith 		device_printf(sc->aac_dev, "device_add_child failed\n");
28935863739SMike Smith 	    } else {
29035863739SMike Smith 		device_set_ivars(child, &sc->aac_container[i]);
29135863739SMike Smith 	    }
29235863739SMike Smith 	    device_set_desc(child, aac_describe_code(aac_container_types, mir.MntTable[0].VolType));
29335863739SMike Smith 	    sc->aac_container[i].co_disk = child;
29435863739SMike Smith 	    sc->aac_container[i].co_mntobj = mir.MntTable[0];
29535863739SMike Smith 	}
29635863739SMike Smith     }
29735863739SMike Smith 
29835863739SMike Smith     /* poke the bus to actually attach the child devices */
29935863739SMike Smith     if (bus_generic_attach(sc->aac_dev))
30035863739SMike Smith 	device_printf(sc->aac_dev, "bus_generic_attach failed\n");
30135863739SMike Smith 
30235863739SMike Smith     /* mark the controller up */
30335863739SMike Smith     sc->aac_state &= ~AAC_STATE_SUSPEND;
30435863739SMike Smith 
30535863739SMike Smith     /* enable interrupts now */
30635863739SMike Smith     AAC_UNMASK_INTERRUPTS(sc);
3070b94a66eSMike Smith 
3080b94a66eSMike Smith     /* enable the timeout watchdog */
3090b94a66eSMike Smith     timeout((timeout_t*)aac_timeout, sc, AAC_PERIODIC_INTERVAL * hz);
31035863739SMike Smith }
31135863739SMike Smith 
31235863739SMike Smith /********************************************************************************
31335863739SMike Smith  * Free all of the resources associated with (sc)
31435863739SMike Smith  *
31535863739SMike Smith  * Should not be called if the controller is active.
31635863739SMike Smith  */
31735863739SMike Smith void
31835863739SMike Smith aac_free(struct aac_softc *sc)
31935863739SMike Smith {
32035863739SMike Smith     debug_called(1);
32135863739SMike Smith 
32235863739SMike Smith     /* remove the control device */
32335863739SMike Smith     if (sc->aac_dev_t != NULL)
32435863739SMike Smith 	destroy_dev(sc->aac_dev_t);
32535863739SMike Smith 
3260b94a66eSMike Smith     /* throw away any FIB buffers, discard the FIB DMA tag */
3270b94a66eSMike Smith     if (sc->aac_fibs != NULL)
3280b94a66eSMike Smith 	aac_free_commands(sc);
3290b94a66eSMike Smith     if (sc->aac_fib_dmat)
3300b94a66eSMike Smith 	bus_dma_tag_destroy(sc->aac_fib_dmat);
33135863739SMike Smith 
33235863739SMike Smith     /* destroy the common area */
33335863739SMike Smith     if (sc->aac_common) {
33435863739SMike Smith 	bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
33535863739SMike Smith 	bus_dmamem_free(sc->aac_common_dmat, sc->aac_common, sc->aac_common_dmamap);
33635863739SMike Smith     }
3370b94a66eSMike Smith     if (sc->aac_common_dmat)
3380b94a66eSMike Smith 	bus_dma_tag_destroy(sc->aac_common_dmat);
33935863739SMike Smith 
34035863739SMike Smith     /* disconnect the interrupt handler */
34135863739SMike Smith     if (sc->aac_intr)
34235863739SMike Smith 	bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
34335863739SMike Smith     if (sc->aac_irq != NULL)
34435863739SMike Smith 	bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid, sc->aac_irq);
34535863739SMike Smith 
34635863739SMike Smith     /* destroy data-transfer DMA tag */
34735863739SMike Smith     if (sc->aac_buffer_dmat)
34835863739SMike Smith 	bus_dma_tag_destroy(sc->aac_buffer_dmat);
34935863739SMike Smith 
35035863739SMike Smith     /* destroy the parent DMA tag */
35135863739SMike Smith     if (sc->aac_parent_dmat)
35235863739SMike Smith 	bus_dma_tag_destroy(sc->aac_parent_dmat);
35335863739SMike Smith 
35435863739SMike Smith     /* release the register window mapping */
35535863739SMike Smith     if (sc->aac_regs_resource != NULL)
35635863739SMike Smith 	bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, sc->aac_regs_rid, sc->aac_regs_resource);
35735863739SMike Smith }
35835863739SMike Smith 
35935863739SMike Smith /********************************************************************************
36035863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
36135863739SMike Smith  */
36235863739SMike Smith int
36335863739SMike Smith aac_detach(device_t dev)
36435863739SMike Smith {
36535863739SMike Smith     struct aac_softc	*sc = device_get_softc(dev);
36635863739SMike Smith     int			error;
36735863739SMike Smith 
36835863739SMike Smith     debug_called(1);
36935863739SMike Smith 
37035863739SMike Smith     if (sc->aac_state & AAC_STATE_OPEN)
37135863739SMike Smith 	return(EBUSY);
37235863739SMike Smith 
37335863739SMike Smith     if ((error = aac_shutdown(dev)))
37435863739SMike Smith 	return(error);
37535863739SMike Smith 
37635863739SMike Smith     aac_free(sc);
37735863739SMike Smith 
37835863739SMike Smith     return(0);
37935863739SMike Smith }
38035863739SMike Smith 
38135863739SMike Smith /********************************************************************************
38235863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
38335863739SMike Smith  *
38435863739SMike Smith  * This function is called before detach or system shutdown.
38535863739SMike Smith  *
3860b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
38735863739SMike Smith  * allow shutdown if any device is open.
38835863739SMike Smith  */
38935863739SMike Smith int
39035863739SMike Smith aac_shutdown(device_t dev)
39135863739SMike Smith {
39235863739SMike Smith     struct aac_softc		*sc = device_get_softc(dev);
39335863739SMike Smith     struct aac_close_command	cc;
39435863739SMike Smith     int				s, i;
39535863739SMike Smith 
39635863739SMike Smith     debug_called(1);
39735863739SMike Smith 
39835863739SMike Smith     s = splbio();
39935863739SMike Smith 
40035863739SMike Smith     sc->aac_state |= AAC_STATE_SUSPEND;
40135863739SMike Smith 
40235863739SMike Smith     /*
40335863739SMike Smith      * Send a Container shutdown followed by a HostShutdown FIB to the
40435863739SMike Smith      * controller to convince it that we don't want to talk to it anymore.
40535863739SMike Smith      * We've been closed and all I/O completed already
40635863739SMike Smith      */
40735863739SMike Smith     device_printf(sc->aac_dev, "shutting down controller...");
40835863739SMike Smith 
40935863739SMike Smith     cc.Command = VM_CloseAll;
41035863739SMike Smith     cc.ContainerId = 0xffffffff;
41135863739SMike Smith     if (aac_sync_fib(sc, ContainerCommand, 0, &cc, sizeof(cc), NULL, NULL)) {
41235863739SMike Smith 	printf("FAILED.\n");
41335863739SMike Smith     } else {
41435863739SMike Smith 	i = 0;
41535863739SMike Smith 	if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN, &i, sizeof(i), NULL, NULL)) {
41635863739SMike Smith 	    printf("FAILED.\n");
41735863739SMike Smith 	} else {
41835863739SMike Smith 	    printf("done.\n");
41935863739SMike Smith 	}
42035863739SMike Smith     }
42135863739SMike Smith 
42235863739SMike Smith     AAC_MASK_INTERRUPTS(sc);
42335863739SMike Smith 
42435863739SMike Smith     splx(s);
42535863739SMike Smith     return(0);
42635863739SMike Smith }
42735863739SMike Smith 
42835863739SMike Smith /********************************************************************************
42935863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
43035863739SMike Smith  */
43135863739SMike Smith int
43235863739SMike Smith aac_suspend(device_t dev)
43335863739SMike Smith {
43435863739SMike Smith     struct aac_softc	*sc = device_get_softc(dev);
43535863739SMike Smith     int			s;
43635863739SMike Smith 
43735863739SMike Smith     debug_called(1);
43835863739SMike Smith     s = splbio();
43935863739SMike Smith 
44035863739SMike Smith     sc->aac_state |= AAC_STATE_SUSPEND;
44135863739SMike Smith 
44235863739SMike Smith     AAC_MASK_INTERRUPTS(sc);
44335863739SMike Smith     splx(s);
44435863739SMike Smith     return(0);
44535863739SMike Smith }
44635863739SMike Smith 
44735863739SMike Smith /********************************************************************************
44835863739SMike Smith  * Bring the controller back to a state ready for operation.
44935863739SMike Smith  */
45035863739SMike Smith int
45135863739SMike Smith aac_resume(device_t dev)
45235863739SMike Smith {
45335863739SMike Smith     struct aac_softc	*sc = device_get_softc(dev);
45435863739SMike Smith 
45535863739SMike Smith     debug_called(1);
45635863739SMike Smith     sc->aac_state &= ~AAC_STATE_SUSPEND;
45735863739SMike Smith     AAC_UNMASK_INTERRUPTS(sc);
45835863739SMike Smith     return(0);
45935863739SMike Smith }
46035863739SMike Smith 
46135863739SMike Smith /*******************************************************************************
46235863739SMike Smith  * Take an interrupt.
46335863739SMike Smith  */
46435863739SMike Smith void
46535863739SMike Smith aac_intr(void *arg)
46635863739SMike Smith {
46735863739SMike Smith     struct aac_softc	*sc = (struct aac_softc *)arg;
46835863739SMike Smith     u_int16_t		reason;
46935863739SMike Smith 
47035863739SMike Smith     debug_called(2);
47135863739SMike Smith 
47235863739SMike Smith     reason = AAC_GET_ISTATUS(sc);
47335863739SMike Smith 
47435863739SMike Smith     /* controller wants to talk to the log?  XXX should we defer this? */
47535863739SMike Smith     if (reason & AAC_DB_PRINTF) {
47635863739SMike Smith 	if (sc->aac_common->ac_printf[0]) {
47735863739SMike Smith 	    device_printf(sc->aac_dev, "** %.*s", AAC_PRINTF_BUFSIZE, sc->aac_common->ac_printf);
47835863739SMike Smith 	    sc->aac_common->ac_printf[0] = 0;
47935863739SMike Smith 	}
48035863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_PRINTF);
48135863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_PRINTF);
48235863739SMike Smith     }
48335863739SMike Smith 
48435863739SMike Smith     /* controller has a message for us? */
48535863739SMike Smith     if (reason & AAC_DB_COMMAND_READY) {
48635863739SMike Smith 	aac_host_command(sc);
48735863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_COMMAND_READY);
48835863739SMike Smith     }
48935863739SMike Smith 
49035863739SMike Smith     /* controller has a response for us? */
49135863739SMike Smith     if (reason & AAC_DB_RESPONSE_READY) {
49235863739SMike Smith 	aac_host_response(sc);
49335863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY);
49435863739SMike Smith     }
49535863739SMike Smith 
49635863739SMike Smith     /* spurious interrupts that we don't use - reset the mask and clear the interrupts */
49735863739SMike Smith     if (reason & (AAC_DB_COMMAND_NOT_FULL | AAC_DB_RESPONSE_NOT_FULL)) {
49835863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
49935863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_COMMAND_NOT_FULL | AAC_DB_RESPONSE_NOT_FULL);
50035863739SMike Smith     }
50135863739SMike Smith };
50235863739SMike Smith 
50335863739SMike Smith /********************************************************************************
50435863739SMike Smith  ********************************************************************************
50535863739SMike Smith                                                                Command Processing
50635863739SMike Smith  ********************************************************************************
50735863739SMike Smith  ********************************************************************************/
50835863739SMike Smith 
50935863739SMike Smith /********************************************************************************
51035863739SMike Smith  * Start as much queued I/O as possible on the controller
51135863739SMike Smith  */
51235863739SMike Smith static void
51335863739SMike Smith aac_startio(struct aac_softc *sc)
51435863739SMike Smith {
51535863739SMike Smith     struct aac_command	*cm;
51635863739SMike Smith 
51735863739SMike Smith     debug_called(2);
51835863739SMike Smith 
51935863739SMike Smith     for(;;) {
52035863739SMike Smith 	/* try to get a command that's been put off for lack of resources */
52135863739SMike Smith 	cm = aac_dequeue_ready(sc);
52235863739SMike Smith 
52335863739SMike Smith 	/* try to build a command off the bio queue (ignore error return) */
5240b94a66eSMike Smith 	if (cm == NULL)
52535863739SMike Smith 	    aac_bio_command(sc, &cm);
52635863739SMike Smith 
52735863739SMike Smith 	/* nothing to do? */
52835863739SMike Smith 	if (cm == NULL)
52935863739SMike Smith 	    break;
53035863739SMike Smith 
53135863739SMike Smith 	/* try to give the command to the controller */
53235863739SMike Smith 	if (aac_start(cm) == EBUSY) {
53335863739SMike Smith 	    /* put it on the ready queue for later */
53435863739SMike Smith 	    aac_requeue_ready(cm);
53535863739SMike Smith 	    break;
53635863739SMike Smith 	}
53735863739SMike Smith     }
53835863739SMike Smith }
53935863739SMike Smith 
54035863739SMike Smith /********************************************************************************
54135863739SMike Smith  * Deliver a command to the controller; allocate controller resources at the
54235863739SMike Smith  * last moment when possible.
54335863739SMike Smith  */
54435863739SMike Smith static int
54535863739SMike Smith aac_start(struct aac_command *cm)
54635863739SMike Smith {
54735863739SMike Smith     struct aac_softc	*sc = cm->cm_sc;
5480b94a66eSMike Smith     int			s, error;
54935863739SMike Smith 
55035863739SMike Smith     debug_called(2);
55135863739SMike Smith 
55235863739SMike Smith     /* get the command mapped */
55335863739SMike Smith     aac_map_command(cm);
55435863739SMike Smith 
5550b94a66eSMike Smith     /* fix up the address values in the FIB */
55635863739SMike Smith     cm->cm_fib->Header.SenderFibAddress = (u_int32_t)cm->cm_fib;
55735863739SMike Smith     cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
55835863739SMike Smith 
55935863739SMike Smith     /* save a pointer to the command for speedy reverse-lookup */
5600b94a66eSMike Smith     cm->cm_fib->Header.SenderData = (u_int32_t)cm;	/* XXX 64-bit physical address issue */
56135863739SMike Smith 
56235863739SMike Smith     /* put the FIB on the outbound queue */
5630b94a66eSMike Smith     s = splbio();
56435863739SMike Smith     if (aac_enqueue_fib(sc, AAC_ADAP_NORM_CMD_QUEUE, cm->cm_fib->Header.Size,
5650b94a66eSMike Smith 			cm->cm_fib->Header.ReceiverFibAddress)) {
5660b94a66eSMike Smith 	error = EBUSY;
5670b94a66eSMike Smith     } else {
5680b94a66eSMike Smith 	aac_enqueue_busy(cm);
5690b94a66eSMike Smith 	error = 0;
5700b94a66eSMike Smith     }
5710b94a66eSMike Smith     return(error);
57235863739SMike Smith }
57335863739SMike Smith 
57435863739SMike Smith /********************************************************************************
57535863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
57635863739SMike Smith  */
57735863739SMike Smith static void
57835863739SMike Smith aac_host_command(struct aac_softc *sc)
57935863739SMike Smith {
58035863739SMike Smith     struct aac_fib	*fib;
58135863739SMike Smith     u_int32_t		fib_size;
58235863739SMike Smith 
58335863739SMike Smith     debug_called(1);
58435863739SMike Smith 
58535863739SMike Smith     for (;;) {
58635863739SMike Smith 	if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE, &fib_size, &fib))
58735863739SMike Smith 	    break;	/* nothing to do */
58835863739SMike Smith 
58935863739SMike Smith 	switch(fib->Header.Command) {
59035863739SMike Smith 	case AifRequest:
59135863739SMike Smith 	    aac_handle_aif(sc, (struct aac_aif_command *)&fib->data[0]);
59235863739SMike Smith 	    break;
59335863739SMike Smith 	default:
59435863739SMike Smith 	    device_printf(sc->aac_dev, "unknown command from controller\n");
59535863739SMike Smith 	    AAC_PRINT_FIB(sc, fib);
59635863739SMike Smith 	    break;
59735863739SMike Smith 	}
59835863739SMike Smith 
59935863739SMike Smith 	/* XXX reply to FIBs requesting responses ?? */
60035863739SMike Smith 	/* XXX how do we return these FIBs to the controller? */
60135863739SMike Smith     }
60235863739SMike Smith }
60335863739SMike Smith 
60435863739SMike Smith /********************************************************************************
60535863739SMike Smith  * Handle notification of one or more FIBs completed by the controller
60635863739SMike Smith  */
60735863739SMike Smith static void
60835863739SMike Smith aac_host_response(struct aac_softc *sc)
60935863739SMike Smith {
61035863739SMike Smith     struct aac_command	*cm;
61135863739SMike Smith     struct aac_fib	*fib;
61235863739SMike Smith     u_int32_t		fib_size;
61335863739SMike Smith 
61435863739SMike Smith     debug_called(2);
61535863739SMike Smith 
61635863739SMike Smith     for (;;) {
61735863739SMike Smith 	/* look for completed FIBs on our queue */
61835863739SMike Smith 	if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size, &fib))
61935863739SMike Smith 	    break;	/* nothing to do */
62035863739SMike Smith 
62135863739SMike Smith 	/* get the command, unmap and queue for later processing */
62235863739SMike Smith 	cm = (struct aac_command *)fib->Header.SenderData;
62335863739SMike Smith 	if (cm == NULL) {
62435863739SMike Smith 	    AAC_PRINT_FIB(sc, fib);
62535863739SMike Smith 	} else {
6260b94a66eSMike Smith 	    aac_remove_busy(cm);
62735863739SMike Smith 	    aac_unmap_command(cm);		/* XXX defer? */
6280b94a66eSMike Smith 	    aac_enqueue_complete(cm);
62935863739SMike Smith 	}
63035863739SMike Smith     }
63135863739SMike Smith 
63235863739SMike Smith     /* handle completion processing */
63335863739SMike Smith #if __FreeBSD_version >= 500005
63435863739SMike Smith     taskqueue_enqueue(taskqueue_swi, &sc->aac_task_complete);
63535863739SMike Smith #else
63635863739SMike Smith     aac_complete(sc, 0);
63735863739SMike Smith #endif
63835863739SMike Smith }
63935863739SMike Smith 
64035863739SMike Smith /********************************************************************************
64135863739SMike Smith  * Process completed commands.
64235863739SMike Smith  */
64335863739SMike Smith static void
64435863739SMike Smith aac_complete(void *context, int pending)
64535863739SMike Smith {
64635863739SMike Smith     struct aac_softc	*sc = (struct aac_softc *)context;
64735863739SMike Smith     struct aac_command	*cm;
64835863739SMike Smith 
64935863739SMike Smith     debug_called(2);
65035863739SMike Smith 
65135863739SMike Smith     /* pull completed commands off the queue */
65235863739SMike Smith     for (;;) {
6530b94a66eSMike Smith 	cm = aac_dequeue_complete(sc);
65435863739SMike Smith 	if (cm == NULL)
6550b94a66eSMike Smith 	    break;
65635863739SMike Smith 	cm->cm_flags |= AAC_CMD_COMPLETED;
65735863739SMike Smith 
65835863739SMike Smith 	/* is there a completion handler? */
65935863739SMike Smith 	if (cm->cm_complete != NULL) {
66035863739SMike Smith 	    cm->cm_complete(cm);
66135863739SMike Smith 	} else {
66235863739SMike Smith 	    /* assume that someone is sleeping on this command */
66335863739SMike Smith 	    wakeup(cm);
66435863739SMike Smith 	}
66535863739SMike Smith     }
6660b94a66eSMike Smith 
6670b94a66eSMike Smith     /* see if we can start some more I/O */
6680b94a66eSMike Smith     aac_startio(sc);
66935863739SMike Smith }
67035863739SMike Smith 
67135863739SMike Smith /********************************************************************************
67235863739SMike Smith  * Handle a bio submitted from a disk device.
67335863739SMike Smith  */
67435863739SMike Smith void
67535863739SMike Smith aac_submit_bio(struct bio *bp)
67635863739SMike Smith {
67735863739SMike Smith     struct aac_disk	*ad = (struct aac_disk *)bp->bio_dev->si_drv1;
67835863739SMike Smith     struct aac_softc	*sc = ad->ad_controller;
67935863739SMike Smith 
68035863739SMike Smith     debug_called(2);
68135863739SMike Smith 
68235863739SMike Smith     /* queue the BIO and try to get some work done */
6830b94a66eSMike Smith     aac_enqueue_bio(sc, bp);
68435863739SMike Smith     aac_startio(sc);
68535863739SMike Smith }
68635863739SMike Smith 
68735863739SMike Smith /********************************************************************************
68835863739SMike Smith  * Get a bio and build a command to go with it.
68935863739SMike Smith  */
69035863739SMike Smith static int
69135863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
69235863739SMike Smith {
69335863739SMike Smith     struct aac_command		*cm;
69435863739SMike Smith     struct aac_fib		*fib;
69535863739SMike Smith     struct aac_blockread	*br;
69635863739SMike Smith     struct aac_blockwrite	*bw;
69735863739SMike Smith     struct aac_disk		*ad;
69835863739SMike Smith     struct bio			*bp;
69935863739SMike Smith 
70035863739SMike Smith     debug_called(2);
70135863739SMike Smith 
70235863739SMike Smith     /* get the resources we will need */
70335863739SMike Smith     cm = NULL;
7040b94a66eSMike Smith     if ((bp = aac_dequeue_bio(sc)) == NULL)
70535863739SMike Smith 	goto fail;
70635863739SMike Smith     if (aac_alloc_command(sc, &cm))	/* get a command */
70735863739SMike Smith 	goto fail;
70835863739SMike Smith 
70935863739SMike Smith     /* fill out the command */
7100b94a66eSMike Smith     cm->cm_data = (void *)bp->bio_data;
7110b94a66eSMike Smith     cm->cm_datalen = bp->bio_bcount;
7120b94a66eSMike Smith     cm->cm_complete = aac_bio_complete;
71335863739SMike Smith     cm->cm_private = bp;
7140b94a66eSMike Smith     cm->cm_timestamp = time_second;
71535863739SMike Smith 
71635863739SMike Smith     /* build the FIB */
71735863739SMike Smith     fib = cm->cm_fib;
71835863739SMike Smith     fib->Header.XferState =
71935863739SMike Smith 	AAC_FIBSTATE_HOSTOWNED   |
72035863739SMike Smith 	AAC_FIBSTATE_INITIALISED |
72135863739SMike Smith 	AAC_FIBSTATE_FROMHOST    |
72235863739SMike Smith 	AAC_FIBSTATE_REXPECTED   |
72335863739SMike Smith 	AAC_FIBSTATE_NORM;
72435863739SMike Smith     fib->Header.Command = ContainerCommand;
72535863739SMike Smith     fib->Header.Size = sizeof(struct aac_fib_header);
72635863739SMike Smith 
72735863739SMike Smith     /* build the read/write request */
72835863739SMike Smith     ad = (struct aac_disk *)bp->bio_dev->si_drv1;
72935863739SMike Smith     if (BIO_IS_READ(bp)) {
73035863739SMike Smith 	br = (struct aac_blockread *)&fib->data[0];
73135863739SMike Smith 	br->Command = VM_CtBlockRead;
73235863739SMike Smith 	br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
73335863739SMike Smith 	br->BlockNumber = bp->bio_pblkno;
73435863739SMike Smith 	br->ByteCount = bp->bio_bcount;
73535863739SMike Smith 	fib->Header.Size += sizeof(struct aac_blockread);
73635863739SMike Smith 	cm->cm_sgtable = &br->SgMap;
73735863739SMike Smith 	cm->cm_flags |= AAC_CMD_DATAIN;
73835863739SMike Smith     } else {
73935863739SMike Smith 	bw = (struct aac_blockwrite *)&fib->data[0];
74035863739SMike Smith 	bw->Command = VM_CtBlockWrite;
74135863739SMike Smith 	bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
74235863739SMike Smith 	bw->BlockNumber = bp->bio_pblkno;
74335863739SMike Smith 	bw->ByteCount = bp->bio_bcount;
74435863739SMike Smith 	bw->Stable = CUNSTABLE;		/* XXX what's appropriate here? */
74535863739SMike Smith 	fib->Header.Size += sizeof(struct aac_blockwrite);
74635863739SMike Smith 	cm->cm_flags |= AAC_CMD_DATAOUT;
74735863739SMike Smith 	cm->cm_sgtable = &bw->SgMap;
74835863739SMike Smith     }
74935863739SMike Smith 
75035863739SMike Smith     *cmp = cm;
75135863739SMike Smith     return(0);
75235863739SMike Smith 
75335863739SMike Smith fail:
75435863739SMike Smith     if (bp != NULL)
7550b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
75635863739SMike Smith     if (cm != NULL)
75735863739SMike Smith 	aac_release_command(cm);
75835863739SMike Smith     return(ENOMEM);
75935863739SMike Smith }
76035863739SMike Smith 
76135863739SMike Smith /********************************************************************************
76235863739SMike Smith  * Handle a bio-instigated command that has been completed.
76335863739SMike Smith  */
76435863739SMike Smith static void
76535863739SMike Smith aac_bio_complete(struct aac_command *cm)
76635863739SMike Smith {
76735863739SMike Smith     struct aac_blockread_response	*brr;
76835863739SMike Smith     struct aac_blockwrite_response	*bwr;
76935863739SMike Smith     struct bio				*bp;
77035863739SMike Smith     AAC_FSAStatus			status;
77135863739SMike Smith 
77235863739SMike Smith     /* fetch relevant status and then release the command */
77335863739SMike Smith     bp = (struct bio *)cm->cm_private;
77435863739SMike Smith     if (BIO_IS_READ(bp)) {
77535863739SMike Smith 	brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
77635863739SMike Smith 	status = brr->Status;
77735863739SMike Smith     } else {
77835863739SMike Smith 	bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
77935863739SMike Smith 	status = bwr->Status;
78035863739SMike Smith     }
78135863739SMike Smith     aac_release_command(cm);
78235863739SMike Smith 
78335863739SMike Smith     /* fix up the bio based on status */
78435863739SMike Smith     if (status == ST_OK) {
78535863739SMike Smith 	bp->bio_resid = 0;
78635863739SMike Smith     } else {
78735863739SMike Smith 	bp->bio_error = EIO;
78835863739SMike Smith 	bp->bio_flags |= BIO_ERROR;
7890b94a66eSMike Smith 	/* pass an error string out to the disk layer */
7900b94a66eSMike Smith 	bp->bio_driver1 = aac_describe_code(aac_command_status_table, status);
79135863739SMike Smith     }
7920b94a66eSMike Smith     aac_biodone(bp);
79335863739SMike Smith }
79435863739SMike Smith 
79535863739SMike Smith /********************************************************************************
79635863739SMike Smith  * Submit a command to the controller, return when it completes.
79735863739SMike Smith  */
79835863739SMike Smith static int
79935863739SMike Smith aac_wait_command(struct aac_command *cm, int timeout)
80035863739SMike Smith {
80135863739SMike Smith     int s, error = 0;
80235863739SMike Smith 
80335863739SMike Smith     debug_called(2);
80435863739SMike Smith 
80535863739SMike Smith     /* Put the command on the ready queue and get things going */
80635863739SMike Smith     aac_enqueue_ready(cm);
80735863739SMike Smith     aac_startio(cm->cm_sc);
80835863739SMike Smith     s = splbio();
80935863739SMike Smith     while(!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) {
81035863739SMike Smith         error = tsleep(cm, PRIBIO, "aacwait", timeout * hz);
81135863739SMike Smith     }
81235863739SMike Smith     splx(s);
81335863739SMike Smith     return(error);
81435863739SMike Smith }
81535863739SMike Smith 
81635863739SMike Smith /********************************************************************************
81735863739SMike Smith  ********************************************************************************
81835863739SMike Smith                                                         Command Buffer Management
81935863739SMike Smith  ********************************************************************************
82035863739SMike Smith  ********************************************************************************/
82135863739SMike Smith 
82235863739SMike Smith /********************************************************************************
82335863739SMike Smith  * Allocate a command.
82435863739SMike Smith  */
82535863739SMike Smith static int
82635863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
82735863739SMike Smith {
82835863739SMike Smith     struct aac_command	*cm;
82935863739SMike Smith 
83035863739SMike Smith     debug_called(3);
83135863739SMike Smith 
8320b94a66eSMike Smith     if ((cm = aac_dequeue_free(sc)) == NULL)
83335863739SMike Smith 	return(ENOMEM);
83435863739SMike Smith 
8350b94a66eSMike Smith     *cmp = cm;
8360b94a66eSMike Smith     return(0);
8370b94a66eSMike Smith }
8380b94a66eSMike Smith 
8390b94a66eSMike Smith /********************************************************************************
8400b94a66eSMike Smith  * Release a command back to the freelist.
8410b94a66eSMike Smith  */
8420b94a66eSMike Smith static void
8430b94a66eSMike Smith aac_release_command(struct aac_command *cm)
8440b94a66eSMike Smith {
8450b94a66eSMike Smith     debug_called(3);
8460b94a66eSMike Smith 
8470b94a66eSMike Smith     /* (re)initialise the command/FIB */
84835863739SMike Smith     cm->cm_sgtable = NULL;
84935863739SMike Smith     cm->cm_flags = 0;
85035863739SMike Smith     cm->cm_complete = NULL;
85135863739SMike Smith     cm->cm_private = NULL;
85235863739SMike Smith     cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
85335863739SMike Smith     cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
85435863739SMike Smith     cm->cm_fib->Header.Flags = 0;
85535863739SMike Smith     cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
85635863739SMike Smith 
85735863739SMike Smith     /*
85835863739SMike Smith      * These are duplicated in aac_start to cover the case where an
85935863739SMike Smith      * intermediate stage may have destroyed them.  They're left
86035863739SMike Smith      * initialised here for debugging purposes only.
86135863739SMike Smith      */
86235863739SMike Smith     cm->cm_fib->Header.SenderFibAddress = (u_int32_t)cm->cm_fib;
86335863739SMike Smith     cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
86435863739SMike Smith 
86535863739SMike Smith     aac_enqueue_free(cm);
86635863739SMike Smith }
86735863739SMike Smith 
86835863739SMike Smith /********************************************************************************
8690b94a66eSMike Smith  * Map helper for command/FIB allocation.
87035863739SMike Smith  */
87135863739SMike Smith static void
8720b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
87335863739SMike Smith {
8740b94a66eSMike Smith     struct aac_softc	*sc = (struct aac_softc *)arg;
87535863739SMike Smith 
87635863739SMike Smith     debug_called(3);
87735863739SMike Smith 
8780b94a66eSMike Smith     sc->aac_fibphys = segs[0].ds_addr;
87935863739SMike Smith }
88035863739SMike Smith 
88135863739SMike Smith /********************************************************************************
8820b94a66eSMike Smith  * Allocate and initialise commands/FIBs for this adapter.
88335863739SMike Smith  */
8840b94a66eSMike Smith static int
8850b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
88635863739SMike Smith {
88735863739SMike Smith     struct aac_command		*cm;
88835863739SMike Smith     int				i;
88935863739SMike Smith 
89035863739SMike Smith     debug_called(1);
89135863739SMike Smith 
8920b94a66eSMike Smith     /* allocate the FIBs in DMAable memory and load them */
8930b94a66eSMike Smith     if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&sc->aac_fibs, BUS_DMA_NOWAIT, &sc->aac_fibmap)) {
8940b94a66eSMike Smith 	return(ENOMEM);
89535863739SMike Smith     }
8960b94a66eSMike Smith     bus_dmamap_load(sc->aac_fib_dmat, sc->aac_fibmap, sc->aac_fibs,
8970b94a66eSMike Smith 		    AAC_FIB_COUNT * sizeof(struct aac_fib), aac_map_command_helper, sc, 0);
89835863739SMike Smith 
8990b94a66eSMike Smith     /* initialise constant fields in the command structure */
9000b94a66eSMike Smith     for (i = 0; i < AAC_FIB_COUNT; i++) {
9010b94a66eSMike Smith 	cm = &sc->aac_command[i];
90235863739SMike Smith 	cm->cm_sc = sc;
9030b94a66eSMike Smith 	cm->cm_fib = sc->aac_fibs + i;
9040b94a66eSMike Smith 	cm->cm_fibphys = sc->aac_fibphys + (i * sizeof(struct aac_fib));
90535863739SMike Smith 
90635863739SMike Smith 	if (!bus_dmamap_create(sc->aac_buffer_dmat, 0, &cm->cm_datamap))
90735863739SMike Smith 	    aac_release_command(cm);
90835863739SMike Smith     }
9090b94a66eSMike Smith     return(0);
91035863739SMike Smith }
91135863739SMike Smith 
91235863739SMike Smith /********************************************************************************
9130b94a66eSMike Smith  * Free FIBs owned by this adapter.
91435863739SMike Smith  */
91535863739SMike Smith static void
9160b94a66eSMike Smith aac_free_commands(struct aac_softc *sc)
91735863739SMike Smith {
91835863739SMike Smith     int			i;
91935863739SMike Smith 
92035863739SMike Smith     debug_called(1);
92135863739SMike Smith 
9220b94a66eSMike Smith     for (i = 0; i < AAC_FIB_COUNT; i++)
9230b94a66eSMike Smith 	bus_dmamap_destroy(sc->aac_buffer_dmat, sc->aac_command[i].cm_datamap);
9240b94a66eSMike Smith     bus_dmamap_unload(sc->aac_fib_dmat, sc->aac_fibmap);
9250b94a66eSMike Smith     bus_dmamem_free(sc->aac_fib_dmat, sc->aac_fibs, sc->aac_fibmap);
92635863739SMike Smith }
92735863739SMike Smith 
92835863739SMike Smith /********************************************************************************
92935863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
93035863739SMike Smith  */
93135863739SMike Smith static void
93235863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
93335863739SMike Smith {
93435863739SMike Smith     struct aac_command		*cm = (struct aac_command *)arg;
93535863739SMike Smith     struct aac_fib		*fib = cm->cm_fib;
93635863739SMike Smith     struct aac_sg_table		*sg;
93735863739SMike Smith     int				i;
93835863739SMike Smith 
93935863739SMike Smith     debug_called(3);
94035863739SMike Smith 
94135863739SMike Smith     /* find the s/g table */
94235863739SMike Smith     sg = cm->cm_sgtable;
94335863739SMike Smith 
94435863739SMike Smith     /* copy into the FIB */
94535863739SMike Smith     if (sg != NULL) {
94635863739SMike Smith 	sg->SgCount = nseg;
94735863739SMike Smith 	for (i = 0; i < nseg; i++) {
94835863739SMike Smith 	    sg->SgEntry[i].SgAddress = segs[i].ds_addr;
94935863739SMike Smith 	    sg->SgEntry[i].SgByteCount = segs[i].ds_len;
95035863739SMike Smith 	}
95135863739SMike Smith 	/* update the FIB size for the s/g count */
95235863739SMike Smith 	fib->Header.Size += nseg * sizeof(struct aac_sg_entry);
95335863739SMike Smith     }
95435863739SMike Smith 
95535863739SMike Smith }
95635863739SMike Smith 
95735863739SMike Smith /********************************************************************************
95835863739SMike Smith  * Map a command into controller-visible space.
95935863739SMike Smith  */
96035863739SMike Smith static void
96135863739SMike Smith aac_map_command(struct aac_command *cm)
96235863739SMike Smith {
96335863739SMike Smith     struct aac_softc	*sc = cm->cm_sc;
96435863739SMike Smith 
96535863739SMike Smith     debug_called(2);
96635863739SMike Smith 
96735863739SMike Smith     /* don't map more than once */
96835863739SMike Smith     if (cm->cm_flags & AAC_CMD_MAPPED)
96935863739SMike Smith 	return;
97035863739SMike Smith 
97135863739SMike Smith     if (cm->cm_datalen != 0) {
97235863739SMike Smith 	bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap, cm->cm_data,
97335863739SMike Smith 			cm->cm_datalen, aac_map_command_sg, cm, 0);
97435863739SMike Smith 
97535863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
97635863739SMike Smith 	    bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_PREREAD);
97735863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
97835863739SMike Smith 	    bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_PREWRITE);
97935863739SMike Smith     }
98035863739SMike Smith     cm->cm_flags |= AAC_CMD_MAPPED;
98135863739SMike Smith }
98235863739SMike Smith 
98335863739SMike Smith /********************************************************************************
98435863739SMike Smith  * Unmap a command from controller-visible space.
98535863739SMike Smith  */
98635863739SMike Smith static void
98735863739SMike Smith aac_unmap_command(struct aac_command *cm)
98835863739SMike Smith {
98935863739SMike Smith     struct aac_softc	*sc = cm->cm_sc;
99035863739SMike Smith 
99135863739SMike Smith     debug_called(2);
99235863739SMike Smith 
99335863739SMike Smith     if (!(cm->cm_flags & AAC_CMD_MAPPED))
99435863739SMike Smith 	return;
99535863739SMike Smith 
99635863739SMike Smith     if (cm->cm_datalen != 0) {
99735863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
99835863739SMike Smith 	    bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_POSTREAD);
99935863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
100035863739SMike Smith 	    bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_POSTWRITE);
100135863739SMike Smith 
100235863739SMike Smith 	bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
100335863739SMike Smith     }
100435863739SMike Smith     cm->cm_flags &= ~AAC_CMD_MAPPED;
100535863739SMike Smith }
100635863739SMike Smith 
100735863739SMike Smith /********************************************************************************
100835863739SMike Smith  ********************************************************************************
100935863739SMike Smith                                                                Hardware Interface
101035863739SMike Smith  ********************************************************************************
101135863739SMike Smith  ********************************************************************************/
101235863739SMike Smith 
101335863739SMike Smith /********************************************************************************
101435863739SMike Smith  * Initialise the adapter.
101535863739SMike Smith  */
101635863739SMike Smith static void
101735863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
101835863739SMike Smith {
101935863739SMike Smith     struct aac_softc	*sc = (struct aac_softc *)arg;
102035863739SMike Smith 
102135863739SMike Smith     debug_called(1);
102235863739SMike Smith 
102335863739SMike Smith     sc->aac_common_busaddr = segs[0].ds_addr;
102435863739SMike Smith }
102535863739SMike Smith 
102635863739SMike Smith static int
102735863739SMike Smith aac_init(struct aac_softc *sc)
102835863739SMike Smith {
102935863739SMike Smith     struct aac_adapter_init	*ip;
103035863739SMike Smith     time_t			then;
103135863739SMike Smith     u_int32_t			code;
103235863739SMike Smith     u_int8_t			*qaddr;
103335863739SMike Smith 
103435863739SMike Smith     debug_called(1);
103535863739SMike Smith 
103635863739SMike Smith     /*
103735863739SMike Smith      * First wait for the adapter to come ready.
103835863739SMike Smith      */
103935863739SMike Smith     then = time_second;
104035863739SMike Smith     do {
104135863739SMike Smith 	code = AAC_GET_FWSTATUS(sc);
104235863739SMike Smith 	if (code & AAC_SELF_TEST_FAILED) {
104335863739SMike Smith 	    device_printf(sc->aac_dev, "FATAL: selftest failed\n");
104435863739SMike Smith 	    return(ENXIO);
104535863739SMike Smith 	}
104635863739SMike Smith 	if (code & AAC_KERNEL_PANIC) {
104735863739SMike Smith 	    device_printf(sc->aac_dev, "FATAL: controller kernel panic\n");
104835863739SMike Smith 	    return(ENXIO);
104935863739SMike Smith 	}
105035863739SMike Smith 	if (time_second > (then + AAC_BOOT_TIMEOUT)) {
105135863739SMike Smith 	    device_printf(sc->aac_dev, "FATAL: controller not coming ready, status %x\n", code);
105235863739SMike Smith 	    return(ENXIO);
105335863739SMike Smith 	}
105435863739SMike Smith     } while (!(code & AAC_UP_AND_RUNNING));
105535863739SMike Smith 
105635863739SMike Smith     /*
105735863739SMike Smith      * Create DMA tag for the common structure and allocate it.
105835863739SMike Smith      */
105935863739SMike Smith     if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
106035863739SMike Smith 			   1, 0, 			/* alignment, boundary */
106135863739SMike Smith 			   BUS_SPACE_MAXADDR,		/* lowaddr */
106235863739SMike Smith 			   BUS_SPACE_MAXADDR, 		/* highaddr */
106335863739SMike Smith 			   NULL, NULL, 			/* filter, filterarg */
106435863739SMike Smith 			   sizeof(struct aac_common), 1,/* maxsize, nsegments */
106535863739SMike Smith 			   BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
106635863739SMike Smith 			   0,				/* flags */
106735863739SMike Smith 			   &sc->aac_common_dmat)) {
106835863739SMike Smith 	device_printf(sc->aac_dev, "can't allocate common structure DMA tag\n");
106935863739SMike Smith 	return(ENOMEM);
107035863739SMike Smith     }
107135863739SMike Smith     if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common, BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
107235863739SMike Smith 	device_printf(sc->aac_dev, "can't allocate common structure\n");
107335863739SMike Smith 	return(ENOMEM);
107435863739SMike Smith     }
107535863739SMike Smith     bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap, sc->aac_common, sizeof(*sc->aac_common),
107635863739SMike Smith 		    aac_common_map, sc, 0);
107735863739SMike Smith     bzero(sc->aac_common, sizeof(*sc->aac_common));
107835863739SMike Smith 
107935863739SMike Smith     /*
108035863739SMike Smith      * Fill in the init structure.  This tells the adapter about the physical location
108135863739SMike Smith      * of various important shared data structures.
108235863739SMike Smith      */
108335863739SMike Smith     ip = &sc->aac_common->ac_init;
108435863739SMike Smith     ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
108535863739SMike Smith 
108646aa3347SPoul-Henning Kamp     ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr + offsetof(struct aac_common, ac_fibs);
108735863739SMike Smith     ip->AdapterFibsVirtualAddress = &sc->aac_common->ac_fibs[0];
108835863739SMike Smith     ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
108935863739SMike Smith     ip->AdapterFibAlign = sizeof(struct aac_fib);
109035863739SMike Smith 
109146aa3347SPoul-Henning Kamp     ip->PrintfBufferAddress = sc->aac_common_busaddr + offsetof(struct aac_common, ac_printf);
109235863739SMike Smith     ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
109335863739SMike Smith 
109435863739SMike Smith     ip->HostPhysMemPages = 0;			/* not used? */
109535863739SMike Smith     ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
109635863739SMike Smith 
109735863739SMike Smith     /*
109835863739SMike Smith      * Initialise FIB queues.  Note that it appears that the layout of the indexes
10990b94a66eSMike Smith      * and the segmentation of the entries may be mandated by the adapter, which is
110035863739SMike Smith      * only told about the base of the queue index fields.
110135863739SMike Smith      *
110235863739SMike Smith      * The initial values of the indices are assumed to inform the adapter
11030b94a66eSMike Smith      * of the sizes of the respective queues, and theoretically it could work out
11040b94a66eSMike Smith      * the entire layout of the queue structures from this.  We take the easy
11050b94a66eSMike Smith      * route and just lay this area out like everyone else does.
110635863739SMike Smith      *
110735863739SMike Smith      * The Linux driver uses a much more complex scheme whereby several header
110835863739SMike Smith      * records are kept for each queue.  We use a couple of generic list manipulation
110935863739SMike Smith      * functions which 'know' the size of each list by virtue of a table.
111035863739SMike Smith      */
111135863739SMike Smith     qaddr = &sc->aac_common->ac_qbuf[0] + AAC_QUEUE_ALIGN;
111235863739SMike Smith     qaddr -= (u_int32_t)qaddr % AAC_QUEUE_ALIGN;
111335863739SMike Smith     sc->aac_queues = (struct aac_queue_table *)qaddr;
111435863739SMike Smith     ip->CommHeaderAddress = sc->aac_common_busaddr + ((u_int32_t)sc->aac_queues - (u_int32_t)sc->aac_common);
111535863739SMike Smith     bzero(sc->aac_queues, sizeof(struct aac_queue_table));
111635863739SMike Smith 
111735863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX]  = AAC_HOST_NORM_CMD_ENTRIES;
111835863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX]  = AAC_HOST_NORM_CMD_ENTRIES;
111935863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX]  = AAC_HOST_HIGH_CMD_ENTRIES;
112035863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX]  = AAC_HOST_HIGH_CMD_ENTRIES;
112135863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX]  = AAC_ADAP_NORM_CMD_ENTRIES;
112235863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX]  = AAC_ADAP_NORM_CMD_ENTRIES;
112335863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX]  = AAC_ADAP_HIGH_CMD_ENTRIES;
112435863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX]  = AAC_ADAP_HIGH_CMD_ENTRIES;
112535863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] = AAC_HOST_NORM_RESP_ENTRIES;
112635863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] = AAC_HOST_NORM_RESP_ENTRIES;
112735863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] = AAC_HOST_HIGH_RESP_ENTRIES;
112835863739SMike Smith     sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] = AAC_HOST_HIGH_RESP_ENTRIES;
112935863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] = AAC_ADAP_NORM_RESP_ENTRIES;
113035863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] = AAC_ADAP_NORM_RESP_ENTRIES;
113135863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] = AAC_ADAP_HIGH_RESP_ENTRIES;
113235863739SMike Smith     sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] = AAC_ADAP_HIGH_RESP_ENTRIES;
113335863739SMike Smith     sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] = &sc->aac_queues->qt_HostNormCmdQueue[0];
113435863739SMike Smith     sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] = &sc->aac_queues->qt_HostHighCmdQueue[0];
113535863739SMike Smith     sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] = &sc->aac_queues->qt_AdapNormCmdQueue[0];
113635863739SMike Smith     sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] = &sc->aac_queues->qt_AdapHighCmdQueue[0];
113735863739SMike Smith     sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] = &sc->aac_queues->qt_HostNormRespQueue[0];
113835863739SMike Smith     sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] = &sc->aac_queues->qt_HostHighRespQueue[0];
113935863739SMike Smith     sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] = &sc->aac_queues->qt_AdapNormRespQueue[0];
114035863739SMike Smith     sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] = &sc->aac_queues->qt_AdapHighRespQueue[0];
114135863739SMike Smith 
114235863739SMike Smith     /*
114335863739SMike Smith      * Do controller-type-specific initialisation
114435863739SMike Smith      */
114535863739SMike Smith     switch (sc->aac_hwif) {
114635863739SMike Smith     case AAC_HWIF_I960RX:
114735863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
114835863739SMike Smith 	break;
114935863739SMike Smith     }
115035863739SMike Smith 
115135863739SMike Smith     /*
115235863739SMike Smith      * Give the init structure to the controller.
115335863739SMike Smith      */
115435863739SMike Smith     if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
115546aa3347SPoul-Henning Kamp 			 sc->aac_common_busaddr + offsetof(struct aac_common, ac_init),
115635863739SMike Smith 			 0, 0, 0, NULL)) {
115735863739SMike Smith 	device_printf(sc->aac_dev, "error establishing init structure\n");
115835863739SMike Smith 	return(EIO);
115935863739SMike Smith     }
116035863739SMike Smith 
116135863739SMike Smith     return(0);
116235863739SMike Smith }
116335863739SMike Smith 
116435863739SMike Smith /********************************************************************************
116535863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
116635863739SMike Smith  */
116735863739SMike Smith static int
116835863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
116935863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
117035863739SMike Smith 		 u_int32_t *sp)
117135863739SMike Smith {
117235863739SMike Smith     time_t	then;
117335863739SMike Smith     u_int32_t	status;
117435863739SMike Smith 
117535863739SMike Smith     debug_called(3);
117635863739SMike Smith 
117735863739SMike Smith     /* populate the mailbox */
117835863739SMike Smith     AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
117935863739SMike Smith 
118035863739SMike Smith     /* ensure the sync command doorbell flag is cleared */
118135863739SMike Smith     AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
118235863739SMike Smith 
118335863739SMike Smith     /* then set it to signal the adapter */
118435863739SMike Smith     AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
118535863739SMike Smith 
118635863739SMike Smith     /* spin waiting for the command to complete */
118735863739SMike Smith     then = time_second;
118835863739SMike Smith     do {
118935863739SMike Smith 	if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
119035863739SMike Smith 	    debug(2, "timed out");
119135863739SMike Smith 	    return(EIO);
119235863739SMike Smith 	}
119335863739SMike Smith     } while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
119435863739SMike Smith 
119535863739SMike Smith     /* clear the completion flag */
119635863739SMike Smith     AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
119735863739SMike Smith 
119835863739SMike Smith     /* get the command status */
119935863739SMike Smith     status = AAC_GET_MAILBOXSTATUS(sc);
120035863739SMike Smith     if (sp != NULL)
120135863739SMike Smith 	*sp = status;
12020b94a66eSMike Smith     return(0);
120335863739SMike Smith }
120435863739SMike Smith 
120535863739SMike Smith /********************************************************************************
120635863739SMike Smith  * Send a synchronous FIB to the controller and wait for a result.
120735863739SMike Smith  */
120835863739SMike Smith static int
120935863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
121035863739SMike Smith 	     void *data, u_int16_t datasize,
121135863739SMike Smith 	     void *result, u_int16_t *resultsize)
121235863739SMike Smith {
121335863739SMike Smith     struct aac_fib	*fib = &sc->aac_common->ac_sync_fib;
121435863739SMike Smith 
121535863739SMike Smith     debug_called(3);
121635863739SMike Smith 
121735863739SMike Smith     if (datasize > AAC_FIB_DATASIZE)
121835863739SMike Smith 	return(EINVAL);
121935863739SMike Smith 
122035863739SMike Smith     /*
122135863739SMike Smith      * Set up the sync FIB
122235863739SMike Smith      */
122335863739SMike Smith     fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY;
122435863739SMike Smith     fib->Header.XferState |= xferstate;
122535863739SMike Smith     fib->Header.Command = command;
122635863739SMike Smith     fib->Header.StructType = AAC_FIBTYPE_TFIB;
122735863739SMike Smith     fib->Header.Size = sizeof(struct aac_fib) + datasize;
122835863739SMike Smith     fib->Header.SenderSize = sizeof(struct aac_fib);
122935863739SMike Smith     fib->Header.SenderFibAddress = (u_int32_t)fib;
123046aa3347SPoul-Henning Kamp     fib->Header.ReceiverFibAddress = sc->aac_common_busaddr + offsetof(struct aac_common, ac_sync_fib);
123135863739SMike Smith 
123235863739SMike Smith     /*
123335863739SMike Smith      * Copy in data.
123435863739SMike Smith      */
123535863739SMike Smith     if (data != NULL) {
123635863739SMike Smith 	bcopy(data, fib->data, datasize);
123735863739SMike Smith 	fib->Header.XferState |= AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_NORM;
123835863739SMike Smith     }
123935863739SMike Smith 
124035863739SMike Smith     /*
124135863739SMike Smith      * Give the FIB to the controller, wait for a response.
124235863739SMike Smith      */
124335863739SMike Smith     if (aac_sync_command(sc, AAC_MONKER_SYNCFIB, fib->Header.ReceiverFibAddress,
124435863739SMike Smith 			 0, 0, 0, NULL)) {
124535863739SMike Smith 	debug(2, "IO error");
124635863739SMike Smith 	return(EIO);
124735863739SMike Smith     }
124835863739SMike Smith 
124935863739SMike Smith     /*
125035863739SMike Smith      * Copy out the result
125135863739SMike Smith      */
125235863739SMike Smith     if (result != NULL) {
125335863739SMike Smith 	*resultsize = fib->Header.Size - sizeof(struct aac_fib_header);
125435863739SMike Smith 	bcopy(fib->data, result, *resultsize);
125535863739SMike Smith     }
125635863739SMike Smith     return(0);
125735863739SMike Smith }
125835863739SMike Smith 
125935863739SMike Smith /********************************************************************************
126035863739SMike Smith  * Adapter-space FIB queue manipulation
126135863739SMike Smith  *
126235863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
126335863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
126435863739SMike Smith  */
126535863739SMike Smith static struct {
126635863739SMike Smith     int		size;
126735863739SMike Smith     int		notify;
126835863739SMike Smith } aac_qinfo[] = {
126935863739SMike Smith     {AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
127035863739SMike Smith     {AAC_HOST_HIGH_CMD_ENTRIES, 0},
127135863739SMike Smith     {AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
127235863739SMike Smith     {AAC_ADAP_HIGH_CMD_ENTRIES, 0},
127335863739SMike Smith     {AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
127435863739SMike Smith     {AAC_HOST_HIGH_RESP_ENTRIES, 0},
127535863739SMike Smith     {AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
127635863739SMike Smith     {AAC_ADAP_HIGH_RESP_ENTRIES, 0}
127735863739SMike Smith };
127835863739SMike Smith 
127935863739SMike Smith /*
128035863739SMike Smith  * Atomically insert an entry into the nominated queue, returns 0 on success or EBUSY
128135863739SMike Smith  * if the queue is full.
128235863739SMike Smith  *
12830b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
128435863739SMike Smith  *       the case where we may be inserting several entries in rapid succession, but
12850b94a66eSMike Smith  *       implementing this usefully may be difficult (it would involve a separate
12860b94a66eSMike Smith  *       queue/notify interface).
128735863739SMike Smith  */
128835863739SMike Smith static int
128935863739SMike Smith aac_enqueue_fib(struct aac_softc *sc, int queue, u_int32_t fib_size, u_int32_t fib_addr)
129035863739SMike Smith {
129135863739SMike Smith     u_int32_t	pi, ci;
129235863739SMike Smith     int		s, error;
129335863739SMike Smith 
129435863739SMike Smith     debug_called(3);
129535863739SMike Smith 
129635863739SMike Smith     s = splbio();
129735863739SMike Smith 
129835863739SMike Smith     /* get the producer/consumer indices */
129935863739SMike Smith     pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
130035863739SMike Smith     ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
130135863739SMike Smith 
130235863739SMike Smith     /* wrap the queue? */
130335863739SMike Smith     if (pi >= aac_qinfo[queue].size)
130435863739SMike Smith 	pi = 0;
130535863739SMike Smith 
130635863739SMike Smith     /* check for queue full */
130735863739SMike Smith     if ((pi + 1) == ci) {
130835863739SMike Smith 	error = EBUSY;
130935863739SMike Smith 	goto out;
131035863739SMike Smith     }
131135863739SMike Smith 
131235863739SMike Smith     /* populate queue entry */
131335863739SMike Smith     (sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
131435863739SMike Smith     (sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
131535863739SMike Smith 
131635863739SMike Smith     /* update producer index */
131735863739SMike Smith     sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
131835863739SMike Smith 
131935863739SMike Smith     /* notify the adapter if we know how */
132035863739SMike Smith     if (aac_qinfo[queue].notify != 0)
132135863739SMike Smith 	AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
132235863739SMike Smith 
132335863739SMike Smith     error = 0;
132435863739SMike Smith 
132535863739SMike Smith out:
132635863739SMike Smith     splx(s);
132735863739SMike Smith     return(error);
132835863739SMike Smith }
132935863739SMike Smith 
133035863739SMike Smith /*
133135863739SMike Smith  * Atomically remove one entry from the nominated queue, returns 0 on success or ENOENT
133235863739SMike Smith  * if the queue is empty.
133335863739SMike Smith  */
133435863739SMike Smith static int
133535863739SMike Smith aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, struct aac_fib **fib_addr)
133635863739SMike Smith {
133735863739SMike Smith     u_int32_t	pi, ci;
133835863739SMike Smith     int		s, error;
133935863739SMike Smith 
134035863739SMike Smith     debug_called(3);
134135863739SMike Smith 
134235863739SMike Smith     s = splbio();
134335863739SMike Smith 
134435863739SMike Smith     /* get the producer/consumer indices */
134535863739SMike Smith     pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
134635863739SMike Smith     ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
134735863739SMike Smith 
134835863739SMike Smith     /* check for queue empty */
134935863739SMike Smith     if (ci == pi) {
135035863739SMike Smith 	error = ENOENT;
135135863739SMike Smith 	goto out;
135235863739SMike Smith     }
135335863739SMike Smith 
135435863739SMike Smith     /* wrap the queue? */
135535863739SMike Smith     if (ci >= aac_qinfo[queue].size)
135635863739SMike Smith 	ci = 0;
135735863739SMike Smith 
135835863739SMike Smith     /* fetch the entry */
135935863739SMike Smith     *fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
136035863739SMike Smith     *fib_addr = (struct aac_fib *)(sc->aac_qentries[queue] + ci)->aq_fib_addr;
136135863739SMike Smith 
136235863739SMike Smith     /* update consumer index */
136335863739SMike Smith     sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
136435863739SMike Smith 
136535863739SMike Smith     /* if we have made the queue un-full, notify the adapter */
136635863739SMike Smith     if (((pi + 1) == ci) && (aac_qinfo[queue].notify != 0))
136735863739SMike Smith 	AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
136835863739SMike Smith     error = 0;
136935863739SMike Smith 
137035863739SMike Smith out:
137135863739SMike Smith     splx(s);
137235863739SMike Smith     return(error);
137335863739SMike Smith }
137435863739SMike Smith 
137535863739SMike Smith /********************************************************************************
13760b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
13770b94a66eSMike Smith  * and complain about them.
13780b94a66eSMike Smith  */
13790b94a66eSMike Smith static void
13800b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
13810b94a66eSMike Smith {
13820b94a66eSMike Smith     int		s;
13830b94a66eSMike Smith     struct	aac_command *cm;
13840b94a66eSMike Smith     time_t	deadline;
13850b94a66eSMike Smith 
13860b94a66eSMike Smith     /* simulate an interrupt to handle possibly-missed interrupts */
13870b94a66eSMike Smith     aac_intr(sc);
13880b94a66eSMike Smith 
13890b94a66eSMike Smith     /* kick the I/O queue to restart it in the case of deadlock */
13900b94a66eSMike Smith     aac_startio(sc);
13910b94a66eSMike Smith 
13920b94a66eSMike Smith     /* traverse the busy command list, bitch about late commands once only */
13930b94a66eSMike Smith     deadline = time_second - AAC_CMD_TIMEOUT;
13940b94a66eSMike Smith     s = splbio();
13950b94a66eSMike Smith     TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
13960b94a66eSMike Smith 	if ((cm->cm_timestamp  < deadline) && !(cm->cm_flags & AAC_CMD_TIMEDOUT)) {
13970b94a66eSMike Smith 	    cm->cm_flags |= AAC_CMD_TIMEDOUT;
13980b94a66eSMike Smith 	    device_printf(sc->aac_dev, "COMMAND TIMED OUT AFTER %d SECONDS\n",
13990b94a66eSMike Smith 			  (int)(time_second - cm->cm_timestamp));
14000b94a66eSMike Smith 	    AAC_PRINT_FIB(sc, cm->cm_fib);
14010b94a66eSMike Smith 	}
14020b94a66eSMike Smith     }
14030b94a66eSMike Smith     splx(s);
14040b94a66eSMike Smith 
14050b94a66eSMike Smith     /* reset the timer for next time */
14060b94a66eSMike Smith     timeout((timeout_t*)aac_timeout, sc, AAC_PERIODIC_INTERVAL * hz);
14070b94a66eSMike Smith     return;
14080b94a66eSMike Smith }
14090b94a66eSMike Smith 
14100b94a66eSMike Smith /********************************************************************************
141135863739SMike Smith  ********************************************************************************
141235863739SMike Smith                                                        Interface Function Vectors
141335863739SMike Smith  ********************************************************************************
141435863739SMike Smith  ********************************************************************************/
141535863739SMike Smith 
141635863739SMike Smith /********************************************************************************
141735863739SMike Smith  * Read the current firmware status word.
141835863739SMike Smith  */
141935863739SMike Smith static int
142035863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
142135863739SMike Smith {
142235863739SMike Smith     debug_called(3);
142335863739SMike Smith 
142435863739SMike Smith     return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
142535863739SMike Smith }
142635863739SMike Smith 
142735863739SMike Smith static int
142835863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
142935863739SMike Smith {
143035863739SMike Smith     debug_called(3);
143135863739SMike Smith 
143235863739SMike Smith     return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
143335863739SMike Smith }
143435863739SMike Smith 
143535863739SMike Smith /********************************************************************************
143635863739SMike Smith  * Notify the controller of a change in a given queue
143735863739SMike Smith  */
143835863739SMike Smith 
143935863739SMike Smith static void
144035863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
144135863739SMike Smith {
144235863739SMike Smith     debug_called(3);
144335863739SMike Smith 
144435863739SMike Smith     AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
144535863739SMike Smith }
144635863739SMike Smith 
144735863739SMike Smith static void
144835863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
144935863739SMike Smith {
145035863739SMike Smith     debug_called(3);
145135863739SMike Smith 
145235863739SMike Smith     AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
145335863739SMike Smith }
145435863739SMike Smith 
145535863739SMike Smith /********************************************************************************
145635863739SMike Smith  * Get the interrupt reason bits
145735863739SMike Smith  */
145835863739SMike Smith static int
145935863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
146035863739SMike Smith {
146135863739SMike Smith     debug_called(3);
146235863739SMike Smith 
146335863739SMike Smith     return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
146435863739SMike Smith }
146535863739SMike Smith 
146635863739SMike Smith static int
146735863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
146835863739SMike Smith {
146935863739SMike Smith     debug_called(3);
147035863739SMike Smith 
147135863739SMike Smith     return(AAC_GETREG4(sc, AAC_RX_ODBR));
147235863739SMike Smith }
147335863739SMike Smith 
147435863739SMike Smith /********************************************************************************
147535863739SMike Smith  * Clear some interrupt reason bits
147635863739SMike Smith  */
147735863739SMike Smith static void
147835863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
147935863739SMike Smith {
148035863739SMike Smith     debug_called(3);
148135863739SMike Smith 
148235863739SMike Smith     AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
148335863739SMike Smith }
148435863739SMike Smith 
148535863739SMike Smith static void
148635863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
148735863739SMike Smith {
148835863739SMike Smith     debug_called(3);
148935863739SMike Smith 
149035863739SMike Smith     AAC_SETREG4(sc, AAC_RX_ODBR, mask);
149135863739SMike Smith }
149235863739SMike Smith 
149335863739SMike Smith /********************************************************************************
149435863739SMike Smith  * Populate the mailbox and set the command word
149535863739SMike Smith  */
149635863739SMike Smith static void
149735863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
149835863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
149935863739SMike Smith {
150035863739SMike Smith     debug_called(4);
150135863739SMike Smith 
150235863739SMike Smith     AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
150335863739SMike Smith     AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
150435863739SMike Smith     AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
150535863739SMike Smith     AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
150635863739SMike Smith     AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
150735863739SMike Smith }
150835863739SMike Smith 
150935863739SMike Smith static void
151035863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
151135863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
151235863739SMike Smith {
151335863739SMike Smith     debug_called(4);
151435863739SMike Smith 
151535863739SMike Smith     AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
151635863739SMike Smith     AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
151735863739SMike Smith     AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
151835863739SMike Smith     AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
151935863739SMike Smith     AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
152035863739SMike Smith }
152135863739SMike Smith 
152235863739SMike Smith /********************************************************************************
152335863739SMike Smith  * Fetch the immediate command status word
152435863739SMike Smith  */
152535863739SMike Smith static int
152635863739SMike Smith aac_sa_get_mailboxstatus(struct aac_softc *sc)
152735863739SMike Smith {
152835863739SMike Smith     debug_called(4);
152935863739SMike Smith 
153035863739SMike Smith     return(AAC_GETREG4(sc, AAC_SA_MAILBOX));
153135863739SMike Smith }
153235863739SMike Smith 
153335863739SMike Smith static int
153435863739SMike Smith aac_rx_get_mailboxstatus(struct aac_softc *sc)
153535863739SMike Smith {
153635863739SMike Smith     debug_called(4);
153735863739SMike Smith 
153835863739SMike Smith     return(AAC_GETREG4(sc, AAC_RX_MAILBOX));
153935863739SMike Smith }
154035863739SMike Smith 
154135863739SMike Smith /********************************************************************************
154235863739SMike Smith  * Set/clear interrupt masks
154335863739SMike Smith  */
154435863739SMike Smith static void
154535863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
154635863739SMike Smith {
154735863739SMike Smith     debug(2, "%sable interrupts", enable ? "en" : "dis");
154835863739SMike Smith 
154935863739SMike Smith     if (enable) {
155035863739SMike Smith 	AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
155135863739SMike Smith     } else {
155235863739SMike Smith 	AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
155335863739SMike Smith     }
155435863739SMike Smith }
155535863739SMike Smith 
155635863739SMike Smith static void
155735863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
155835863739SMike Smith {
155935863739SMike Smith     debug(2, "%sable interrupts", enable ? "en" : "dis");
156035863739SMike Smith 
156135863739SMike Smith     if (enable) {
156235863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
156335863739SMike Smith     } else {
156435863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
156535863739SMike Smith     }
156635863739SMike Smith }
156735863739SMike Smith 
156835863739SMike Smith /********************************************************************************
156935863739SMike Smith  ********************************************************************************
157035863739SMike Smith                                                         Debugging and Diagnostics
157135863739SMike Smith  ********************************************************************************
157235863739SMike Smith  ********************************************************************************/
157335863739SMike Smith 
157435863739SMike Smith /********************************************************************************
157535863739SMike Smith  * Print some information about the controller.
157635863739SMike Smith  */
157735863739SMike Smith static void
157835863739SMike Smith aac_describe_controller(struct aac_softc *sc)
157935863739SMike Smith {
158035863739SMike Smith     u_int8_t			buf[AAC_FIB_DATASIZE];	/* XXX really a bit big for the stack */
158135863739SMike Smith     u_int16_t			bufsize;
158235863739SMike Smith     struct aac_adapter_info	*info;
158335863739SMike Smith     u_int8_t			arg;
158435863739SMike Smith 
158535863739SMike Smith     debug_called(2);
158635863739SMike Smith 
158735863739SMike Smith     arg = 0;
158835863739SMike Smith     if (aac_sync_fib(sc, RequestAdapterInfo, 0, &arg, sizeof(arg), &buf, &bufsize)) {
158935863739SMike Smith 	device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
159035863739SMike Smith 	return;
159135863739SMike Smith     }
159235863739SMike Smith     if (bufsize != sizeof(*info)) {
159335863739SMike Smith 	device_printf(sc->aac_dev, "RequestAdapterInfo returned wrong data size (%d != %d)\n",
159435863739SMike Smith 		      bufsize, sizeof(*info));
15950b94a66eSMike Smith 	/*return;*/
159635863739SMike Smith     }
159735863739SMike Smith     info = (struct aac_adapter_info *)&buf[0];
159835863739SMike Smith 
159935863739SMike Smith     device_printf(sc->aac_dev, "%s %dMHz, %dMB total memory, %s (%d)\n",
160035863739SMike Smith 		  aac_describe_code(aac_cpu_variant, info->CpuVariant), info->ClockSpeed,
160135863739SMike Smith 		  info->TotalMem / (1024 * 1024),
160235863739SMike Smith 		  aac_describe_code(aac_battery_platform, info->batteryPlatform), info->batteryPlatform);
160335863739SMike Smith 
160435863739SMike Smith     /* save the kernel revision structure for later use */
160535863739SMike Smith     sc->aac_revision = info->KernelRevision;
160635863739SMike Smith     device_printf(sc->aac_dev, "Kernel %d.%d-%d, S/N %llx\n",
160735863739SMike Smith 		  info->KernelRevision.external.comp.major,
160835863739SMike Smith 		  info->KernelRevision.external.comp.minor,
160935863739SMike Smith 		  info->KernelRevision.external.comp.dash,
161035863739SMike Smith 		  info->SerialNumber);	/* XXX how is this meant to be formatted? */
161135863739SMike Smith }
161235863739SMike Smith 
161335863739SMike Smith /********************************************************************************
161435863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
161535863739SMike Smith  * same.
161635863739SMike Smith  */
161735863739SMike Smith static char *
161835863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
161935863739SMike Smith {
162035863739SMike Smith     int		i;
162135863739SMike Smith 
162235863739SMike Smith     for (i = 0; table[i].string != NULL; i++)
162335863739SMike Smith 	if (table[i].code == code)
162435863739SMike Smith 	    return(table[i].string);
162535863739SMike Smith     return(table[i + 1].string);
162635863739SMike Smith }
162735863739SMike Smith 
162835863739SMike Smith /*****************************************************************************
162935863739SMike Smith  *****************************************************************************
163035863739SMike Smith                                                     Management Interface
163135863739SMike Smith  *****************************************************************************
163235863739SMike Smith  *****************************************************************************/
163335863739SMike Smith 
163435863739SMike Smith static int
163535863739SMike Smith aac_open(dev_t dev, int flags, int fmt, struct proc *p)
163635863739SMike Smith {
163735863739SMike Smith     struct aac_softc	*sc = dev->si_drv1;
163835863739SMike Smith 
163935863739SMike Smith     debug_called(2);
164035863739SMike Smith 
164135863739SMike Smith     /* Check to make sure the device isn't already open */
164235863739SMike Smith     if (sc->aac_state & AAC_STATE_OPEN) {
164335863739SMike Smith         return EBUSY;
164435863739SMike Smith     }
164535863739SMike Smith     sc->aac_state |= AAC_STATE_OPEN;
164635863739SMike Smith 
164735863739SMike Smith     return 0;
164835863739SMike Smith }
164935863739SMike Smith 
165035863739SMike Smith static int
165135863739SMike Smith aac_close(dev_t dev, int flags, int fmt, struct proc *p)
165235863739SMike Smith {
165335863739SMike Smith     struct aac_softc	*sc = dev->si_drv1;
165435863739SMike Smith 
165535863739SMike Smith     debug_called(2);
165635863739SMike Smith 
165735863739SMike Smith     /* Mark this unit as no longer open  */
165835863739SMike Smith     sc->aac_state &= ~AAC_STATE_OPEN;
165935863739SMike Smith 
166035863739SMike Smith     return 0;
166135863739SMike Smith }
166235863739SMike Smith 
166335863739SMike Smith static int
166435863739SMike Smith aac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
166535863739SMike Smith {
16660b94a66eSMike Smith     union aac_statrequest	*as = (union aac_statrequest *)arg;
166735863739SMike Smith     struct aac_softc		*sc = dev->si_drv1;
16680b94a66eSMike Smith     int				error = 0;
16690b94a66eSMike Smith #ifdef AAC_COMPAT_LINUX
16700b94a66eSMike Smith     int				i;
16710b94a66eSMike Smith #endif
167235863739SMike Smith 
167335863739SMike Smith     debug_called(2);
167435863739SMike Smith 
167535863739SMike Smith     switch (cmd) {
16760b94a66eSMike Smith     case AACIO_STATS:
16770b94a66eSMike Smith 	switch (as->as_item) {
16780b94a66eSMike Smith 	case AACQ_FREE:
16790b94a66eSMike Smith 	case AACQ_BIO:
16800b94a66eSMike Smith 	case AACQ_READY:
16810b94a66eSMike Smith 	case AACQ_BUSY:
16820b94a66eSMike Smith 	case AACQ_COMPLETE:
16830b94a66eSMike Smith 	    bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat, sizeof(struct aac_qstat));
16840b94a66eSMike Smith 	    break;
16850b94a66eSMike Smith 	default:
16860b94a66eSMike Smith 	    error = ENOENT;
16870b94a66eSMike Smith 	    break;
16880b94a66eSMike Smith 	}
16890b94a66eSMike Smith 	break;
16900b94a66eSMike Smith 
169135863739SMike Smith #ifdef AAC_COMPAT_LINUX
169235863739SMike Smith     case FSACTL_SENDFIB:
16930b94a66eSMike Smith 	debug(1, "FSACTL_SENDFIB");
169435863739SMike Smith 	error = aac_ioctl_sendfib(sc, arg);
169535863739SMike Smith 	break;
169635863739SMike Smith     case FSACTL_AIF_THREAD:
16970b94a66eSMike Smith 	debug(1, "FSACTL_AIF_THREAD");
169835863739SMike Smith 	error = EINVAL;
169935863739SMike Smith 	break;
170035863739SMike Smith     case FSACTL_OPEN_GET_ADAPTER_FIB:
17010b94a66eSMike Smith 	debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
170235863739SMike Smith 	/*
170335863739SMike Smith 	 * Pass the caller out an AdapterFibContext.
170435863739SMike Smith 	 *
170535863739SMike Smith 	 * Note that because we only support one opener, we
170635863739SMike Smith 	 * basically ignore this.  Set the caller's context to a magic
170735863739SMike Smith 	 * number just in case.
17080b94a66eSMike Smith 	 *
17090b94a66eSMike Smith 	 * The Linux code hands the driver a pointer into kernel space,
17100b94a66eSMike Smith 	 * and then trusts it when the caller hands it back.  Aiee!
171135863739SMike Smith 	 */
171235863739SMike Smith 	i = AAC_AIF_SILLYMAGIC;
171335863739SMike Smith 	error = copyout(&i, arg, sizeof(i));
171435863739SMike Smith 	break;
171535863739SMike Smith     case FSACTL_GET_NEXT_ADAPTER_FIB:
17160b94a66eSMike Smith 	debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
171735863739SMike Smith 	error = aac_linux_getnext_aif(sc, arg);
171835863739SMike Smith 	break;
171935863739SMike Smith     case FSACTL_CLOSE_GET_ADAPTER_FIB:
17200b94a66eSMike Smith 	debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
172135863739SMike Smith 	/* don't do anything here */
172235863739SMike Smith 	break;
172335863739SMike Smith     case FSACTL_MINIPORT_REV_CHECK:
17240b94a66eSMike Smith 	debug(1, "FSACTL_MINIPORT_REV_CHECK");
172535863739SMike Smith 	error = aac_linux_rev_check(sc, arg);
172635863739SMike Smith 	break;
172735863739SMike Smith #endif
172835863739SMike Smith     default:
172935863739SMike Smith 	device_printf(sc->aac_dev, "unsupported cmd 0x%lx\n", cmd);
173035863739SMike Smith 	error = EINVAL;
173135863739SMike Smith 	break;
173235863739SMike Smith     }
173335863739SMike Smith     return(error);
173435863739SMike Smith }
173535863739SMike Smith 
173635863739SMike Smith /********************************************************************************
173735863739SMike Smith  * Send a FIB supplied from userspace
173835863739SMike Smith  */
173935863739SMike Smith static int
174035863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
174135863739SMike Smith {
174235863739SMike Smith     struct aac_command 	*cm;
174335863739SMike Smith     int			size, error;
174435863739SMike Smith 
174535863739SMike Smith     debug_called(2);
174635863739SMike Smith 
174735863739SMike Smith     cm = NULL;
174835863739SMike Smith 
174935863739SMike Smith     /*
175035863739SMike Smith      * Get a command
175135863739SMike Smith      */
175235863739SMike Smith     if (aac_alloc_command(sc, &cm)) {
175335863739SMike Smith 	error = EBUSY;
175435863739SMike Smith 	goto out;
175535863739SMike Smith     }
175635863739SMike Smith 
175735863739SMike Smith     /*
175835863739SMike Smith      * Fetch the FIB header, then re-copy to get data as well.
175935863739SMike Smith      */
176035863739SMike Smith     if ((error = copyin(ufib, cm->cm_fib, sizeof(struct aac_fib_header))) != 0)
176135863739SMike Smith 	goto out;
176235863739SMike Smith     size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
176335863739SMike Smith     if (size > sizeof(struct aac_fib)) {
176435863739SMike Smith 	device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n", size, sizeof(struct aac_fib));
176535863739SMike Smith 	size = sizeof(struct aac_fib);
176635863739SMike Smith     }
176735863739SMike Smith     if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
176835863739SMike Smith 	goto out;
176935863739SMike Smith     cm->cm_fib->Header.Size = size;
177035863739SMike Smith 
177135863739SMike Smith     /*
177235863739SMike Smith      * Pass the FIB to the controller, wait for it to complete.
177335863739SMike Smith      */
177435863739SMike Smith     if ((error = aac_wait_command(cm, 30)) != 0)	/* XXX user timeout? */
177535863739SMike Smith 	goto out;
177635863739SMike Smith 
177735863739SMike Smith     /*
177835863739SMike Smith      * Copy the FIB and data back out to the caller.
177935863739SMike Smith      */
178035863739SMike Smith     size = cm->cm_fib->Header.Size;
178135863739SMike Smith     if (size > sizeof(struct aac_fib)) {
178235863739SMike Smith 	device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n", size, sizeof(struct aac_fib));
178335863739SMike Smith 	size = sizeof(struct aac_fib);
178435863739SMike Smith     }
178535863739SMike Smith     error = copyout(cm->cm_fib, ufib, size);
178635863739SMike Smith 
178735863739SMike Smith out:
178835863739SMike Smith     if (cm != NULL)
178935863739SMike Smith 	aac_release_command(cm);
179035863739SMike Smith     return(error);
179135863739SMike Smith }
179235863739SMike Smith 
179335863739SMike Smith /********************************************************************************
179435863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
179535863739SMike Smith  *
179635863739SMike Smith  * XXX what's the right thing to do here when the queue is full?  Drop the older
179735863739SMike Smith  * or newer entries?
179835863739SMike Smith  */
179935863739SMike Smith static void
180035863739SMike Smith aac_handle_aif(struct aac_softc *sc, struct aac_aif_command *aif)
180135863739SMike Smith {
180235863739SMike Smith     int		next, s;
180335863739SMike Smith 
180435863739SMike Smith     debug_called(2);
180535863739SMike Smith 
180635863739SMike Smith     s = splbio();
180735863739SMike Smith     next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
180835863739SMike Smith     if (next != sc->aac_aifq_tail) {
180935863739SMike Smith 	bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
181035863739SMike Smith 	sc->aac_aifq_head = next;
181135863739SMike Smith 	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
181235863739SMike Smith 	    wakeup(sc->aac_aifq);
181335863739SMike Smith     }
181435863739SMike Smith     splx(s);
181535863739SMike Smith     aac_print_aif(sc, aif);
181635863739SMike Smith }
181735863739SMike Smith 
181835863739SMike Smith /********************************************************************************
181935863739SMike Smith  ********************************************************************************
182035863739SMike Smith                                                        Linux Management Interface
182135863739SMike Smith  ********************************************************************************
182235863739SMike Smith  ********************************************************************************/
182335863739SMike Smith 
182435863739SMike Smith #ifdef AAC_COMPAT_LINUX
182535863739SMike Smith 
182630d57611SMike Smith #include <sys/proc.h>
182735863739SMike Smith #include <machine/../linux/linux.h>
182835863739SMike Smith #include <machine/../linux/linux_proto.h>
182935863739SMike Smith #include <compat/linux/linux_ioctl.h>
183035863739SMike Smith 
183135863739SMike Smith #define AAC_LINUX_IOCTL_MIN  0x2000
183235863739SMike Smith #define AAC_LINUX_IOCTL_MAX  0x21ff
183335863739SMike Smith 
183435863739SMike Smith static linux_ioctl_function_t aac_linux_ioctl;
183535863739SMike Smith static struct linux_ioctl_handler aac_handler = {aac_linux_ioctl, AAC_LINUX_IOCTL_MIN, AAC_LINUX_IOCTL_MAX};
183635863739SMike Smith 
183735863739SMike Smith SYSINIT  (aac_register,   SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_register_handler, &aac_handler);
183835863739SMike Smith SYSUNINIT(aac_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_unregister_handler, &aac_handler);
183935863739SMike Smith 
184035863739SMike Smith MODULE_DEPEND(aac, linux, 1, 1, 1);
184135863739SMike Smith 
184235863739SMike Smith static int
184335863739SMike Smith aac_linux_ioctl(struct proc *p, struct linux_ioctl_args *args)
184435863739SMike Smith {
184535863739SMike Smith     struct file		*fp = p->p_fd->fd_ofiles[args->fd];
184635863739SMike Smith     u_long		cmd = args->cmd;
184735863739SMike Smith 
184835863739SMike Smith     /*
184935863739SMike Smith      * Pass the ioctl off to our standard handler.
185035863739SMike Smith      */
185135863739SMike Smith     return(fo_ioctl(fp, cmd, (caddr_t)args->arg, p));
185235863739SMike Smith }
185335863739SMike Smith 
185435863739SMike Smith /********************************************************************************
18550b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
185635863739SMike Smith  * userspace app is possibly compatible.  This is extremely bogus right now
185735863739SMike Smith  * because I have no idea how to handle the versioning of this driver.  It is
185835863739SMike Smith  * needed, though, to get aaccli working.
185935863739SMike Smith  */
186035863739SMike Smith static int
186135863739SMike Smith aac_linux_rev_check(struct aac_softc *sc, caddr_t udata)
186235863739SMike Smith {
186335863739SMike Smith     struct aac_rev_check	rev_check;
186435863739SMike Smith     struct aac_rev_check_resp	rev_check_resp;
186535863739SMike Smith     int				error = 0;
186635863739SMike Smith 
186735863739SMike Smith     debug_called(2);
186835863739SMike Smith 
186935863739SMike Smith     /*
187035863739SMike Smith      * Copyin the revision struct from userspace
187135863739SMike Smith      */
187235863739SMike Smith     if ((error = copyin(udata, (caddr_t)&rev_check, sizeof(struct aac_rev_check))) != 0) {
187335863739SMike Smith 	return error;
187435863739SMike Smith     }
187535863739SMike Smith 
187635863739SMike Smith     debug(2, "Userland revision= %d\n", rev_check.callingRevision.buildNumber);
187735863739SMike Smith 
187835863739SMike Smith     /*
187935863739SMike Smith      * Doctor up the response struct.
188035863739SMike Smith      */
188135863739SMike Smith     rev_check_resp.possiblyCompatible = 1;
188235863739SMike Smith     rev_check_resp.adapterSWRevision.external.ul = sc->aac_revision.external.ul;
188335863739SMike Smith     rev_check_resp.adapterSWRevision.buildNumber = sc->aac_revision.buildNumber;
188435863739SMike Smith 
188535863739SMike Smith     return(copyout((caddr_t)&rev_check_resp, udata, sizeof(struct aac_rev_check_resp)));
188635863739SMike Smith }
188735863739SMike Smith 
188835863739SMike Smith /********************************************************************************
188935863739SMike Smith  * Pass the caller the next AIF in their queue
189035863739SMike Smith  */
189135863739SMike Smith static int
189235863739SMike Smith aac_linux_getnext_aif(struct aac_softc *sc, caddr_t arg)
189335863739SMike Smith {
189435863739SMike Smith     struct get_adapter_fib_ioctl	agf;
189535863739SMike Smith     int					error, s;
189635863739SMike Smith 
189735863739SMike Smith     debug_called(2);
189835863739SMike Smith 
189935863739SMike Smith     if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
190035863739SMike Smith 
190135863739SMike Smith 	/*
190235863739SMike Smith 	 * Check the magic number that we gave the caller.
190335863739SMike Smith 	 */
190435863739SMike Smith 	if (agf.AdapterFibContext != AAC_AIF_SILLYMAGIC) {
190535863739SMike Smith 	    error = EFAULT;
190635863739SMike Smith 	} else {
190735863739SMike Smith 
190835863739SMike Smith 	    s = splbio();
19090b94a66eSMike Smith 	    error = aac_linux_return_aif(sc, agf.AifFib);
191035863739SMike Smith 
191135863739SMike Smith 	    if ((error == EAGAIN) && (agf.Wait)) {
191235863739SMike Smith 		sc->aac_state |= AAC_STATE_AIF_SLEEPER;
191335863739SMike Smith 		while (error == EAGAIN) {
191435863739SMike Smith 		    error = tsleep(sc->aac_aifq, PRIBIO | PCATCH, "aacaif", 0);
191535863739SMike Smith 		    if (error == 0)
19160b94a66eSMike Smith 			error = aac_linux_return_aif(sc, agf.AifFib);
191735863739SMike Smith 		}
191835863739SMike Smith 		sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
191935863739SMike Smith 	    }
192035863739SMike Smith 	    splx(s);
192135863739SMike Smith 	}
192235863739SMike Smith     }
192335863739SMike Smith     return(error);
192435863739SMike Smith }
192535863739SMike Smith 
19260b94a66eSMike Smith /********************************************************************************
19270b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
19280b94a66eSMike Smith  */
19290b94a66eSMike Smith static int
19300b94a66eSMike Smith aac_linux_return_aif(struct aac_softc *sc, caddr_t uptr)
19310b94a66eSMike Smith {
19320b94a66eSMike Smith     int		error, s;
19330b94a66eSMike Smith 
19340b94a66eSMike Smith     debug_called(2);
19350b94a66eSMike Smith 
19360b94a66eSMike Smith     s = splbio();
19370b94a66eSMike Smith     if (sc->aac_aifq_tail == sc->aac_aifq_head) {
19380b94a66eSMike Smith 	error = EAGAIN;
19390b94a66eSMike Smith     } else {
19400b94a66eSMike Smith 	error = copyout(&sc->aac_aifq[sc->aac_aifq_tail], uptr, sizeof(struct aac_aif_command));
19410b94a66eSMike Smith 	if (!error)
19420b94a66eSMike Smith 	    sc->aac_aifq_tail = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
19430b94a66eSMike Smith     }
19440b94a66eSMike Smith     splx(s);
19450b94a66eSMike Smith     return(error);
19460b94a66eSMike Smith }
19470b94a66eSMike Smith 
19480b94a66eSMike Smith 
194935863739SMike Smith #endif /* AAC_COMPAT_LINUX */
1950