1250963Sachim/*-
2250963Sachim * Copyright (c) 2000 Michael Smith
3250963Sachim * Copyright (c) 2001 Scott Long
4250963Sachim * Copyright (c) 2000 BSDi
5250963Sachim * Copyright (c) 2001-2010 Adaptec, Inc.
6250963Sachim * Copyright (c) 2010-2012 PMC-Sierra, Inc.
7250963Sachim * All rights reserved.
8250963Sachim *
9250963Sachim * Redistribution and use in source and binary forms, with or without
10250963Sachim * modification, are permitted provided that the following conditions
11250963Sachim * are met:
12250963Sachim * 1. Redistributions of source code must retain the above copyright
13250963Sachim *    notice, this list of conditions and the following disclaimer.
14250963Sachim * 2. Redistributions in binary form must reproduce the above copyright
15250963Sachim *    notice, this list of conditions and the following disclaimer in the
16250963Sachim *    documentation and/or other materials provided with the distribution.
17250963Sachim *
18250963Sachim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19250963Sachim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20250963Sachim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21250963Sachim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22250963Sachim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23250963Sachim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24250963Sachim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25250963Sachim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26250963Sachim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27250963Sachim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28250963Sachim * SUCH DAMAGE.
29250963Sachim */
30250963Sachim
31250963Sachim#include <sys/cdefs.h>
32250963Sachim__FBSDID("$FreeBSD$");
33250963Sachim
34250963Sachim/*
35250963Sachim * Driver for the Adaptec by PMC Series 6,7,8,... families of RAID controllers
36250963Sachim */
37250963Sachim#define AAC_DRIVERNAME			"aacraid"
38250963Sachim
39250963Sachim#include "opt_aacraid.h"
40250963Sachim
41250963Sachim/* #include <stddef.h> */
42250963Sachim#include <sys/param.h>
43250963Sachim#include <sys/systm.h>
44250963Sachim#include <sys/malloc.h>
45250963Sachim#include <sys/kernel.h>
46250963Sachim#include <sys/kthread.h>
47250963Sachim#include <sys/sysctl.h>
48250963Sachim#include <sys/poll.h>
49250963Sachim#include <sys/ioccom.h>
50250963Sachim
51250963Sachim#include <sys/bus.h>
52250963Sachim#include <sys/conf.h>
53250963Sachim#include <sys/signalvar.h>
54250963Sachim#include <sys/time.h>
55250963Sachim#include <sys/eventhandler.h>
56250963Sachim#include <sys/rman.h>
57250963Sachim
58250963Sachim#include <machine/bus.h>
59250963Sachim#include <sys/bus_dma.h>
60250963Sachim#include <machine/resource.h>
61250963Sachim
62250963Sachim#include <dev/pci/pcireg.h>
63250963Sachim#include <dev/pci/pcivar.h>
64250963Sachim
65250963Sachim#include <dev/aacraid/aacraid_reg.h>
66250963Sachim#include <sys/aac_ioctl.h>
67250963Sachim#include <dev/aacraid/aacraid_debug.h>
68250963Sachim#include <dev/aacraid/aacraid_var.h>
69250963Sachim
70250963Sachim#ifndef FILTER_HANDLED
71250963Sachim#define FILTER_HANDLED	0x02
72250963Sachim#endif
73250963Sachim
74250963Sachimstatic void	aac_add_container(struct aac_softc *sc,
75250963Sachim				  struct aac_mntinforesp *mir, int f,
76250963Sachim				  u_int32_t uid);
77250963Sachimstatic void	aac_get_bus_info(struct aac_softc *sc);
78250963Sachimstatic void	aac_container_bus(struct aac_softc *sc);
79250963Sachimstatic void	aac_daemon(void *arg);
80250963Sachimstatic int aac_convert_sgraw2(struct aac_softc *sc, struct aac_raw_io2 *raw,
81250963Sachim							  int pages, int nseg, int nseg_new);
82250963Sachim
83250963Sachim/* Command Processing */
84250963Sachimstatic void	aac_timeout(struct aac_softc *sc);
85250963Sachimstatic void	aac_command_thread(struct aac_softc *sc);
86250963Sachimstatic int	aac_sync_fib(struct aac_softc *sc, u_int32_t command,
87250963Sachim				     u_int32_t xferstate, struct aac_fib *fib,
88250963Sachim				     u_int16_t datasize);
89250963Sachim/* Command Buffer Management */
90250963Sachimstatic void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
91250963Sachim				       int nseg, int error);
92250963Sachimstatic int	aac_alloc_commands(struct aac_softc *sc);
93250963Sachimstatic void	aac_free_commands(struct aac_softc *sc);
94250963Sachimstatic void	aac_unmap_command(struct aac_command *cm);
95250963Sachim
96250963Sachim/* Hardware Interface */
97250963Sachimstatic int	aac_alloc(struct aac_softc *sc);
98250963Sachimstatic void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
99250963Sachim			       int error);
100250963Sachimstatic int	aac_check_firmware(struct aac_softc *sc);
101263340Sachimstatic void	aac_define_int_mode(struct aac_softc *sc);
102250963Sachimstatic int	aac_init(struct aac_softc *sc);
103263340Sachimstatic int	aac_find_pci_capability(struct aac_softc *sc, int cap);
104250963Sachimstatic int	aac_setup_intr(struct aac_softc *sc);
105263340Sachimstatic int	aac_check_config(struct aac_softc *sc);
106250963Sachim
107250963Sachim/* PMC SRC interface */
108250963Sachimstatic int	aac_src_get_fwstatus(struct aac_softc *sc);
109250963Sachimstatic void	aac_src_qnotify(struct aac_softc *sc, int qbit);
110250963Sachimstatic int	aac_src_get_istatus(struct aac_softc *sc);
111250963Sachimstatic void	aac_src_clear_istatus(struct aac_softc *sc, int mask);
112250963Sachimstatic void	aac_src_set_mailbox(struct aac_softc *sc, u_int32_t command,
113250963Sachim				    u_int32_t arg0, u_int32_t arg1,
114250963Sachim				    u_int32_t arg2, u_int32_t arg3);
115250963Sachimstatic int	aac_src_get_mailbox(struct aac_softc *sc, int mb);
116263340Sachimstatic void	aac_src_access_devreg(struct aac_softc *sc, int mode);
117250963Sachimstatic int aac_src_send_command(struct aac_softc *sc, struct aac_command *cm);
118250963Sachimstatic int aac_src_get_outb_queue(struct aac_softc *sc);
119250963Sachimstatic void aac_src_set_outb_queue(struct aac_softc *sc, int index);
120250963Sachim
121250963Sachimstruct aac_interface aacraid_src_interface = {
122250963Sachim	aac_src_get_fwstatus,
123250963Sachim	aac_src_qnotify,
124250963Sachim	aac_src_get_istatus,
125250963Sachim	aac_src_clear_istatus,
126250963Sachim	aac_src_set_mailbox,
127250963Sachim	aac_src_get_mailbox,
128263340Sachim	aac_src_access_devreg,
129250963Sachim	aac_src_send_command,
130250963Sachim	aac_src_get_outb_queue,
131250963Sachim	aac_src_set_outb_queue
132250963Sachim};
133250963Sachim
134250963Sachim/* PMC SRCv interface */
135250963Sachimstatic void	aac_srcv_set_mailbox(struct aac_softc *sc, u_int32_t command,
136250963Sachim				    u_int32_t arg0, u_int32_t arg1,
137250963Sachim				    u_int32_t arg2, u_int32_t arg3);
138250963Sachimstatic int	aac_srcv_get_mailbox(struct aac_softc *sc, int mb);
139250963Sachim
140250963Sachimstruct aac_interface aacraid_srcv_interface = {
141250963Sachim	aac_src_get_fwstatus,
142250963Sachim	aac_src_qnotify,
143250963Sachim	aac_src_get_istatus,
144250963Sachim	aac_src_clear_istatus,
145250963Sachim	aac_srcv_set_mailbox,
146250963Sachim	aac_srcv_get_mailbox,
147263340Sachim	aac_src_access_devreg,
148250963Sachim	aac_src_send_command,
149250963Sachim	aac_src_get_outb_queue,
150250963Sachim	aac_src_set_outb_queue
151250963Sachim};
152250963Sachim
153250963Sachim/* Debugging and Diagnostics */
154250963Sachimstatic struct aac_code_lookup aac_cpu_variant[] = {
155250963Sachim	{"i960JX",		CPUI960_JX},
156250963Sachim	{"i960CX",		CPUI960_CX},
157250963Sachim	{"i960HX",		CPUI960_HX},
158250963Sachim	{"i960RX",		CPUI960_RX},
159250963Sachim	{"i960 80303",		CPUI960_80303},
160250963Sachim	{"StrongARM SA110",	CPUARM_SA110},
161250963Sachim	{"PPC603e",		CPUPPC_603e},
162250963Sachim	{"XScale 80321",	CPU_XSCALE_80321},
163250963Sachim	{"MIPS 4KC",		CPU_MIPS_4KC},
164250963Sachim	{"MIPS 5KC",		CPU_MIPS_5KC},
165250963Sachim	{"Unknown StrongARM",	CPUARM_xxx},
166250963Sachim	{"Unknown PowerPC",	CPUPPC_xxx},
167250963Sachim	{NULL, 0},
168250963Sachim	{"Unknown processor",	0}
169250963Sachim};
170250963Sachim
171250963Sachimstatic struct aac_code_lookup aac_battery_platform[] = {
172250963Sachim	{"required battery present",		PLATFORM_BAT_REQ_PRESENT},
173250963Sachim	{"REQUIRED BATTERY NOT PRESENT",	PLATFORM_BAT_REQ_NOTPRESENT},
174250963Sachim	{"optional battery present",		PLATFORM_BAT_OPT_PRESENT},
175250963Sachim	{"optional battery not installed",	PLATFORM_BAT_OPT_NOTPRESENT},
176250963Sachim	{"no battery support",			PLATFORM_BAT_NOT_SUPPORTED},
177250963Sachim	{NULL, 0},
178250963Sachim	{"unknown battery platform",		0}
179250963Sachim};
180250963Sachimstatic void	aac_describe_controller(struct aac_softc *sc);
181250963Sachimstatic char	*aac_describe_code(struct aac_code_lookup *table,
182250963Sachim				   u_int32_t code);
183250963Sachim
184250963Sachim/* Management Interface */
185250963Sachimstatic d_open_t		aac_open;
186250963Sachimstatic d_ioctl_t	aac_ioctl;
187250963Sachimstatic d_poll_t		aac_poll;
188250963Sachim#if __FreeBSD_version >= 702000
189250963Sachimstatic void		aac_cdevpriv_dtor(void *arg);
190250963Sachim#else
191250963Sachimstatic d_close_t	aac_close;
192250963Sachim#endif
193250963Sachimstatic int	aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
194250963Sachimstatic int	aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg);
195250963Sachimstatic void	aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib);
196250963Sachimstatic void	aac_request_aif(struct aac_softc *sc);
197250963Sachimstatic int	aac_rev_check(struct aac_softc *sc, caddr_t udata);
198250963Sachimstatic int	aac_open_aif(struct aac_softc *sc, caddr_t arg);
199250963Sachimstatic int	aac_close_aif(struct aac_softc *sc, caddr_t arg);
200250963Sachimstatic int	aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
201250963Sachimstatic int	aac_return_aif(struct aac_softc *sc,
202250963Sachim			       struct aac_fib_context *ctx, caddr_t uptr);
203250963Sachimstatic int	aac_query_disk(struct aac_softc *sc, caddr_t uptr);
204250963Sachimstatic int	aac_get_pci_info(struct aac_softc *sc, caddr_t uptr);
205250963Sachimstatic int	aac_supported_features(struct aac_softc *sc, caddr_t uptr);
206250963Sachimstatic void	aac_ioctl_event(struct aac_softc *sc,
207250963Sachim				struct aac_event *event, void *arg);
208250963Sachimstatic int	aac_reset_adapter(struct aac_softc *sc);
209250963Sachimstatic int	aac_get_container_info(struct aac_softc *sc,
210250963Sachim				       struct aac_fib *fib, int cid,
211250963Sachim				       struct aac_mntinforesp *mir,
212250963Sachim				       u_int32_t *uid);
213250963Sachimstatic u_int32_t
214250963Sachim	aac_check_adapter_health(struct aac_softc *sc, u_int8_t *bled);
215250963Sachim
216250963Sachimstatic struct cdevsw aacraid_cdevsw = {
217250963Sachim	.d_version =	D_VERSION,
218250963Sachim	.d_flags =	D_NEEDGIANT,
219250963Sachim	.d_open =	aac_open,
220250963Sachim#if __FreeBSD_version < 702000
221250963Sachim	.d_close =	aac_close,
222250963Sachim#endif
223250963Sachim	.d_ioctl =	aac_ioctl,
224250963Sachim	.d_poll =	aac_poll,
225250963Sachim	.d_name =	"aacraid",
226250963Sachim};
227250963Sachim
228250963SachimMALLOC_DEFINE(M_AACRAIDBUF, "aacraid_buf", "Buffers for the AACRAID driver");
229250963Sachim
230250963Sachim/* sysctl node */
231250963SachimSYSCTL_NODE(_hw, OID_AUTO, aacraid, CTLFLAG_RD, 0, "AACRAID driver parameters");
232250963Sachim
233250963Sachim/*
234250963Sachim * Device Interface
235250963Sachim */
236250963Sachim
237250963Sachim/*
238250963Sachim * Initialize the controller and softc
239250963Sachim */
240250963Sachimint
241250963Sachimaacraid_attach(struct aac_softc *sc)
242250963Sachim{
243250963Sachim	int error, unit;
244250963Sachim	struct aac_fib *fib;
245250963Sachim	struct aac_mntinforesp mir;
246250963Sachim	int count = 0, i = 0;
247250963Sachim	u_int32_t uid;
248250963Sachim
249250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
250250963Sachim	sc->hint_flags = device_get_flags(sc->aac_dev);
251250963Sachim	/*
252250963Sachim	 * Initialize per-controller queues.
253250963Sachim	 */
254250963Sachim	aac_initq_free(sc);
255250963Sachim	aac_initq_ready(sc);
256250963Sachim	aac_initq_busy(sc);
257250963Sachim
258250963Sachim	/* mark controller as suspended until we get ourselves organised */
259250963Sachim	sc->aac_state |= AAC_STATE_SUSPEND;
260250963Sachim
261250963Sachim	/*
262250963Sachim	 * Check that the firmware on the card is supported.
263250963Sachim	 */
264263340Sachim	sc->msi_enabled = FALSE;
265250963Sachim	if ((error = aac_check_firmware(sc)) != 0)
266250963Sachim		return(error);
267250963Sachim
268250963Sachim	/*
269250963Sachim	 * Initialize locks
270250963Sachim	 */
271250963Sachim	mtx_init(&sc->aac_io_lock, "AACRAID I/O lock", NULL, MTX_DEF);
272250963Sachim	TAILQ_INIT(&sc->aac_container_tqh);
273250963Sachim	TAILQ_INIT(&sc->aac_ev_cmfree);
274250963Sachim
275250963Sachim#if __FreeBSD_version >= 800000
276250963Sachim	/* Initialize the clock daemon callout. */
277250963Sachim	callout_init_mtx(&sc->aac_daemontime, &sc->aac_io_lock, 0);
278250963Sachim#endif
279250963Sachim	/*
280250963Sachim	 * Initialize the adapter.
281250963Sachim	 */
282250963Sachim	if ((error = aac_alloc(sc)) != 0)
283250963Sachim		return(error);
284250963Sachim	if (!(sc->flags & AAC_FLAGS_SYNC_MODE)) {
285263340Sachim		aac_define_int_mode(sc);
286250963Sachim		if ((error = aac_init(sc)) != 0)
287250963Sachim			return(error);
288250963Sachim	}
289250963Sachim
290250963Sachim	/*
291250963Sachim	 * Allocate and connect our interrupt.
292250963Sachim	 */
293250963Sachim	if ((error = aac_setup_intr(sc)) != 0)
294250963Sachim		return(error);
295250963Sachim
296250963Sachim	/*
297250963Sachim	 * Print a little information about the controller.
298250963Sachim	 */
299250963Sachim	aac_describe_controller(sc);
300250963Sachim
301250963Sachim	/*
302250963Sachim	 * Make the control device.
303250963Sachim	 */
304250963Sachim	unit = device_get_unit(sc->aac_dev);
305250963Sachim	sc->aac_dev_t = make_dev(&aacraid_cdevsw, unit, UID_ROOT, GID_OPERATOR,
306250963Sachim				 0640, "aacraid%d", unit);
307250963Sachim	sc->aac_dev_t->si_drv1 = sc;
308250963Sachim
309250963Sachim	/* Create the AIF thread */
310250963Sachim	if (aac_kthread_create((void(*)(void *))aac_command_thread, sc,
311250963Sachim		   &sc->aifthread, 0, 0, "aacraid%daif", unit))
312250963Sachim		panic("Could not create AIF thread");
313250963Sachim
314250963Sachim	/* Register the shutdown method to only be called post-dump */
315250963Sachim	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aacraid_shutdown,
316250963Sachim	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
317250963Sachim		device_printf(sc->aac_dev,
318250963Sachim			      "shutdown event registration failed\n");
319250963Sachim
320250963Sachim	/* Find containers */
321250963Sachim	mtx_lock(&sc->aac_io_lock);
322250963Sachim	aac_alloc_sync_fib(sc, &fib);
323250963Sachim	/* loop over possible containers */
324250963Sachim	do {
325250963Sachim		if ((aac_get_container_info(sc, fib, i, &mir, &uid)) != 0)
326250963Sachim			continue;
327250963Sachim		if (i == 0)
328250963Sachim			count = mir.MntRespCount;
329250963Sachim		aac_add_container(sc, &mir, 0, uid);
330250963Sachim		i++;
331250963Sachim	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
332250963Sachim	aac_release_sync_fib(sc);
333250963Sachim	mtx_unlock(&sc->aac_io_lock);
334250963Sachim
335250963Sachim	/* Register with CAM for the containers */
336250963Sachim	TAILQ_INIT(&sc->aac_sim_tqh);
337250963Sachim	aac_container_bus(sc);
338250963Sachim	/* Register with CAM for the non-DASD devices */
339250963Sachim	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0)
340250963Sachim		aac_get_bus_info(sc);
341250963Sachim
342250963Sachim	/* poke the bus to actually attach the child devices */
343250963Sachim	bus_generic_attach(sc->aac_dev);
344250963Sachim
345250963Sachim	/* mark the controller up */
346250963Sachim	sc->aac_state &= ~AAC_STATE_SUSPEND;
347250963Sachim
348250963Sachim	/* enable interrupts now */
349263340Sachim	AAC_ACCESS_DEVREG(sc, AAC_ENABLE_INTERRUPT);
350250963Sachim
351250963Sachim#if __FreeBSD_version >= 800000
352250963Sachim	mtx_lock(&sc->aac_io_lock);
353250963Sachim	callout_reset(&sc->aac_daemontime, 60 * hz, aac_daemon, sc);
354250963Sachim	mtx_unlock(&sc->aac_io_lock);
355250963Sachim#else
356250963Sachim	{
357250963Sachim		struct timeval tv;
358250963Sachim		tv.tv_sec = 60;
359250963Sachim		tv.tv_usec = 0;
360250963Sachim		sc->timeout_id = timeout(aac_daemon, (void *)sc, tvtohz(&tv));
361250963Sachim	}
362250963Sachim#endif
363250963Sachim
364250963Sachim	return(0);
365250963Sachim}
366250963Sachim
367250963Sachimstatic void
368250963Sachimaac_daemon(void *arg)
369250963Sachim{
370250963Sachim	struct aac_softc *sc;
371250963Sachim	struct timeval tv;
372250963Sachim	struct aac_command *cm;
373250963Sachim	struct aac_fib *fib;
374250963Sachim
375250963Sachim	sc = arg;
376250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
377250963Sachim
378250963Sachim#if __FreeBSD_version >= 800000
379250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
380250963Sachim	if (callout_pending(&sc->aac_daemontime) ||
381250963Sachim	    callout_active(&sc->aac_daemontime) == 0)
382250963Sachim		return;
383250963Sachim#else
384250963Sachim	mtx_lock(&sc->aac_io_lock);
385250963Sachim#endif
386250963Sachim	getmicrotime(&tv);
387250963Sachim
388250963Sachim	if (!aacraid_alloc_command(sc, &cm)) {
389250963Sachim		fib = cm->cm_fib;
390250963Sachim		cm->cm_timestamp = time_uptime;
391250963Sachim		cm->cm_datalen = 0;
392250963Sachim		cm->cm_flags |= AAC_CMD_WAIT;
393250963Sachim
394250963Sachim		fib->Header.Size =
395250963Sachim			sizeof(struct aac_fib_header) + sizeof(u_int32_t);
396250963Sachim		fib->Header.XferState =
397250963Sachim			AAC_FIBSTATE_HOSTOWNED   |
398250963Sachim			AAC_FIBSTATE_INITIALISED |
399250963Sachim			AAC_FIBSTATE_EMPTY	 |
400250963Sachim			AAC_FIBSTATE_FROMHOST	 |
401250963Sachim			AAC_FIBSTATE_REXPECTED   |
402250963Sachim			AAC_FIBSTATE_NORM	 |
403250963Sachim			AAC_FIBSTATE_ASYNC	 |
404250963Sachim			AAC_FIBSTATE_FAST_RESPONSE;
405250963Sachim		fib->Header.Command = SendHostTime;
406250963Sachim		*(uint32_t *)fib->data = tv.tv_sec;
407250963Sachim
408250963Sachim		aacraid_map_command_sg(cm, NULL, 0, 0);
409250963Sachim		aacraid_release_command(cm);
410250963Sachim	}
411250963Sachim
412250963Sachim#if __FreeBSD_version >= 800000
413250963Sachim	callout_schedule(&sc->aac_daemontime, 30 * 60 * hz);
414250963Sachim#else
415250963Sachim	mtx_unlock(&sc->aac_io_lock);
416250963Sachim	tv.tv_sec = 30 * 60;
417250963Sachim	tv.tv_usec = 0;
418250963Sachim	sc->timeout_id = timeout(aac_daemon, (void *)sc, tvtohz(&tv));
419250963Sachim#endif
420250963Sachim}
421250963Sachim
422250963Sachimvoid
423250963Sachimaacraid_add_event(struct aac_softc *sc, struct aac_event *event)
424250963Sachim{
425250963Sachim
426250963Sachim	switch (event->ev_type & AAC_EVENT_MASK) {
427250963Sachim	case AAC_EVENT_CMFREE:
428250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links);
429250963Sachim		break;
430250963Sachim	default:
431250963Sachim		device_printf(sc->aac_dev, "aac_add event: unknown event %d\n",
432250963Sachim		    event->ev_type);
433250963Sachim		break;
434250963Sachim	}
435250963Sachim
436250963Sachim	return;
437250963Sachim}
438250963Sachim
439250963Sachim/*
440250963Sachim * Request information of container #cid
441250963Sachim */
442250963Sachimstatic int
443250963Sachimaac_get_container_info(struct aac_softc *sc, struct aac_fib *sync_fib, int cid,
444250963Sachim		       struct aac_mntinforesp *mir, u_int32_t *uid)
445250963Sachim{
446250963Sachim	struct aac_command *cm;
447250963Sachim	struct aac_fib *fib;
448250963Sachim	struct aac_mntinfo *mi;
449250963Sachim	struct aac_cnt_config *ccfg;
450263340Sachim	int rval;
451250963Sachim
452250963Sachim	if (sync_fib == NULL) {
453250963Sachim		if (aacraid_alloc_command(sc, &cm)) {
454250963Sachim			device_printf(sc->aac_dev,
455250963Sachim				"Warning, no free command available\n");
456250963Sachim			return (-1);
457250963Sachim		}
458250963Sachim		fib = cm->cm_fib;
459250963Sachim	} else {
460250963Sachim		fib = sync_fib;
461250963Sachim	}
462250963Sachim
463250963Sachim	mi = (struct aac_mntinfo *)&fib->data[0];
464250963Sachim	/* 4KB support?, 64-bit LBA? */
465250963Sachim	if (sc->aac_support_opt2 & AAC_SUPPORTED_VARIABLE_BLOCK_SIZE)
466250963Sachim		mi->Command = VM_NameServeAllBlk;
467250963Sachim	else if (sc->flags & AAC_FLAGS_LBA_64BIT)
468250963Sachim		mi->Command = VM_NameServe64;
469250963Sachim	else
470250963Sachim		mi->Command = VM_NameServe;
471250963Sachim	mi->MntType = FT_FILESYS;
472250963Sachim	mi->MntCount = cid;
473250963Sachim
474250963Sachim	if (sync_fib) {
475250963Sachim		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
476250963Sachim			 sizeof(struct aac_mntinfo))) {
477250963Sachim			device_printf(sc->aac_dev, "Error probing container %d\n", cid);
478250963Sachim			return (-1);
479250963Sachim		}
480250963Sachim	} else {
481250963Sachim		cm->cm_timestamp = time_uptime;
482250963Sachim		cm->cm_datalen = 0;
483250963Sachim
484250963Sachim		fib->Header.Size =
485250963Sachim			sizeof(struct aac_fib_header) + sizeof(struct aac_mntinfo);
486250963Sachim		fib->Header.XferState =
487250963Sachim			AAC_FIBSTATE_HOSTOWNED   |
488250963Sachim			AAC_FIBSTATE_INITIALISED |
489250963Sachim			AAC_FIBSTATE_EMPTY	 |
490250963Sachim			AAC_FIBSTATE_FROMHOST	 |
491250963Sachim			AAC_FIBSTATE_REXPECTED   |
492250963Sachim			AAC_FIBSTATE_NORM	 |
493250963Sachim			AAC_FIBSTATE_ASYNC	 |
494250963Sachim			AAC_FIBSTATE_FAST_RESPONSE;
495250963Sachim		fib->Header.Command = ContainerCommand;
496250963Sachim		if (aacraid_wait_command(cm) != 0) {
497250963Sachim			device_printf(sc->aac_dev, "Error probing container %d\n", cid);
498250963Sachim			aacraid_release_command(cm);
499250963Sachim			return (-1);
500250963Sachim		}
501250963Sachim	}
502250963Sachim	bcopy(&fib->data[0], mir, sizeof(struct aac_mntinforesp));
503250963Sachim
504250963Sachim	/* UID */
505250963Sachim	*uid = cid;
506250963Sachim	if (mir->MntTable[0].VolType != CT_NONE &&
507250963Sachim		!(mir->MntTable[0].ContentState & AAC_FSCS_HIDDEN)) {
508263340Sachim		if (!(sc->aac_support_opt2 & AAC_SUPPORTED_VARIABLE_BLOCK_SIZE)) {
509263340Sachim			mir->MntTable[0].ObjExtension.BlockDevice.BlockSize = 0x200;
510263340Sachim			mir->MntTable[0].ObjExtension.BlockDevice.bdLgclPhysMap = 0;
511263340Sachim		}
512250963Sachim		ccfg = (struct aac_cnt_config *)&fib->data[0];
513250963Sachim		bzero(ccfg, sizeof (*ccfg) - CT_PACKET_SIZE);
514250963Sachim		ccfg->Command = VM_ContainerConfig;
515250963Sachim		ccfg->CTCommand.command = CT_CID_TO_32BITS_UID;
516250963Sachim		ccfg->CTCommand.param[0] = cid;
517250963Sachim
518250963Sachim		if (sync_fib) {
519263340Sachim			rval = aac_sync_fib(sc, ContainerCommand, 0, fib,
520263340Sachim				sizeof(struct aac_cnt_config));
521263340Sachim			if (rval == 0 && ccfg->Command == ST_OK &&
522263340Sachim				ccfg->CTCommand.param[0] == CT_OK &&
523250963Sachim				mir->MntTable[0].VolType != CT_PASSTHRU)
524250963Sachim				*uid = ccfg->CTCommand.param[1];
525250963Sachim		} else {
526250963Sachim			fib->Header.Size =
527250963Sachim				sizeof(struct aac_fib_header) + sizeof(struct aac_cnt_config);
528250963Sachim			fib->Header.XferState =
529250963Sachim				AAC_FIBSTATE_HOSTOWNED   |
530250963Sachim				AAC_FIBSTATE_INITIALISED |
531250963Sachim				AAC_FIBSTATE_EMPTY	 |
532250963Sachim				AAC_FIBSTATE_FROMHOST	 |
533250963Sachim				AAC_FIBSTATE_REXPECTED   |
534250963Sachim				AAC_FIBSTATE_NORM	 |
535250963Sachim				AAC_FIBSTATE_ASYNC	 |
536250963Sachim				AAC_FIBSTATE_FAST_RESPONSE;
537250963Sachim			fib->Header.Command = ContainerCommand;
538263340Sachim			rval = aacraid_wait_command(cm);
539263340Sachim			if (rval == 0 && ccfg->Command == ST_OK &&
540263340Sachim				ccfg->CTCommand.param[0] == CT_OK &&
541250963Sachim				mir->MntTable[0].VolType != CT_PASSTHRU)
542250963Sachim				*uid = ccfg->CTCommand.param[1];
543250963Sachim			aacraid_release_command(cm);
544250963Sachim		}
545250963Sachim	}
546250963Sachim
547250963Sachim	return (0);
548250963Sachim}
549250963Sachim
550250963Sachim/*
551250963Sachim * Create a device to represent a new container
552250963Sachim */
553250963Sachimstatic void
554250963Sachimaac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f,
555250963Sachim		  u_int32_t uid)
556250963Sachim{
557250963Sachim	struct aac_container *co;
558250963Sachim
559250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
560250963Sachim
561250963Sachim	/*
562250963Sachim	 * Check container volume type for validity.  Note that many of
563250963Sachim	 * the possible types may never show up.
564250963Sachim	 */
565250963Sachim	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
566250963Sachim		co = (struct aac_container *)malloc(sizeof *co, M_AACRAIDBUF,
567250963Sachim		       M_NOWAIT | M_ZERO);
568250963Sachim		if (co == NULL) {
569250963Sachim			panic("Out of memory?!");
570250963Sachim		}
571250963Sachim
572250963Sachim		co->co_found = f;
573250963Sachim		bcopy(&mir->MntTable[0], &co->co_mntobj,
574250963Sachim		      sizeof(struct aac_mntobj));
575250963Sachim		co->co_uid = uid;
576250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
577250963Sachim	}
578250963Sachim}
579250963Sachim
580250963Sachim/*
581250963Sachim * Allocate resources associated with (sc)
582250963Sachim */
583250963Sachimstatic int
584250963Sachimaac_alloc(struct aac_softc *sc)
585250963Sachim{
586250963Sachim	bus_size_t maxsize;
587250963Sachim
588250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
589250963Sachim
590250963Sachim	/*
591250963Sachim	 * Create DMA tag for mapping buffers into controller-addressable space.
592250963Sachim	 */
593250963Sachim	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
594250963Sachim			       1, 0, 			/* algnmnt, boundary */
595250963Sachim			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
596250963Sachim			       BUS_SPACE_MAXADDR :
597250963Sachim			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
598250963Sachim			       BUS_SPACE_MAXADDR, 	/* highaddr */
599250963Sachim			       NULL, NULL, 		/* filter, filterarg */
600250963Sachim			       MAXBSIZE,		/* maxsize */
601250963Sachim			       sc->aac_sg_tablesize,	/* nsegments */
602250963Sachim			       MAXBSIZE,		/* maxsegsize */
603250963Sachim			       BUS_DMA_ALLOCNOW,	/* flags */
604250963Sachim			       busdma_lock_mutex,	/* lockfunc */
605250963Sachim			       &sc->aac_io_lock,	/* lockfuncarg */
606250963Sachim			       &sc->aac_buffer_dmat)) {
607250963Sachim		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
608250963Sachim		return (ENOMEM);
609250963Sachim	}
610250963Sachim
611250963Sachim	/*
612250963Sachim	 * Create DMA tag for mapping FIBs into controller-addressable space..
613250963Sachim	 */
614250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1)
615250963Sachim		maxsize = sc->aac_max_fibs_alloc * (sc->aac_max_fib_size +
616250963Sachim			sizeof(struct aac_fib_xporthdr) + 31);
617250963Sachim	else
618250963Sachim		maxsize = sc->aac_max_fibs_alloc * (sc->aac_max_fib_size + 31);
619250963Sachim	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
620250963Sachim			       1, 0, 			/* algnmnt, boundary */
621250963Sachim			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
622250963Sachim			       BUS_SPACE_MAXADDR_32BIT :
623250963Sachim			       0x7fffffff,		/* lowaddr */
624250963Sachim			       BUS_SPACE_MAXADDR, 	/* highaddr */
625250963Sachim			       NULL, NULL, 		/* filter, filterarg */
626250963Sachim			       maxsize,  		/* maxsize */
627250963Sachim			       1,			/* nsegments */
628250963Sachim			       maxsize,			/* maxsize */
629250963Sachim			       0,			/* flags */
630250963Sachim			       NULL, NULL,		/* No locking needed */
631250963Sachim			       &sc->aac_fib_dmat)) {
632250963Sachim		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");
633250963Sachim		return (ENOMEM);
634250963Sachim	}
635250963Sachim
636250963Sachim	/*
637250963Sachim	 * Create DMA tag for the common structure and allocate it.
638250963Sachim	 */
639250963Sachim	maxsize = sizeof(struct aac_common);
640250963Sachim	maxsize += sc->aac_max_fibs * sizeof(u_int32_t);
641250963Sachim	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
642250963Sachim			       1, 0,			/* algnmnt, boundary */
643250963Sachim			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
644250963Sachim			       BUS_SPACE_MAXADDR_32BIT :
645250963Sachim			       0x7fffffff,		/* lowaddr */
646250963Sachim			       BUS_SPACE_MAXADDR, 	/* highaddr */
647250963Sachim			       NULL, NULL, 		/* filter, filterarg */
648250963Sachim			       maxsize, 		/* maxsize */
649250963Sachim			       1,			/* nsegments */
650250963Sachim			       maxsize,			/* maxsegsize */
651250963Sachim			       0,			/* flags */
652250963Sachim			       NULL, NULL,		/* No locking needed */
653250963Sachim			       &sc->aac_common_dmat)) {
654250963Sachim		device_printf(sc->aac_dev,
655250963Sachim			      "can't allocate common structure DMA tag\n");
656250963Sachim		return (ENOMEM);
657250963Sachim	}
658250963Sachim	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
659250963Sachim			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
660250963Sachim		device_printf(sc->aac_dev, "can't allocate common structure\n");
661250963Sachim		return (ENOMEM);
662250963Sachim	}
663250963Sachim
664250963Sachim	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
665250963Sachim			sc->aac_common, maxsize,
666250963Sachim			aac_common_map, sc, 0);
667250963Sachim	bzero(sc->aac_common, maxsize);
668250963Sachim
669250963Sachim	/* Allocate some FIBs and associated command structs */
670250963Sachim	TAILQ_INIT(&sc->aac_fibmap_tqh);
671250963Sachim	sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command),
672250963Sachim				  M_AACRAIDBUF, M_WAITOK|M_ZERO);
673250963Sachim	mtx_lock(&sc->aac_io_lock);
674250963Sachim	while (sc->total_fibs < sc->aac_max_fibs) {
675250963Sachim		if (aac_alloc_commands(sc) != 0)
676250963Sachim			break;
677250963Sachim	}
678250963Sachim	mtx_unlock(&sc->aac_io_lock);
679250963Sachim	if (sc->total_fibs == 0)
680250963Sachim		return (ENOMEM);
681250963Sachim
682250963Sachim	return (0);
683250963Sachim}
684250963Sachim
685250963Sachim/*
686250963Sachim * Free all of the resources associated with (sc)
687250963Sachim *
688250963Sachim * Should not be called if the controller is active.
689250963Sachim */
690250963Sachimvoid
691250963Sachimaacraid_free(struct aac_softc *sc)
692250963Sachim{
693263340Sachim	int i;
694263340Sachim
695250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
696250963Sachim
697250963Sachim	/* remove the control device */
698250963Sachim	if (sc->aac_dev_t != NULL)
699250963Sachim		destroy_dev(sc->aac_dev_t);
700250963Sachim
701250963Sachim	/* throw away any FIB buffers, discard the FIB DMA tag */
702250963Sachim	aac_free_commands(sc);
703250963Sachim	if (sc->aac_fib_dmat)
704250963Sachim		bus_dma_tag_destroy(sc->aac_fib_dmat);
705250963Sachim
706250963Sachim	free(sc->aac_commands, M_AACRAIDBUF);
707250963Sachim
708250963Sachim	/* destroy the common area */
709250963Sachim	if (sc->aac_common) {
710250963Sachim		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
711250963Sachim		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
712250963Sachim				sc->aac_common_dmamap);
713250963Sachim	}
714250963Sachim	if (sc->aac_common_dmat)
715250963Sachim		bus_dma_tag_destroy(sc->aac_common_dmat);
716250963Sachim
717250963Sachim	/* disconnect the interrupt handler */
718263340Sachim	for (i = 0; i < AAC_MAX_MSIX; ++i) {
719263340Sachim		if (sc->aac_intr[i])
720263340Sachim			bus_teardown_intr(sc->aac_dev,
721263340Sachim				sc->aac_irq[i], sc->aac_intr[i]);
722263340Sachim		if (sc->aac_irq[i])
723263340Sachim			bus_release_resource(sc->aac_dev, SYS_RES_IRQ,
724263340Sachim				sc->aac_irq_rid[i], sc->aac_irq[i]);
725263340Sachim		else
726263340Sachim			break;
727263340Sachim	}
728263340Sachim	if (sc->msi_enabled)
729263340Sachim		pci_release_msi(sc->aac_dev);
730250963Sachim
731250963Sachim	/* destroy data-transfer DMA tag */
732250963Sachim	if (sc->aac_buffer_dmat)
733250963Sachim		bus_dma_tag_destroy(sc->aac_buffer_dmat);
734250963Sachim
735250963Sachim	/* destroy the parent DMA tag */
736250963Sachim	if (sc->aac_parent_dmat)
737250963Sachim		bus_dma_tag_destroy(sc->aac_parent_dmat);
738250963Sachim
739250963Sachim	/* release the register window mapping */
740250963Sachim	if (sc->aac_regs_res0 != NULL)
741250963Sachim		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
742250963Sachim				     sc->aac_regs_rid0, sc->aac_regs_res0);
743250963Sachim	if (sc->aac_regs_res1 != NULL)
744250963Sachim		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
745250963Sachim				     sc->aac_regs_rid1, sc->aac_regs_res1);
746250963Sachim}
747250963Sachim
748250963Sachim/*
749250963Sachim * Disconnect from the controller completely, in preparation for unload.
750250963Sachim */
751250963Sachimint
752250963Sachimaacraid_detach(device_t dev)
753250963Sachim{
754250963Sachim	struct aac_softc *sc;
755250963Sachim	struct aac_container *co;
756250963Sachim	struct aac_sim	*sim;
757250963Sachim	int error;
758250963Sachim
759250963Sachim	sc = device_get_softc(dev);
760250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
761250963Sachim
762250963Sachim#if __FreeBSD_version >= 800000
763250963Sachim	callout_drain(&sc->aac_daemontime);
764250963Sachim#else
765250963Sachim	untimeout(aac_daemon, (void *)sc, sc->timeout_id);
766250963Sachim#endif
767250963Sachim	/* Remove the child containers */
768250963Sachim	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
769250963Sachim		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
770250963Sachim		free(co, M_AACRAIDBUF);
771250963Sachim	}
772250963Sachim
773250963Sachim	/* Remove the CAM SIMs */
774250963Sachim	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
775250963Sachim		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
776250963Sachim		error = device_delete_child(dev, sim->sim_dev);
777250963Sachim		if (error)
778250963Sachim			return (error);
779250963Sachim		free(sim, M_AACRAIDBUF);
780250963Sachim	}
781250963Sachim
782250963Sachim	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
783250963Sachim		sc->aifflags |= AAC_AIFFLAGS_EXIT;
784250963Sachim		wakeup(sc->aifthread);
785250963Sachim		tsleep(sc->aac_dev, PUSER | PCATCH, "aac_dch", 30 * hz);
786250963Sachim	}
787250963Sachim
788250963Sachim	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
789250963Sachim		panic("Cannot shutdown AIF thread");
790250963Sachim
791250963Sachim	if ((error = aacraid_shutdown(dev)))
792250963Sachim		return(error);
793250963Sachim
794250963Sachim	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
795250963Sachim
796250963Sachim	aacraid_free(sc);
797250963Sachim
798250963Sachim	mtx_destroy(&sc->aac_io_lock);
799250963Sachim
800250963Sachim	return(0);
801250963Sachim}
802250963Sachim
803250963Sachim/*
804250963Sachim * Bring the controller down to a dormant state and detach all child devices.
805250963Sachim *
806250963Sachim * This function is called before detach or system shutdown.
807250963Sachim *
808250963Sachim * Note that we can assume that the bioq on the controller is empty, as we won't
809250963Sachim * allow shutdown if any device is open.
810250963Sachim */
811250963Sachimint
812250963Sachimaacraid_shutdown(device_t dev)
813250963Sachim{
814250963Sachim	struct aac_softc *sc;
815250963Sachim	struct aac_fib *fib;
816250963Sachim	struct aac_close_command *cc;
817250963Sachim
818250963Sachim	sc = device_get_softc(dev);
819250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
820250963Sachim
821250963Sachim	sc->aac_state |= AAC_STATE_SUSPEND;
822250963Sachim
823250963Sachim	/*
824250963Sachim	 * Send a Container shutdown followed by a HostShutdown FIB to the
825250963Sachim	 * controller to convince it that we don't want to talk to it anymore.
826250963Sachim	 * We've been closed and all I/O completed already
827250963Sachim	 */
828250963Sachim	device_printf(sc->aac_dev, "shutting down controller...");
829250963Sachim
830250963Sachim	mtx_lock(&sc->aac_io_lock);
831250963Sachim	aac_alloc_sync_fib(sc, &fib);
832250963Sachim	cc = (struct aac_close_command *)&fib->data[0];
833250963Sachim
834250963Sachim	bzero(cc, sizeof(struct aac_close_command));
835250963Sachim	cc->Command = VM_CloseAll;
836263340Sachim	cc->ContainerId = 0xfffffffe;
837250963Sachim	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
838250963Sachim	    sizeof(struct aac_close_command)))
839250963Sachim		printf("FAILED.\n");
840250963Sachim	else
841250963Sachim		printf("done\n");
842250963Sachim
843263340Sachim	AAC_ACCESS_DEVREG(sc, AAC_DISABLE_INTERRUPT);
844250963Sachim	aac_release_sync_fib(sc);
845250963Sachim	mtx_unlock(&sc->aac_io_lock);
846250963Sachim
847250963Sachim	return(0);
848250963Sachim}
849250963Sachim
850250963Sachim/*
851250963Sachim * Bring the controller to a quiescent state, ready for system suspend.
852250963Sachim */
853250963Sachimint
854250963Sachimaacraid_suspend(device_t dev)
855250963Sachim{
856250963Sachim	struct aac_softc *sc;
857250963Sachim
858250963Sachim	sc = device_get_softc(dev);
859250963Sachim
860250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
861250963Sachim	sc->aac_state |= AAC_STATE_SUSPEND;
862250963Sachim
863263340Sachim	AAC_ACCESS_DEVREG(sc, AAC_DISABLE_INTERRUPT);
864250963Sachim	return(0);
865250963Sachim}
866250963Sachim
867250963Sachim/*
868250963Sachim * Bring the controller back to a state ready for operation.
869250963Sachim */
870250963Sachimint
871250963Sachimaacraid_resume(device_t dev)
872250963Sachim{
873250963Sachim	struct aac_softc *sc;
874250963Sachim
875250963Sachim	sc = device_get_softc(dev);
876250963Sachim
877250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
878250963Sachim	sc->aac_state &= ~AAC_STATE_SUSPEND;
879263340Sachim	AAC_ACCESS_DEVREG(sc, AAC_ENABLE_INTERRUPT);
880250963Sachim	return(0);
881250963Sachim}
882250963Sachim
883250963Sachim/*
884250963Sachim * Interrupt handler for NEW_COMM_TYPE1, NEW_COMM_TYPE2, NEW_COMM_TYPE34 interface.
885250963Sachim */
886250963Sachimvoid
887250963Sachimaacraid_new_intr_type1(void *arg)
888250963Sachim{
889263340Sachim	struct aac_msix_ctx *ctx;
890250963Sachim	struct aac_softc *sc;
891263340Sachim	int vector_no;
892250963Sachim	struct aac_command *cm;
893250963Sachim	struct aac_fib *fib;
894250963Sachim	u_int32_t bellbits, bellbits_shifted, index, handle;
895263340Sachim	int isFastResponse, isAif, noMoreAif, mode;
896250963Sachim
897263340Sachim	ctx = (struct aac_msix_ctx *)arg;
898263340Sachim	sc = ctx->sc;
899263340Sachim	vector_no = ctx->vector_no;
900250963Sachim
901250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
902250963Sachim	mtx_lock(&sc->aac_io_lock);
903263340Sachim
904263340Sachim	if (sc->msi_enabled) {
905263340Sachim		mode = AAC_INT_MODE_MSI;
906263340Sachim		if (vector_no == 0) {
907263340Sachim			bellbits = AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_MSI);
908263340Sachim			if (bellbits & 0x40000)
909263340Sachim				mode |= AAC_INT_MODE_AIF;
910263340Sachim			else if (bellbits & 0x1000)
911263340Sachim				mode |= AAC_INT_MODE_SYNC;
912263340Sachim		}
913263340Sachim	} else {
914263340Sachim		mode = AAC_INT_MODE_INTX;
915263340Sachim		bellbits = AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_R);
916263340Sachim		if (bellbits & AAC_DB_RESPONSE_SENT_NS) {
917263340Sachim			bellbits = AAC_DB_RESPONSE_SENT_NS;
918263340Sachim			AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, bellbits);
919263340Sachim		} else {
920263340Sachim			bellbits_shifted = (bellbits >> AAC_SRC_ODR_SHIFT);
921263340Sachim			AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, bellbits);
922263340Sachim			if (bellbits_shifted & AAC_DB_AIF_PENDING)
923263340Sachim				mode |= AAC_INT_MODE_AIF;
924263340Sachim			else if (bellbits_shifted & AAC_DB_SYNC_COMMAND)
925263340Sachim				mode |= AAC_INT_MODE_SYNC;
926263340Sachim		}
927263340Sachim		/* ODR readback, Prep #238630 */
928263340Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_R);
929263340Sachim	}
930263340Sachim
931263340Sachim	if (mode & AAC_INT_MODE_SYNC) {
932263340Sachim		if (sc->aac_sync_cm) {
933263340Sachim			cm = sc->aac_sync_cm;
934263340Sachim			cm->cm_flags |= AAC_CMD_COMPLETED;
935263340Sachim			/* is there a completion handler? */
936263340Sachim			if (cm->cm_complete != NULL) {
937263340Sachim				cm->cm_complete(cm);
938263340Sachim			} else {
939263340Sachim				/* assume that someone is sleeping on this command */
940263340Sachim				wakeup(cm);
941263340Sachim			}
942263340Sachim			sc->flags &= ~AAC_QUEUE_FRZN;
943263340Sachim			sc->aac_sync_cm = NULL;
944263340Sachim		}
945263340Sachim		mode = 0;
946263340Sachim	}
947263340Sachim
948263340Sachim	if (mode & AAC_INT_MODE_AIF) {
949263340Sachim		if (mode & AAC_INT_MODE_INTX) {
950263340Sachim			aac_request_aif(sc);
951263340Sachim			mode = 0;
952263340Sachim		}
953263340Sachim	}
954263340Sachim
955263340Sachim	if (mode) {
956250963Sachim		/* handle async. status */
957263340Sachim		index = sc->aac_host_rrq_idx[vector_no];
958250963Sachim		for (;;) {
959250963Sachim			isFastResponse = isAif = noMoreAif = 0;
960250963Sachim			/* remove toggle bit (31) */
961250963Sachim			handle = (sc->aac_common->ac_host_rrq[index] & 0x7fffffff);
962250963Sachim			/* check fast response bit (30) */
963250963Sachim			if (handle & 0x40000000)
964250963Sachim				isFastResponse = 1;
965250963Sachim			/* check AIF bit (23) */
966250963Sachim			else if (handle & 0x00800000)
967250963Sachim				isAif = TRUE;
968250963Sachim			handle &= 0x0000ffff;
969250963Sachim			if (handle == 0)
970250963Sachim				break;
971250963Sachim
972250963Sachim			cm = sc->aac_commands + (handle - 1);
973250963Sachim			fib = cm->cm_fib;
974263340Sachim			sc->aac_rrq_outstanding[vector_no]--;
975250963Sachim			if (isAif) {
976250963Sachim				noMoreAif = (fib->Header.XferState & AAC_FIBSTATE_NOMOREAIF) ? 1:0;
977250963Sachim				if (!noMoreAif)
978250963Sachim					aac_handle_aif(sc, fib);
979250963Sachim				aac_remove_busy(cm);
980250963Sachim				aacraid_release_command(cm);
981250963Sachim			} else {
982250963Sachim				if (isFastResponse) {
983250963Sachim					fib->Header.XferState |= AAC_FIBSTATE_DONEADAP;
984250963Sachim					*((u_int32_t *)(fib->data)) = ST_OK;
985250963Sachim					cm->cm_flags |= AAC_CMD_FASTRESP;
986250963Sachim				}
987250963Sachim				aac_remove_busy(cm);
988250963Sachim				aac_unmap_command(cm);
989250963Sachim				cm->cm_flags |= AAC_CMD_COMPLETED;
990250963Sachim
991250963Sachim				/* is there a completion handler? */
992250963Sachim				if (cm->cm_complete != NULL) {
993250963Sachim					cm->cm_complete(cm);
994250963Sachim				} else {
995250963Sachim					/* assume that someone is sleeping on this command */
996250963Sachim					wakeup(cm);
997250963Sachim				}
998250963Sachim				sc->flags &= ~AAC_QUEUE_FRZN;
999250963Sachim			}
1000250963Sachim
1001250963Sachim			sc->aac_common->ac_host_rrq[index++] = 0;
1002263340Sachim			if (index == (vector_no + 1) * sc->aac_vector_cap)
1003263340Sachim				index = vector_no * sc->aac_vector_cap;
1004263340Sachim			sc->aac_host_rrq_idx[vector_no] = index;
1005250963Sachim
1006250963Sachim			if ((isAif && !noMoreAif) || sc->aif_pending)
1007250963Sachim				aac_request_aif(sc);
1008250963Sachim		}
1009250963Sachim	}
1010250963Sachim
1011263340Sachim	if (mode & AAC_INT_MODE_AIF) {
1012263340Sachim		aac_request_aif(sc);
1013263340Sachim		AAC_ACCESS_DEVREG(sc, AAC_CLEAR_AIF_BIT);
1014263340Sachim		mode = 0;
1015263340Sachim	}
1016263340Sachim
1017250963Sachim	/* see if we can start some more I/O */
1018250963Sachim	if ((sc->flags & AAC_QUEUE_FRZN) == 0)
1019250963Sachim		aacraid_startio(sc);
1020250963Sachim	mtx_unlock(&sc->aac_io_lock);
1021250963Sachim}
1022250963Sachim
1023250963Sachim/*
1024250963Sachim * Handle notification of one or more FIBs coming from the controller.
1025250963Sachim */
1026250963Sachimstatic void
1027250963Sachimaac_command_thread(struct aac_softc *sc)
1028250963Sachim{
1029250963Sachim	int retval;
1030250963Sachim
1031250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1032250963Sachim
1033250963Sachim	mtx_lock(&sc->aac_io_lock);
1034250963Sachim	sc->aifflags = AAC_AIFFLAGS_RUNNING;
1035250963Sachim
1036250963Sachim	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
1037250963Sachim
1038250963Sachim		retval = 0;
1039250963Sachim		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
1040250963Sachim			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
1041250963Sachim					"aacraid_aifthd", AAC_PERIODIC_INTERVAL * hz);
1042250963Sachim
1043250963Sachim		/*
1044250963Sachim		 * First see if any FIBs need to be allocated.  This needs
1045250963Sachim		 * to be called without the driver lock because contigmalloc
1046250963Sachim		 * will grab Giant, and would result in an LOR.
1047250963Sachim		 */
1048250963Sachim		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
1049250963Sachim			aac_alloc_commands(sc);
1050250963Sachim			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
1051250963Sachim			aacraid_startio(sc);
1052250963Sachim		}
1053250963Sachim
1054250963Sachim		/*
1055250963Sachim		 * While we're here, check to see if any commands are stuck.
1056250963Sachim		 * This is pretty low-priority, so it's ok if it doesn't
1057250963Sachim		 * always fire.
1058250963Sachim		 */
1059250963Sachim		if (retval == EWOULDBLOCK)
1060250963Sachim			aac_timeout(sc);
1061250963Sachim
1062250963Sachim		/* Check the hardware printf message buffer */
1063250963Sachim		if (sc->aac_common->ac_printf[0] != 0)
1064250963Sachim			aac_print_printf(sc);
1065250963Sachim	}
1066250963Sachim	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
1067250963Sachim	mtx_unlock(&sc->aac_io_lock);
1068250963Sachim	wakeup(sc->aac_dev);
1069250963Sachim
1070250963Sachim	aac_kthread_exit(0);
1071250963Sachim}
1072250963Sachim
1073250963Sachim/*
1074250963Sachim * Submit a command to the controller, return when it completes.
1075250963Sachim * XXX This is very dangerous!  If the card has gone out to lunch, we could
1076250963Sachim *     be stuck here forever.  At the same time, signals are not caught
1077250963Sachim *     because there is a risk that a signal could wakeup the sleep before
1078250963Sachim *     the card has a chance to complete the command.  Since there is no way
1079250963Sachim *     to cancel a command that is in progress, we can't protect against the
1080250963Sachim *     card completing a command late and spamming the command and data
1081250963Sachim *     memory.  So, we are held hostage until the command completes.
1082250963Sachim */
1083250963Sachimint
1084250963Sachimaacraid_wait_command(struct aac_command *cm)
1085250963Sachim{
1086250963Sachim	struct aac_softc *sc;
1087250963Sachim	int error;
1088250963Sachim
1089250963Sachim	sc = cm->cm_sc;
1090250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1091250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1092250963Sachim
1093250963Sachim	/* Put the command on the ready queue and get things going */
1094250963Sachim	aac_enqueue_ready(cm);
1095250963Sachim	aacraid_startio(sc);
1096250963Sachim	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacraid_wait", 0);
1097250963Sachim	return(error);
1098250963Sachim}
1099250963Sachim
1100250963Sachim/*
1101250963Sachim *Command Buffer Management
1102250963Sachim */
1103250963Sachim
1104250963Sachim/*
1105250963Sachim * Allocate a command.
1106250963Sachim */
1107250963Sachimint
1108250963Sachimaacraid_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
1109250963Sachim{
1110250963Sachim	struct aac_command *cm;
1111250963Sachim
1112250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1113250963Sachim
1114250963Sachim	if ((cm = aac_dequeue_free(sc)) == NULL) {
1115250963Sachim		if (sc->total_fibs < sc->aac_max_fibs) {
1116250963Sachim			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1117250963Sachim			wakeup(sc->aifthread);
1118250963Sachim		}
1119250963Sachim		return (EBUSY);
1120250963Sachim	}
1121250963Sachim
1122250963Sachim	*cmp = cm;
1123250963Sachim	return(0);
1124250963Sachim}
1125250963Sachim
1126250963Sachim/*
1127250963Sachim * Release a command back to the freelist.
1128250963Sachim */
1129250963Sachimvoid
1130250963Sachimaacraid_release_command(struct aac_command *cm)
1131250963Sachim{
1132250963Sachim	struct aac_event *event;
1133250963Sachim	struct aac_softc *sc;
1134250963Sachim
1135250963Sachim	sc = cm->cm_sc;
1136250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1137250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1138250963Sachim
1139250963Sachim	/* (re)initialize the command/FIB */
1140250963Sachim	cm->cm_sgtable = NULL;
1141250963Sachim	cm->cm_flags = 0;
1142250963Sachim	cm->cm_complete = NULL;
1143250963Sachim	cm->cm_ccb = NULL;
1144250963Sachim	cm->cm_passthr_dmat = 0;
1145250963Sachim	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
1146250963Sachim	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
1147250963Sachim	cm->cm_fib->Header.Unused = 0;
1148250963Sachim	cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size;
1149250963Sachim
1150250963Sachim	/*
1151250963Sachim	 * These are duplicated in aac_start to cover the case where an
1152250963Sachim	 * intermediate stage may have destroyed them.  They're left
1153250963Sachim	 * initialized here for debugging purposes only.
1154250963Sachim	 */
1155250963Sachim	cm->cm_fib->Header.u.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1156250963Sachim	cm->cm_fib->Header.Handle = 0;
1157250963Sachim
1158250963Sachim	aac_enqueue_free(cm);
1159250963Sachim
1160250963Sachim	/*
1161250963Sachim	 * Dequeue all events so that there's no risk of events getting
1162250963Sachim	 * stranded.
1163250963Sachim	 */
1164250963Sachim	while ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) {
1165250963Sachim		TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links);
1166250963Sachim		event->ev_callback(sc, event, event->ev_arg);
1167250963Sachim	}
1168250963Sachim}
1169250963Sachim
1170250963Sachim/*
1171250963Sachim * Map helper for command/FIB allocation.
1172250963Sachim */
1173250963Sachimstatic void
1174250963Sachimaac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1175250963Sachim{
1176250963Sachim	uint64_t	*fibphys;
1177250963Sachim
1178250963Sachim	fibphys = (uint64_t *)arg;
1179250963Sachim
1180250963Sachim	*fibphys = segs[0].ds_addr;
1181250963Sachim}
1182250963Sachim
1183250963Sachim/*
1184250963Sachim * Allocate and initialize commands/FIBs for this adapter.
1185250963Sachim */
1186250963Sachimstatic int
1187250963Sachimaac_alloc_commands(struct aac_softc *sc)
1188250963Sachim{
1189250963Sachim	struct aac_command *cm;
1190250963Sachim	struct aac_fibmap *fm;
1191250963Sachim	uint64_t fibphys;
1192250963Sachim	int i, error;
1193250963Sachim	u_int32_t maxsize;
1194250963Sachim
1195250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1196250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1197250963Sachim
1198250963Sachim	if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs)
1199250963Sachim		return (ENOMEM);
1200250963Sachim
1201250963Sachim	fm = malloc(sizeof(struct aac_fibmap), M_AACRAIDBUF, M_NOWAIT|M_ZERO);
1202250963Sachim	if (fm == NULL)
1203250963Sachim		return (ENOMEM);
1204250963Sachim
1205250963Sachim	mtx_unlock(&sc->aac_io_lock);
1206250963Sachim	/* allocate the FIBs in DMAable memory and load them */
1207250963Sachim	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1208250963Sachim			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
1209250963Sachim		device_printf(sc->aac_dev,
1210250963Sachim			      "Not enough contiguous memory available.\n");
1211250963Sachim		free(fm, M_AACRAIDBUF);
1212250963Sachim		mtx_lock(&sc->aac_io_lock);
1213250963Sachim		return (ENOMEM);
1214250963Sachim	}
1215250963Sachim
1216250963Sachim	maxsize = sc->aac_max_fib_size + 31;
1217250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1)
1218250963Sachim		maxsize += sizeof(struct aac_fib_xporthdr);
1219250963Sachim	/* Ignore errors since this doesn't bounce */
1220250963Sachim	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1221250963Sachim			      sc->aac_max_fibs_alloc * maxsize,
1222250963Sachim			      aac_map_command_helper, &fibphys, 0);
1223250963Sachim	mtx_lock(&sc->aac_io_lock);
1224250963Sachim
1225250963Sachim	/* initialize constant fields in the command structure */
1226250963Sachim	bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * maxsize);
1227250963Sachim	for (i = 0; i < sc->aac_max_fibs_alloc; i++) {
1228250963Sachim		cm = sc->aac_commands + sc->total_fibs;
1229250963Sachim		fm->aac_commands = cm;
1230250963Sachim		cm->cm_sc = sc;
1231250963Sachim		cm->cm_fib = (struct aac_fib *)
1232250963Sachim			((u_int8_t *)fm->aac_fibs + i * maxsize);
1233250963Sachim		cm->cm_fibphys = fibphys + i * maxsize;
1234250963Sachim		if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1) {
1235250963Sachim			u_int64_t fibphys_aligned;
1236250963Sachim			fibphys_aligned =
1237250963Sachim				(cm->cm_fibphys + sizeof(struct aac_fib_xporthdr) + 31) & ~31;
1238250963Sachim			cm->cm_fib = (struct aac_fib *)
1239250963Sachim				((u_int8_t *)cm->cm_fib + (fibphys_aligned - cm->cm_fibphys));
1240250963Sachim			cm->cm_fibphys = fibphys_aligned;
1241250963Sachim		} else {
1242250963Sachim			u_int64_t fibphys_aligned;
1243250963Sachim			fibphys_aligned = (cm->cm_fibphys + 31) & ~31;
1244250963Sachim			cm->cm_fib = (struct aac_fib *)
1245250963Sachim				((u_int8_t *)cm->cm_fib + (fibphys_aligned - cm->cm_fibphys));
1246250963Sachim			cm->cm_fibphys = fibphys_aligned;
1247250963Sachim		}
1248250963Sachim		cm->cm_index = sc->total_fibs;
1249250963Sachim
1250250963Sachim		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1251250963Sachim					       &cm->cm_datamap)) != 0)
1252250963Sachim			break;
1253250963Sachim		if (sc->aac_max_fibs <= 1 || sc->aac_max_fibs - sc->total_fibs > 1)
1254250963Sachim			aacraid_release_command(cm);
1255250963Sachim		sc->total_fibs++;
1256250963Sachim	}
1257250963Sachim
1258250963Sachim	if (i > 0) {
1259250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1260250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs);
1261250963Sachim		return (0);
1262250963Sachim	}
1263250963Sachim
1264250963Sachim	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1265250963Sachim	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
1266250963Sachim	free(fm, M_AACRAIDBUF);
1267250963Sachim	return (ENOMEM);
1268250963Sachim}
1269250963Sachim
1270250963Sachim/*
1271250963Sachim * Free FIBs owned by this adapter.
1272250963Sachim */
1273250963Sachimstatic void
1274250963Sachimaac_free_commands(struct aac_softc *sc)
1275250963Sachim{
1276250963Sachim	struct aac_fibmap *fm;
1277250963Sachim	struct aac_command *cm;
1278250963Sachim	int i;
1279250963Sachim
1280250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1281250963Sachim
1282250963Sachim	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
1283250963Sachim
1284250963Sachim		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
1285250963Sachim		/*
1286250963Sachim		 * We check against total_fibs to handle partially
1287250963Sachim		 * allocated blocks.
1288250963Sachim		 */
1289250963Sachim		for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) {
1290250963Sachim			cm = fm->aac_commands + i;
1291250963Sachim			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1292250963Sachim		}
1293250963Sachim		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1294250963Sachim		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
1295250963Sachim		free(fm, M_AACRAIDBUF);
1296250963Sachim	}
1297250963Sachim}
1298250963Sachim
1299250963Sachim/*
1300250963Sachim * Command-mapping helper function - populate this command's s/g table.
1301250963Sachim */
1302250963Sachimvoid
1303250963Sachimaacraid_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1304250963Sachim{
1305250963Sachim	struct aac_softc *sc;
1306250963Sachim	struct aac_command *cm;
1307250963Sachim	struct aac_fib *fib;
1308250963Sachim	int i;
1309250963Sachim
1310250963Sachim	cm = (struct aac_command *)arg;
1311250963Sachim	sc = cm->cm_sc;
1312250963Sachim	fib = cm->cm_fib;
1313250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "nseg %d", nseg);
1314250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1315250963Sachim
1316250963Sachim	/* copy into the FIB */
1317250963Sachim	if (cm->cm_sgtable != NULL) {
1318250963Sachim		if (fib->Header.Command == RawIo2) {
1319250963Sachim			struct aac_raw_io2 *raw;
1320250963Sachim			struct aac_sge_ieee1212 *sg;
1321250963Sachim			u_int32_t min_size = PAGE_SIZE, cur_size;
1322250963Sachim			int conformable = TRUE;
1323250963Sachim
1324250963Sachim			raw = (struct aac_raw_io2 *)&fib->data[0];
1325250963Sachim			sg = (struct aac_sge_ieee1212 *)cm->cm_sgtable;
1326250963Sachim			raw->sgeCnt = nseg;
1327250963Sachim
1328250963Sachim			for (i = 0; i < nseg; i++) {
1329250963Sachim				cur_size = segs[i].ds_len;
1330250963Sachim				sg[i].addrHigh = 0;
1331250963Sachim				*(bus_addr_t *)&sg[i].addrLow = segs[i].ds_addr;
1332250963Sachim				sg[i].length = cur_size;
1333250963Sachim				sg[i].flags = 0;
1334250963Sachim				if (i == 0) {
1335250963Sachim					raw->sgeFirstSize = cur_size;
1336250963Sachim				} else if (i == 1) {
1337250963Sachim					raw->sgeNominalSize = cur_size;
1338250963Sachim					min_size = cur_size;
1339250963Sachim				} else if ((i+1) < nseg &&
1340250963Sachim					cur_size != raw->sgeNominalSize) {
1341250963Sachim					conformable = FALSE;
1342250963Sachim					if (cur_size < min_size)
1343250963Sachim						min_size = cur_size;
1344250963Sachim				}
1345250963Sachim			}
1346250963Sachim
1347250963Sachim			/* not conformable: evaluate required sg elements */
1348250963Sachim			if (!conformable) {
1349250963Sachim				int j, err_found, nseg_new = nseg;
1350250963Sachim				for (i = min_size / PAGE_SIZE; i >= 1; --i) {
1351250963Sachim					err_found = FALSE;
1352250963Sachim					nseg_new = 2;
1353250963Sachim					for (j = 1; j < nseg - 1; ++j) {
1354250963Sachim						if (sg[j].length % (i*PAGE_SIZE)) {
1355250963Sachim							err_found = TRUE;
1356250963Sachim							break;
1357250963Sachim						}
1358250963Sachim						nseg_new += (sg[j].length / (i*PAGE_SIZE));
1359250963Sachim					}
1360250963Sachim					if (!err_found)
1361250963Sachim						break;
1362250963Sachim				}
1363250963Sachim				if (i>0 && nseg_new<=sc->aac_sg_tablesize &&
1364250963Sachim					!(sc->hint_flags & 4))
1365250963Sachim					nseg = aac_convert_sgraw2(sc,
1366250963Sachim						raw, i, nseg, nseg_new);
1367250963Sachim			} else {
1368250963Sachim				raw->flags |= RIO2_SGL_CONFORMANT;
1369250963Sachim			}
1370250963Sachim
1371250963Sachim			/* update the FIB size for the s/g count */
1372250963Sachim			fib->Header.Size += nseg *
1373250963Sachim				sizeof(struct aac_sge_ieee1212);
1374250963Sachim
1375250963Sachim		} else if (fib->Header.Command == RawIo) {
1376250963Sachim			struct aac_sg_tableraw *sg;
1377250963Sachim			sg = (struct aac_sg_tableraw *)cm->cm_sgtable;
1378250963Sachim			sg->SgCount = nseg;
1379250963Sachim			for (i = 0; i < nseg; i++) {
1380250963Sachim				sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr;
1381250963Sachim				sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len;
1382250963Sachim				sg->SgEntryRaw[i].Next = 0;
1383250963Sachim				sg->SgEntryRaw[i].Prev = 0;
1384250963Sachim				sg->SgEntryRaw[i].Flags = 0;
1385250963Sachim			}
1386250963Sachim			/* update the FIB size for the s/g count */
1387250963Sachim			fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw);
1388250963Sachim		} else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1389250963Sachim			struct aac_sg_table *sg;
1390250963Sachim			sg = cm->cm_sgtable;
1391250963Sachim			sg->SgCount = nseg;
1392250963Sachim			for (i = 0; i < nseg; i++) {
1393250963Sachim				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
1394250963Sachim				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
1395250963Sachim			}
1396250963Sachim			/* update the FIB size for the s/g count */
1397250963Sachim			fib->Header.Size += nseg*sizeof(struct aac_sg_entry);
1398250963Sachim		} else {
1399250963Sachim			struct aac_sg_table64 *sg;
1400250963Sachim			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1401250963Sachim			sg->SgCount = nseg;
1402250963Sachim			for (i = 0; i < nseg; i++) {
1403250963Sachim				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1404250963Sachim				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
1405250963Sachim			}
1406250963Sachim			/* update the FIB size for the s/g count */
1407250963Sachim			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1408250963Sachim		}
1409250963Sachim	}
1410250963Sachim
1411250963Sachim	/* Fix up the address values in the FIB.  Use the command array index
1412250963Sachim	 * instead of a pointer since these fields are only 32 bits.  Shift
1413250963Sachim	 * the SenderFibAddress over to make room for the fast response bit
1414250963Sachim	 * and for the AIF bit
1415250963Sachim	 */
1416250963Sachim	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2);
1417250963Sachim	cm->cm_fib->Header.u.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1418250963Sachim
1419250963Sachim	/* save a pointer to the command for speedy reverse-lookup */
1420250963Sachim	cm->cm_fib->Header.Handle += cm->cm_index + 1;
1421250963Sachim
1422250963Sachim	if (cm->cm_passthr_dmat == 0) {
1423250963Sachim		if (cm->cm_flags & AAC_CMD_DATAIN)
1424250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1425250963Sachim							BUS_DMASYNC_PREREAD);
1426250963Sachim		if (cm->cm_flags & AAC_CMD_DATAOUT)
1427250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1428250963Sachim							BUS_DMASYNC_PREWRITE);
1429250963Sachim	}
1430250963Sachim
1431250963Sachim	cm->cm_flags |= AAC_CMD_MAPPED;
1432250963Sachim
1433250963Sachim	if (sc->flags & AAC_FLAGS_SYNC_MODE) {
1434250963Sachim		u_int32_t wait = 0;
1435250963Sachim		aacraid_sync_command(sc, AAC_MONKER_SYNCFIB, cm->cm_fibphys, 0, 0, 0, &wait, NULL);
1436250963Sachim	} else if (cm->cm_flags & AAC_CMD_WAIT) {
1437250963Sachim		aacraid_sync_command(sc, AAC_MONKER_SYNCFIB, cm->cm_fibphys, 0, 0, 0, NULL, NULL);
1438250963Sachim	} else {
1439250963Sachim		int count = 10000000L;
1440250963Sachim		while (AAC_SEND_COMMAND(sc, cm) != 0) {
1441250963Sachim			if (--count == 0) {
1442250963Sachim				aac_unmap_command(cm);
1443250963Sachim				sc->flags |= AAC_QUEUE_FRZN;
1444250963Sachim				aac_requeue_ready(cm);
1445250963Sachim			}
1446250963Sachim			DELAY(5);			/* wait 5 usec. */
1447250963Sachim		}
1448250963Sachim	}
1449250963Sachim}
1450250963Sachim
1451250963Sachim
1452250963Sachimstatic int
1453250963Sachimaac_convert_sgraw2(struct aac_softc *sc, struct aac_raw_io2 *raw,
1454250963Sachim				   int pages, int nseg, int nseg_new)
1455250963Sachim{
1456250963Sachim	struct aac_sge_ieee1212 *sge;
1457250963Sachim	int i, j, pos;
1458250963Sachim	u_int32_t addr_low;
1459250963Sachim
1460250963Sachim	sge = malloc(nseg_new * sizeof(struct aac_sge_ieee1212),
1461250963Sachim		M_AACRAIDBUF, M_NOWAIT|M_ZERO);
1462250963Sachim	if (sge == NULL)
1463250963Sachim		return nseg;
1464250963Sachim
1465250963Sachim	for (i = 1, pos = 1; i < nseg - 1; ++i) {
1466250963Sachim		for (j = 0; j < raw->sge[i].length / (pages*PAGE_SIZE); ++j) {
1467250963Sachim			addr_low = raw->sge[i].addrLow + j * pages * PAGE_SIZE;
1468250963Sachim			sge[pos].addrLow = addr_low;
1469250963Sachim			sge[pos].addrHigh = raw->sge[i].addrHigh;
1470250963Sachim			if (addr_low < raw->sge[i].addrLow)
1471250963Sachim				sge[pos].addrHigh++;
1472250963Sachim			sge[pos].length = pages * PAGE_SIZE;
1473250963Sachim			sge[pos].flags = 0;
1474250963Sachim			pos++;
1475250963Sachim		}
1476250963Sachim	}
1477250963Sachim	sge[pos] = raw->sge[nseg-1];
1478250963Sachim	for (i = 1; i < nseg_new; ++i)
1479250963Sachim		raw->sge[i] = sge[i];
1480250963Sachim
1481250963Sachim	free(sge, M_AACRAIDBUF);
1482250963Sachim	raw->sgeCnt = nseg_new;
1483250963Sachim	raw->flags |= RIO2_SGL_CONFORMANT;
1484250963Sachim	raw->sgeNominalSize = pages * PAGE_SIZE;
1485250963Sachim	return nseg_new;
1486250963Sachim}
1487250963Sachim
1488250963Sachim
1489250963Sachim/*
1490250963Sachim * Unmap a command from controller-visible space.
1491250963Sachim */
1492250963Sachimstatic void
1493250963Sachimaac_unmap_command(struct aac_command *cm)
1494250963Sachim{
1495250963Sachim	struct aac_softc *sc;
1496250963Sachim
1497250963Sachim	sc = cm->cm_sc;
1498250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1499250963Sachim
1500250963Sachim	if (!(cm->cm_flags & AAC_CMD_MAPPED))
1501250963Sachim		return;
1502250963Sachim
1503250963Sachim	if (cm->cm_datalen != 0 && cm->cm_passthr_dmat == 0) {
1504250963Sachim		if (cm->cm_flags & AAC_CMD_DATAIN)
1505250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1506250963Sachim					BUS_DMASYNC_POSTREAD);
1507250963Sachim		if (cm->cm_flags & AAC_CMD_DATAOUT)
1508250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1509250963Sachim					BUS_DMASYNC_POSTWRITE);
1510250963Sachim
1511250963Sachim		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
1512250963Sachim	}
1513250963Sachim	cm->cm_flags &= ~AAC_CMD_MAPPED;
1514250963Sachim}
1515250963Sachim
1516250963Sachim/*
1517250963Sachim * Hardware Interface
1518250963Sachim */
1519250963Sachim
1520250963Sachim/*
1521250963Sachim * Initialize the adapter.
1522250963Sachim */
1523250963Sachimstatic void
1524250963Sachimaac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1525250963Sachim{
1526250963Sachim	struct aac_softc *sc;
1527250963Sachim
1528250963Sachim	sc = (struct aac_softc *)arg;
1529250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1530250963Sachim
1531250963Sachim	sc->aac_common_busaddr = segs[0].ds_addr;
1532250963Sachim}
1533250963Sachim
1534250963Sachimstatic int
1535250963Sachimaac_check_firmware(struct aac_softc *sc)
1536250963Sachim{
1537250963Sachim	u_int32_t code, major, minor, maxsize;
1538263340Sachim	u_int32_t options = 0, atu_size = 0, status, waitCount;
1539250963Sachim	time_t then;
1540250963Sachim
1541250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1542263340Sachim
1543263340Sachim	/* check if flash update is running */
1544263340Sachim	if (AAC_GET_FWSTATUS(sc) & AAC_FLASH_UPD_PENDING) {
1545263340Sachim		then = time_uptime;
1546263340Sachim		do {
1547263340Sachim			code = AAC_GET_FWSTATUS(sc);
1548263340Sachim			if (time_uptime > (then + AAC_FWUPD_TIMEOUT)) {
1549263340Sachim				device_printf(sc->aac_dev,
1550263340Sachim						  "FATAL: controller not coming ready, "
1551263340Sachim						   "status %x\n", code);
1552263340Sachim				return(ENXIO);
1553263340Sachim			}
1554263340Sachim		} while (!(code & AAC_FLASH_UPD_SUCCESS) && !(code & AAC_FLASH_UPD_FAILED));
1555263340Sachim		/*
1556263340Sachim		 * Delay 10 seconds. Because right now FW is doing a soft reset,
1557263340Sachim		 * do not read scratch pad register at this time
1558263340Sachim		 */
1559263340Sachim		waitCount = 10 * 10000;
1560263340Sachim		while (waitCount) {
1561263340Sachim			DELAY(100);		/* delay 100 microseconds */
1562263340Sachim			waitCount--;
1563263340Sachim		}
1564263340Sachim	}
1565263340Sachim
1566250963Sachim	/*
1567250963Sachim	 * Wait for the adapter to come ready.
1568250963Sachim	 */
1569250963Sachim	then = time_uptime;
1570250963Sachim	do {
1571250963Sachim		code = AAC_GET_FWSTATUS(sc);
1572250963Sachim		if (time_uptime > (then + AAC_BOOT_TIMEOUT)) {
1573250963Sachim			device_printf(sc->aac_dev,
1574250963Sachim				      "FATAL: controller not coming ready, "
1575250963Sachim					   "status %x\n", code);
1576250963Sachim			return(ENXIO);
1577250963Sachim		}
1578263340Sachim	} while (!(code & AAC_UP_AND_RUNNING) || code == 0xffffffff);
1579250963Sachim
1580250963Sachim	/*
1581250963Sachim	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1582250963Sachim	 * firmware version 1.x are not compatible with this driver.
1583250963Sachim	 */
1584250963Sachim	if (sc->flags & AAC_FLAGS_PERC2QC) {
1585250963Sachim		if (aacraid_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1586250963Sachim				     NULL, NULL)) {
1587250963Sachim			device_printf(sc->aac_dev,
1588250963Sachim				      "Error reading firmware version\n");
1589250963Sachim			return (EIO);
1590250963Sachim		}
1591250963Sachim
1592250963Sachim		/* These numbers are stored as ASCII! */
1593250963Sachim		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1594250963Sachim		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1595250963Sachim		if (major == 1) {
1596250963Sachim			device_printf(sc->aac_dev,
1597250963Sachim			    "Firmware version %d.%d is not supported.\n",
1598250963Sachim			    major, minor);
1599250963Sachim			return (EINVAL);
1600250963Sachim		}
1601250963Sachim	}
1602250963Sachim	/*
1603250963Sachim	 * Retrieve the capabilities/supported options word so we know what
1604250963Sachim	 * work-arounds to enable.  Some firmware revs don't support this
1605250963Sachim	 * command.
1606250963Sachim	 */
1607250963Sachim	if (aacraid_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status, NULL)) {
1608250963Sachim		if (status != AAC_SRB_STS_INVALID_REQUEST) {
1609250963Sachim			device_printf(sc->aac_dev,
1610250963Sachim			     "RequestAdapterInfo failed\n");
1611250963Sachim			return (EIO);
1612250963Sachim		}
1613250963Sachim	} else {
1614250963Sachim		options = AAC_GET_MAILBOX(sc, 1);
1615250963Sachim		atu_size = AAC_GET_MAILBOX(sc, 2);
1616250963Sachim		sc->supported_options = options;
1617250963Sachim
1618250963Sachim		if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1619250963Sachim		    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1620250963Sachim			sc->flags |= AAC_FLAGS_4GB_WINDOW;
1621250963Sachim		if (options & AAC_SUPPORTED_NONDASD)
1622250963Sachim			sc->flags |= AAC_FLAGS_ENABLE_CAM;
1623250963Sachim		if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1624250963Sachim			&& (sizeof(bus_addr_t) > 4)
1625250963Sachim			&& (sc->hint_flags & 0x1)) {
1626250963Sachim			device_printf(sc->aac_dev,
1627250963Sachim			    "Enabling 64-bit address support\n");
1628250963Sachim			sc->flags |= AAC_FLAGS_SG_64BIT;
1629250963Sachim		}
1630250963Sachim		if (sc->aac_if.aif_send_command) {
1631250963Sachim			if ((options & AAC_SUPPORTED_NEW_COMM_TYPE3) ||
1632250963Sachim				(options & AAC_SUPPORTED_NEW_COMM_TYPE4))
1633250963Sachim				sc->flags |= AAC_FLAGS_NEW_COMM | AAC_FLAGS_NEW_COMM_TYPE34;
1634250963Sachim			else if (options & AAC_SUPPORTED_NEW_COMM_TYPE1)
1635250963Sachim				sc->flags |= AAC_FLAGS_NEW_COMM | AAC_FLAGS_NEW_COMM_TYPE1;
1636250963Sachim			else if (options & AAC_SUPPORTED_NEW_COMM_TYPE2)
1637250963Sachim				sc->flags |= AAC_FLAGS_NEW_COMM | AAC_FLAGS_NEW_COMM_TYPE2;
1638250963Sachim		}
1639250963Sachim		if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE)
1640250963Sachim			sc->flags |= AAC_FLAGS_ARRAY_64BIT;
1641250963Sachim	}
1642250963Sachim
1643250963Sachim	if (!(sc->flags & AAC_FLAGS_NEW_COMM)) {
1644250963Sachim		device_printf(sc->aac_dev, "Communication interface not supported!\n");
1645250963Sachim		return (ENXIO);
1646250963Sachim	}
1647250963Sachim
1648250963Sachim	if (sc->hint_flags & 2) {
1649250963Sachim		device_printf(sc->aac_dev,
1650250963Sachim			"Sync. mode enforced by driver parameter. This will cause a significant performance decrease!\n");
1651250963Sachim		sc->flags |= AAC_FLAGS_SYNC_MODE;
1652250963Sachim	} else if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE34) {
1653250963Sachim		device_printf(sc->aac_dev,
1654250963Sachim			"Async. mode not supported by current driver, sync. mode enforced.\nPlease update driver to get full performance.\n");
1655250963Sachim		sc->flags |= AAC_FLAGS_SYNC_MODE;
1656250963Sachim	}
1657250963Sachim
1658250963Sachim	/* Check for broken hardware that does a lower number of commands */
1659250963Sachim	sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512);
1660250963Sachim
1661250963Sachim	/* Remap mem. resource, if required */
1662250963Sachim	if (atu_size > rman_get_size(sc->aac_regs_res0)) {
1663250963Sachim		bus_release_resource(
1664250963Sachim			sc->aac_dev, SYS_RES_MEMORY,
1665250963Sachim			sc->aac_regs_rid0, sc->aac_regs_res0);
1666250963Sachim		sc->aac_regs_res0 = bus_alloc_resource(
1667250963Sachim			sc->aac_dev, SYS_RES_MEMORY, &sc->aac_regs_rid0,
1668250963Sachim			0ul, ~0ul, atu_size, RF_ACTIVE);
1669250963Sachim		if (sc->aac_regs_res0 == NULL) {
1670250963Sachim			sc->aac_regs_res0 = bus_alloc_resource_any(
1671250963Sachim				sc->aac_dev, SYS_RES_MEMORY,
1672250963Sachim				&sc->aac_regs_rid0, RF_ACTIVE);
1673250963Sachim			if (sc->aac_regs_res0 == NULL) {
1674250963Sachim				device_printf(sc->aac_dev,
1675250963Sachim					"couldn't allocate register window\n");
1676250963Sachim				return (ENXIO);
1677250963Sachim			}
1678250963Sachim		}
1679250963Sachim		sc->aac_btag0 = rman_get_bustag(sc->aac_regs_res0);
1680250963Sachim		sc->aac_bhandle0 = rman_get_bushandle(sc->aac_regs_res0);
1681250963Sachim	}
1682250963Sachim
1683250963Sachim	/* Read preferred settings */
1684250963Sachim	sc->aac_max_fib_size = sizeof(struct aac_fib);
1685250963Sachim	sc->aac_max_sectors = 128;				/* 64KB */
1686250963Sachim	sc->aac_max_aif = 1;
1687250963Sachim	if (sc->flags & AAC_FLAGS_SG_64BIT)
1688250963Sachim		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
1689250963Sachim		 - sizeof(struct aac_blockwrite64))
1690250963Sachim		 / sizeof(struct aac_sg_entry64);
1691250963Sachim	else
1692250963Sachim		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
1693250963Sachim		 - sizeof(struct aac_blockwrite))
1694250963Sachim		 / sizeof(struct aac_sg_entry);
1695250963Sachim
1696250963Sachim	if (!aacraid_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL, NULL)) {
1697250963Sachim		options = AAC_GET_MAILBOX(sc, 1);
1698250963Sachim		sc->aac_max_fib_size = (options & 0xFFFF);
1699250963Sachim		sc->aac_max_sectors = (options >> 16) << 1;
1700250963Sachim		options = AAC_GET_MAILBOX(sc, 2);
1701250963Sachim		sc->aac_sg_tablesize = (options >> 16);
1702250963Sachim		options = AAC_GET_MAILBOX(sc, 3);
1703263340Sachim		sc->aac_max_fibs = ((options >> 16) & 0xFFFF);
1704263340Sachim		if (sc->aac_max_fibs == 0 || sc->aac_hwif != AAC_HWIF_SRCV)
1705263340Sachim			sc->aac_max_fibs = (options & 0xFFFF);
1706250963Sachim		options = AAC_GET_MAILBOX(sc, 4);
1707250963Sachim		sc->aac_max_aif = (options & 0xFFFF);
1708263340Sachim		options = AAC_GET_MAILBOX(sc, 5);
1709263340Sachim		sc->aac_max_msix =(sc->flags & AAC_FLAGS_NEW_COMM_TYPE2) ? options : 0;
1710250963Sachim	}
1711250963Sachim
1712250963Sachim	maxsize = sc->aac_max_fib_size + 31;
1713250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1)
1714250963Sachim		maxsize += sizeof(struct aac_fib_xporthdr);
1715250963Sachim	if (maxsize > PAGE_SIZE) {
1716250963Sachim    	sc->aac_max_fib_size -= (maxsize - PAGE_SIZE);
1717250963Sachim		maxsize = PAGE_SIZE;
1718250963Sachim	}
1719250963Sachim	sc->aac_max_fibs_alloc = PAGE_SIZE / maxsize;
1720250963Sachim
1721250963Sachim	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1722250963Sachim		sc->flags |= AAC_FLAGS_RAW_IO;
1723250963Sachim		device_printf(sc->aac_dev, "Enable Raw I/O\n");
1724250963Sachim	}
1725250963Sachim	if ((sc->flags & AAC_FLAGS_RAW_IO) &&
1726250963Sachim	    (sc->flags & AAC_FLAGS_ARRAY_64BIT)) {
1727250963Sachim		sc->flags |= AAC_FLAGS_LBA_64BIT;
1728250963Sachim		device_printf(sc->aac_dev, "Enable 64-bit array\n");
1729250963Sachim	}
1730250963Sachim
1731263340Sachim#ifdef AACRAID_DEBUG
1732250963Sachim	aacraid_get_fw_debug_buffer(sc);
1733263340Sachim#endif
1734250963Sachim	return (0);
1735250963Sachim}
1736250963Sachim
1737250963Sachimstatic int
1738250963Sachimaac_init(struct aac_softc *sc)
1739250963Sachim{
1740250963Sachim	struct aac_adapter_init	*ip;
1741263340Sachim	int i, error;
1742250963Sachim
1743250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1744250963Sachim
1745250963Sachim	/* reset rrq index */
1746263340Sachim	sc->aac_fibs_pushed_no = 0;
1747263340Sachim	for (i = 0; i < sc->aac_max_msix; i++)
1748263340Sachim		sc->aac_host_rrq_idx[i] = i * sc->aac_vector_cap;
1749250963Sachim
1750250963Sachim	/*
1751250963Sachim	 * Fill in the init structure.  This tells the adapter about the
1752250963Sachim	 * physical location of various important shared data structures.
1753250963Sachim	 */
1754250963Sachim	ip = &sc->aac_common->ac_init;
1755250963Sachim	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1756250963Sachim	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1757250963Sachim		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4;
1758250963Sachim		sc->flags |= AAC_FLAGS_RAW_IO;
1759250963Sachim	}
1760263340Sachim	ip->NoOfMSIXVectors = sc->aac_max_msix;
1761250963Sachim
1762250963Sachim	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1763250963Sachim					 offsetof(struct aac_common, ac_fibs);
1764250963Sachim	ip->AdapterFibsVirtualAddress = 0;
1765250963Sachim	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
1766250963Sachim	ip->AdapterFibAlign = sizeof(struct aac_fib);
1767250963Sachim
1768250963Sachim	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1769250963Sachim				  offsetof(struct aac_common, ac_printf);
1770250963Sachim	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
1771250963Sachim
1772250963Sachim	/*
1773250963Sachim	 * The adapter assumes that pages are 4K in size, except on some
1774250963Sachim 	 * broken firmware versions that do the page->byte conversion twice,
1775250963Sachim	 * therefore 'assuming' that this value is in 16MB units (2^24).
1776250963Sachim	 * Round up since the granularity is so high.
1777250963Sachim	 */
1778250963Sachim	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
1779250963Sachim	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
1780250963Sachim		ip->HostPhysMemPages =
1781250963Sachim		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1782250963Sachim	}
1783250963Sachim	ip->HostElapsedSeconds = time_uptime;	/* reset later if invalid */
1784250963Sachim
1785250963Sachim	ip->InitFlags = AAC_INITFLAGS_NEW_COMM_SUPPORTED;
1786250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1) {
1787250963Sachim		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_6;
1788250963Sachim		ip->InitFlags |= (AAC_INITFLAGS_NEW_COMM_TYPE1_SUPPORTED |
1789250963Sachim			AAC_INITFLAGS_FAST_JBOD_SUPPORTED);
1790250963Sachim		device_printf(sc->aac_dev, "New comm. interface type1 enabled\n");
1791250963Sachim	} else if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE2) {
1792250963Sachim		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_7;
1793250963Sachim		ip->InitFlags |= (AAC_INITFLAGS_NEW_COMM_TYPE2_SUPPORTED |
1794250963Sachim			AAC_INITFLAGS_FAST_JBOD_SUPPORTED);
1795250963Sachim		device_printf(sc->aac_dev, "New comm. interface type2 enabled\n");
1796250963Sachim	}
1797250963Sachim	ip->MaxNumAif = sc->aac_max_aif;
1798250963Sachim	ip->HostRRQ_AddrLow =
1799250963Sachim		sc->aac_common_busaddr + offsetof(struct aac_common, ac_host_rrq);
1800250963Sachim	/* always 32-bit address */
1801250963Sachim	ip->HostRRQ_AddrHigh = 0;
1802250963Sachim
1803250963Sachim	if (sc->aac_support_opt2 & AAC_SUPPORTED_POWER_MANAGEMENT) {
1804250963Sachim		ip->InitFlags |= AAC_INITFLAGS_DRIVER_SUPPORTS_PM;
1805250963Sachim		ip->InitFlags |= AAC_INITFLAGS_DRIVER_USES_UTC_TIME;
1806250963Sachim		device_printf(sc->aac_dev, "Power Management enabled\n");
1807250963Sachim	}
1808250963Sachim
1809250963Sachim	ip->MaxIoCommands = sc->aac_max_fibs;
1810250963Sachim	ip->MaxIoSize = sc->aac_max_sectors << 9;
1811250963Sachim	ip->MaxFibSize = sc->aac_max_fib_size;
1812250963Sachim
1813250963Sachim	/*
1814250963Sachim	 * Do controller-type-specific initialisation
1815250963Sachim	 */
1816250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, ~0);
1817250963Sachim
1818250963Sachim	/*
1819250963Sachim	 * Give the init structure to the controller.
1820250963Sachim	 */
1821250963Sachim	if (aacraid_sync_command(sc, AAC_MONKER_INITSTRUCT,
1822250963Sachim			     sc->aac_common_busaddr +
1823250963Sachim			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1824250963Sachim			     NULL, NULL)) {
1825250963Sachim		device_printf(sc->aac_dev,
1826250963Sachim			      "error establishing init structure\n");
1827250963Sachim		error = EIO;
1828250963Sachim		goto out;
1829250963Sachim	}
1830250963Sachim
1831263340Sachim	/*
1832263340Sachim	 * Check configuration issues
1833263340Sachim	 */
1834263340Sachim	if ((error = aac_check_config(sc)) != 0)
1835263340Sachim		goto out;
1836263340Sachim
1837250963Sachim	error = 0;
1838250963Sachimout:
1839250963Sachim	return(error);
1840250963Sachim}
1841250963Sachim
1842263340Sachimstatic void
1843263340Sachimaac_define_int_mode(struct aac_softc *sc)
1844263340Sachim{
1845263340Sachim	device_t dev;
1846263340Sachim	int cap, msi_count, error = 0;
1847263340Sachim	uint32_t val;
1848263340Sachim
1849263340Sachim	dev = sc->aac_dev;
1850263340Sachim
1851263340Sachim	/* max. vectors from AAC_MONKER_GETCOMMPREF */
1852263340Sachim	if (sc->aac_max_msix == 0) {
1853263340Sachim		sc->aac_max_msix = 1;
1854263340Sachim		sc->aac_vector_cap = sc->aac_max_fibs;
1855263340Sachim		return;
1856263340Sachim	}
1857263340Sachim
1858263340Sachim	/* OS capability */
1859263340Sachim	msi_count = pci_msix_count(dev);
1860263340Sachim	if (msi_count > AAC_MAX_MSIX)
1861263340Sachim		msi_count = AAC_MAX_MSIX;
1862263340Sachim	if (msi_count > sc->aac_max_msix)
1863263340Sachim		msi_count = sc->aac_max_msix;
1864263340Sachim	if (msi_count == 0 || (error = pci_alloc_msix(dev, &msi_count)) != 0) {
1865263340Sachim		device_printf(dev, "alloc msix failed - msi_count=%d, err=%d; "
1866263340Sachim				   "will try MSI\n", msi_count, error);
1867263340Sachim		pci_release_msi(dev);
1868263340Sachim	} else {
1869263340Sachim		sc->msi_enabled = TRUE;
1870263340Sachim		device_printf(dev, "using MSI-X interrupts (%u vectors)\n",
1871263340Sachim			msi_count);
1872263340Sachim	}
1873263340Sachim
1874263340Sachim	if (!sc->msi_enabled) {
1875263340Sachim		msi_count = 1;
1876263340Sachim		if ((error = pci_alloc_msi(dev, &msi_count)) != 0) {
1877263340Sachim			device_printf(dev, "alloc msi failed - err=%d; "
1878263340Sachim				           "will use INTx\n", error);
1879263340Sachim			pci_release_msi(dev);
1880263340Sachim		} else {
1881263340Sachim			sc->msi_enabled = TRUE;
1882263340Sachim			device_printf(dev, "using MSI interrupts\n");
1883263340Sachim		}
1884263340Sachim	}
1885263340Sachim
1886263340Sachim	if (sc->msi_enabled) {
1887263340Sachim		/* now read controller capability from PCI config. space */
1888263340Sachim		cap = aac_find_pci_capability(sc, PCIY_MSIX);
1889263340Sachim		val = (cap != 0 ? pci_read_config(dev, cap + 2, 2) : 0);
1890263340Sachim		if (!(val & AAC_PCI_MSI_ENABLE)) {
1891263340Sachim			pci_release_msi(dev);
1892263340Sachim			sc->msi_enabled = FALSE;
1893263340Sachim		}
1894263340Sachim	}
1895263340Sachim
1896263340Sachim	if (!sc->msi_enabled) {
1897263340Sachim		device_printf(dev, "using legacy interrupts\n");
1898263340Sachim		sc->aac_max_msix = 1;
1899263340Sachim	} else {
1900263340Sachim		AAC_ACCESS_DEVREG(sc, AAC_ENABLE_MSIX);
1901263340Sachim		if (sc->aac_max_msix > msi_count)
1902263340Sachim			sc->aac_max_msix = msi_count;
1903263340Sachim	}
1904263340Sachim	sc->aac_vector_cap = sc->aac_max_fibs / sc->aac_max_msix;
1905263340Sachim
1906263340Sachim	fwprintf(sc, HBA_FLAGS_DBG_DEBUG_B, "msi_enabled %d vector_cap %d max_fibs %d max_msix %d",
1907263340Sachim		sc->msi_enabled,sc->aac_vector_cap, sc->aac_max_fibs, sc->aac_max_msix);
1908263340Sachim}
1909263340Sachim
1910250963Sachimstatic int
1911263340Sachimaac_find_pci_capability(struct aac_softc *sc, int cap)
1912250963Sachim{
1913263340Sachim	device_t dev;
1914263340Sachim	uint32_t status;
1915263340Sachim	uint8_t ptr;
1916263340Sachim
1917263340Sachim	dev = sc->aac_dev;
1918263340Sachim
1919263340Sachim	status = pci_read_config(dev, PCIR_STATUS, 2);
1920263340Sachim	if (!(status & PCIM_STATUS_CAPPRESENT))
1921263340Sachim		return (0);
1922263340Sachim
1923263340Sachim	status = pci_read_config(dev, PCIR_HDRTYPE, 1);
1924263340Sachim	switch (status & PCIM_HDRTYPE) {
1925263340Sachim	case 0:
1926263340Sachim	case 1:
1927263340Sachim		ptr = PCIR_CAP_PTR;
1928263340Sachim		break;
1929263340Sachim	case 2:
1930263340Sachim		ptr = PCIR_CAP_PTR_2;
1931263340Sachim		break;
1932263340Sachim	default:
1933263340Sachim		return (0);
1934263340Sachim		break;
1935250963Sachim	}
1936263340Sachim	ptr = pci_read_config(dev, ptr, 1);
1937263340Sachim
1938263340Sachim	while (ptr != 0) {
1939263340Sachim		int next, val;
1940263340Sachim		next = pci_read_config(dev, ptr + PCICAP_NEXTPTR, 1);
1941263340Sachim		val = pci_read_config(dev, ptr + PCICAP_ID, 1);
1942263340Sachim		if (val == cap)
1943263340Sachim			return (ptr);
1944263340Sachim		ptr = next;
1945250963Sachim	}
1946263340Sachim
1947250963Sachim	return (0);
1948250963Sachim}
1949250963Sachim
1950263340Sachimstatic int
1951263340Sachimaac_setup_intr(struct aac_softc *sc)
1952263340Sachim{
1953263340Sachim	int i, msi_count, rid;
1954263340Sachim	struct resource *res;
1955263340Sachim	void *tag;
1956263340Sachim
1957263340Sachim	msi_count = sc->aac_max_msix;
1958263340Sachim	rid = (sc->msi_enabled ? 1:0);
1959263340Sachim
1960263340Sachim	for (i = 0; i < msi_count; i++, rid++) {
1961263340Sachim		if ((res = bus_alloc_resource_any(sc->aac_dev,SYS_RES_IRQ, &rid,
1962263340Sachim			RF_SHAREABLE | RF_ACTIVE)) == NULL) {
1963263340Sachim			device_printf(sc->aac_dev,"can't allocate interrupt\n");
1964263340Sachim			return (EINVAL);
1965263340Sachim		}
1966263340Sachim		sc->aac_irq_rid[i] = rid;
1967263340Sachim		sc->aac_irq[i] = res;
1968263340Sachim		if (aac_bus_setup_intr(sc->aac_dev, res,
1969263340Sachim			INTR_MPSAFE | INTR_TYPE_BIO, NULL,
1970263340Sachim			aacraid_new_intr_type1, &sc->aac_msix[i], &tag)) {
1971263340Sachim			device_printf(sc->aac_dev, "can't set up interrupt\n");
1972263340Sachim			return (EINVAL);
1973263340Sachim		}
1974263340Sachim		sc->aac_msix[i].vector_no = i;
1975263340Sachim		sc->aac_msix[i].sc = sc;
1976263340Sachim		sc->aac_intr[i] = tag;
1977263340Sachim	}
1978263340Sachim
1979263340Sachim	return (0);
1980263340Sachim}
1981263340Sachim
1982263340Sachimstatic int
1983263340Sachimaac_check_config(struct aac_softc *sc)
1984263340Sachim{
1985263340Sachim	struct aac_fib *fib;
1986263340Sachim	struct aac_cnt_config *ccfg;
1987263340Sachim	struct aac_cf_status_hdr *cf_shdr;
1988263340Sachim	int rval;
1989263340Sachim
1990263340Sachim	mtx_lock(&sc->aac_io_lock);
1991263340Sachim	aac_alloc_sync_fib(sc, &fib);
1992263340Sachim
1993263340Sachim	ccfg = (struct aac_cnt_config *)&fib->data[0];
1994263340Sachim	bzero(ccfg, sizeof (*ccfg) - CT_PACKET_SIZE);
1995263340Sachim	ccfg->Command = VM_ContainerConfig;
1996263340Sachim	ccfg->CTCommand.command = CT_GET_CONFIG_STATUS;
1997263340Sachim	ccfg->CTCommand.param[CNT_SIZE] = sizeof(struct aac_cf_status_hdr);
1998263340Sachim
1999263340Sachim	rval = aac_sync_fib(sc, ContainerCommand, 0, fib,
2000263340Sachim		sizeof (struct aac_cnt_config));
2001263340Sachim	cf_shdr = (struct aac_cf_status_hdr *)ccfg->CTCommand.data;
2002263340Sachim	if (rval == 0 && ccfg->Command == ST_OK &&
2003263340Sachim		ccfg->CTCommand.param[0] == CT_OK) {
2004263340Sachim		if (cf_shdr->action <= CFACT_PAUSE) {
2005263340Sachim			bzero(ccfg, sizeof (*ccfg) - CT_PACKET_SIZE);
2006263340Sachim			ccfg->Command = VM_ContainerConfig;
2007263340Sachim			ccfg->CTCommand.command = CT_COMMIT_CONFIG;
2008263340Sachim
2009263340Sachim			rval = aac_sync_fib(sc, ContainerCommand, 0, fib,
2010263340Sachim				sizeof (struct aac_cnt_config));
2011263340Sachim			if (rval == 0 && ccfg->Command == ST_OK &&
2012263340Sachim				ccfg->CTCommand.param[0] == CT_OK) {
2013263340Sachim				/* successful completion */
2014263340Sachim				rval = 0;
2015263340Sachim			} else {
2016263340Sachim				/* auto commit aborted due to error(s) */
2017263340Sachim				rval = -2;
2018263340Sachim			}
2019263340Sachim		} else {
2020263340Sachim			/* auto commit aborted due to adapter indicating
2021263340Sachim			   config. issues too dangerous to auto commit  */
2022263340Sachim			rval = -3;
2023263340Sachim		}
2024263340Sachim	} else {
2025263340Sachim		/* error */
2026263340Sachim		rval = -1;
2027263340Sachim	}
2028263340Sachim
2029263340Sachim	aac_release_sync_fib(sc);
2030263340Sachim	mtx_unlock(&sc->aac_io_lock);
2031263340Sachim	return(rval);
2032263340Sachim}
2033263340Sachim
2034250963Sachim/*
2035250963Sachim * Send a synchronous command to the controller and wait for a result.
2036250963Sachim * Indicate if the controller completed the command with an error status.
2037250963Sachim */
2038250963Sachimint
2039250963Sachimaacraid_sync_command(struct aac_softc *sc, u_int32_t command,
2040250963Sachim		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
2041250963Sachim		 u_int32_t *sp, u_int32_t *r1)
2042250963Sachim{
2043250963Sachim	time_t then;
2044250963Sachim	u_int32_t status;
2045250963Sachim
2046250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2047250963Sachim
2048250963Sachim	/* populate the mailbox */
2049250963Sachim	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
2050250963Sachim
2051250963Sachim	/* ensure the sync command doorbell flag is cleared */
2052263340Sachim	if (!sc->msi_enabled)
2053263340Sachim		AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
2054250963Sachim
2055250963Sachim	/* then set it to signal the adapter */
2056250963Sachim	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
2057250963Sachim
2058250963Sachim	if ((command != AAC_MONKER_SYNCFIB) || (sp == NULL) || (*sp != 0)) {
2059250963Sachim		/* spin waiting for the command to complete */
2060250963Sachim		then = time_uptime;
2061250963Sachim		do {
2062263340Sachim			if (time_uptime > (then + AAC_SYNC_TIMEOUT)) {
2063250963Sachim				fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out");
2064250963Sachim				return(EIO);
2065250963Sachim			}
2066250963Sachim		} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
2067250963Sachim
2068250963Sachim		/* clear the completion flag */
2069250963Sachim		AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
2070250963Sachim
2071250963Sachim		/* get the command status */
2072250963Sachim		status = AAC_GET_MAILBOX(sc, 0);
2073250963Sachim		if (sp != NULL)
2074250963Sachim			*sp = status;
2075250963Sachim
2076250963Sachim		/* return parameter */
2077250963Sachim		if (r1 != NULL)
2078250963Sachim			*r1 = AAC_GET_MAILBOX(sc, 1);
2079250963Sachim
2080250963Sachim		if (status != AAC_SRB_STS_SUCCESS)
2081250963Sachim			return (-1);
2082250963Sachim	}
2083250963Sachim	return(0);
2084250963Sachim}
2085250963Sachim
2086250963Sachimstatic int
2087250963Sachimaac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
2088250963Sachim		 struct aac_fib *fib, u_int16_t datasize)
2089250963Sachim{
2090250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2091250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
2092250963Sachim
2093250963Sachim	if (datasize > AAC_FIB_DATASIZE)
2094250963Sachim		return(EINVAL);
2095250963Sachim
2096250963Sachim	/*
2097250963Sachim	 * Set up the sync FIB
2098250963Sachim	 */
2099250963Sachim	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
2100250963Sachim				AAC_FIBSTATE_INITIALISED |
2101250963Sachim				AAC_FIBSTATE_EMPTY;
2102250963Sachim	fib->Header.XferState |= xferstate;
2103250963Sachim	fib->Header.Command = command;
2104250963Sachim	fib->Header.StructType = AAC_FIBTYPE_TFIB;
2105250963Sachim	fib->Header.Size = sizeof(struct aac_fib_header) + datasize;
2106250963Sachim	fib->Header.SenderSize = sizeof(struct aac_fib);
2107250963Sachim	fib->Header.SenderFibAddress = 0;	/* Not needed */
2108250963Sachim	fib->Header.u.ReceiverFibAddress = sc->aac_common_busaddr +
2109263340Sachim		offsetof(struct aac_common, ac_sync_fib);
2110250963Sachim
2111250963Sachim	/*
2112250963Sachim	 * Give the FIB to the controller, wait for a response.
2113250963Sachim	 */
2114250963Sachim	if (aacraid_sync_command(sc, AAC_MONKER_SYNCFIB,
2115263340Sachim		fib->Header.u.ReceiverFibAddress, 0, 0, 0, NULL, NULL)) {
2116250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error");
2117250963Sachim		return(EIO);
2118250963Sachim	}
2119250963Sachim
2120250963Sachim	return (0);
2121250963Sachim}
2122250963Sachim
2123250963Sachim/*
2124250963Sachim * Check for commands that have been outstanding for a suspiciously long time,
2125250963Sachim * and complain about them.
2126250963Sachim */
2127250963Sachimstatic void
2128250963Sachimaac_timeout(struct aac_softc *sc)
2129250963Sachim{
2130250963Sachim	struct aac_command *cm;
2131250963Sachim	time_t deadline;
2132263340Sachim	int timedout;
2133250963Sachim
2134250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2135250963Sachim	/*
2136250963Sachim	 * Traverse the busy command list, bitch about late commands once
2137250963Sachim	 * only.
2138250963Sachim	 */
2139250963Sachim	timedout = 0;
2140250963Sachim	deadline = time_uptime - AAC_CMD_TIMEOUT;
2141250963Sachim	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2142263340Sachim		if (cm->cm_timestamp < deadline) {
2143250963Sachim			device_printf(sc->aac_dev,
2144250963Sachim				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
2145250963Sachim				      cm, (int)(time_uptime-cm->cm_timestamp));
2146250963Sachim			AAC_PRINT_FIB(sc, cm->cm_fib);
2147250963Sachim			timedout++;
2148250963Sachim		}
2149250963Sachim	}
2150250963Sachim
2151263340Sachim	if (timedout)
2152263340Sachim		aac_reset_adapter(sc);
2153250963Sachim	aacraid_print_queues(sc);
2154250963Sachim}
2155250963Sachim
2156250963Sachim/*
2157250963Sachim * Interface Function Vectors
2158250963Sachim */
2159250963Sachim
2160250963Sachim/*
2161250963Sachim * Read the current firmware status word.
2162250963Sachim */
2163250963Sachimstatic int
2164250963Sachimaac_src_get_fwstatus(struct aac_softc *sc)
2165250963Sachim{
2166250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2167250963Sachim
2168250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRC_OMR));
2169250963Sachim}
2170250963Sachim
2171250963Sachim/*
2172250963Sachim * Notify the controller of a change in a given queue
2173250963Sachim */
2174250963Sachimstatic void
2175250963Sachimaac_src_qnotify(struct aac_softc *sc, int qbit)
2176250963Sachim{
2177250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2178250963Sachim
2179250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, qbit << AAC_SRC_IDR_SHIFT);
2180250963Sachim}
2181250963Sachim
2182250963Sachim/*
2183250963Sachim * Get the interrupt reason bits
2184250963Sachim */
2185250963Sachimstatic int
2186250963Sachimaac_src_get_istatus(struct aac_softc *sc)
2187250963Sachim{
2188263340Sachim	int val;
2189263340Sachim
2190250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2191250963Sachim
2192263340Sachim	if (sc->msi_enabled) {
2193263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_MSI);
2194263340Sachim		if (val & AAC_MSI_SYNC_STATUS)
2195263340Sachim			val = AAC_DB_SYNC_COMMAND;
2196263340Sachim		else
2197263340Sachim			val = 0;
2198263340Sachim	} else {
2199263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_R) >> AAC_SRC_ODR_SHIFT;
2200263340Sachim	}
2201263340Sachim	return(val);
2202250963Sachim}
2203250963Sachim
2204250963Sachim/*
2205250963Sachim * Clear some interrupt reason bits
2206250963Sachim */
2207250963Sachimstatic void
2208250963Sachimaac_src_clear_istatus(struct aac_softc *sc, int mask)
2209250963Sachim{
2210250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2211250963Sachim
2212263340Sachim	if (sc->msi_enabled) {
2213263340Sachim		if (mask == AAC_DB_SYNC_COMMAND)
2214263340Sachim			AAC_ACCESS_DEVREG(sc, AAC_CLEAR_SYNC_BIT);
2215263340Sachim	} else {
2216263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, mask << AAC_SRC_ODR_SHIFT);
2217263340Sachim	}
2218250963Sachim}
2219250963Sachim
2220250963Sachim/*
2221250963Sachim * Populate the mailbox and set the command word
2222250963Sachim */
2223250963Sachimstatic void
2224250963Sachimaac_src_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
2225250963Sachim		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2226250963Sachim{
2227250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2228250963Sachim
2229250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX, command);
2230250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 4, arg0);
2231250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 8, arg1);
2232250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 12, arg2);
2233250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 16, arg3);
2234250963Sachim}
2235250963Sachim
2236250963Sachimstatic void
2237250963Sachimaac_srcv_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
2238250963Sachim		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2239250963Sachim{
2240250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2241250963Sachim
2242250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX, command);
2243250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 4, arg0);
2244250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 8, arg1);
2245250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 12, arg2);
2246250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 16, arg3);
2247250963Sachim}
2248250963Sachim
2249250963Sachim/*
2250250963Sachim * Fetch the immediate command status word
2251250963Sachim */
2252250963Sachimstatic int
2253250963Sachimaac_src_get_mailbox(struct aac_softc *sc, int mb)
2254250963Sachim{
2255250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2256250963Sachim
2257250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRC_MAILBOX + (mb * 4)));
2258250963Sachim}
2259250963Sachim
2260250963Sachimstatic int
2261250963Sachimaac_srcv_get_mailbox(struct aac_softc *sc, int mb)
2262250963Sachim{
2263250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2264250963Sachim
2265250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRCV_MAILBOX + (mb * 4)));
2266250963Sachim}
2267250963Sachim
2268250963Sachim/*
2269250963Sachim * Set/clear interrupt masks
2270250963Sachim */
2271250963Sachimstatic void
2272263340Sachimaac_src_access_devreg(struct aac_softc *sc, int mode)
2273250963Sachim{
2274263340Sachim	u_int32_t val;
2275250963Sachim
2276263340Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2277263340Sachim
2278263340Sachim	switch (mode) {
2279263340Sachim	case AAC_ENABLE_INTERRUPT:
2280263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_OIMR,
2281263340Sachim			(sc->msi_enabled ? AAC_INT_ENABLE_TYPE1_MSIX :
2282263340Sachim				           AAC_INT_ENABLE_TYPE1_INTX));
2283263340Sachim		break;
2284263340Sachim
2285263340Sachim	case AAC_DISABLE_INTERRUPT:
2286263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_OIMR, AAC_INT_DISABLE_ALL);
2287263340Sachim		break;
2288263340Sachim
2289263340Sachim	case AAC_ENABLE_MSIX:
2290263340Sachim		/* set bit 6 */
2291263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2292263340Sachim		val |= 0x40;
2293263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, val);
2294263340Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2295263340Sachim		/* unmask int. */
2296263340Sachim		val = PMC_ALL_INTERRUPT_BITS;
2297263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IOAR, val);
2298263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_OIMR);
2299263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_OIMR,
2300263340Sachim			val & (~(PMC_GLOBAL_INT_BIT2 | PMC_GLOBAL_INT_BIT0)));
2301263340Sachim		break;
2302263340Sachim
2303263340Sachim	case AAC_DISABLE_MSIX:
2304263340Sachim		/* reset bit 6 */
2305263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2306263340Sachim		val &= ~0x40;
2307263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, val);
2308263340Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2309263340Sachim		break;
2310263340Sachim
2311263340Sachim	case AAC_CLEAR_AIF_BIT:
2312263340Sachim		/* set bit 5 */
2313263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2314263340Sachim		val |= 0x20;
2315263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, val);
2316263340Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2317263340Sachim		break;
2318263340Sachim
2319263340Sachim	case AAC_CLEAR_SYNC_BIT:
2320263340Sachim		/* set bit 4 */
2321263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2322263340Sachim		val |= 0x10;
2323263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, val);
2324263340Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2325263340Sachim		break;
2326263340Sachim
2327263340Sachim	case AAC_ENABLE_INTX:
2328263340Sachim		/* set bit 7 */
2329263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2330263340Sachim		val |= 0x80;
2331263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, val);
2332263340Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_IDBR);
2333263340Sachim		/* unmask int. */
2334263340Sachim		val = PMC_ALL_INTERRUPT_BITS;
2335263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IOAR, val);
2336263340Sachim		val = AAC_MEM0_GETREG4(sc, AAC_SRC_OIMR);
2337263340Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_OIMR,
2338263340Sachim			val & (~(PMC_GLOBAL_INT_BIT2)));
2339263340Sachim		break;
2340263340Sachim
2341263340Sachim	default:
2342263340Sachim		break;
2343250963Sachim	}
2344250963Sachim}
2345250963Sachim
2346250963Sachim/*
2347250963Sachim * New comm. interface: Send command functions
2348250963Sachim */
2349250963Sachimstatic int
2350250963Sachimaac_src_send_command(struct aac_softc *sc, struct aac_command *cm)
2351250963Sachim{
2352250963Sachim	struct aac_fib_xporthdr *pFibX;
2353250963Sachim	u_int32_t fibsize, high_addr;
2354250963Sachim	u_int64_t address;
2355250963Sachim
2356250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm. type1)");
2357250963Sachim
2358263340Sachim	if (sc->msi_enabled && cm->cm_fib->Header.Command != AifRequest &&
2359263340Sachim		sc->aac_max_msix > 1) {
2360263340Sachim		u_int16_t vector_no, first_choice = 0xffff;
2361263340Sachim
2362263340Sachim		vector_no = sc->aac_fibs_pushed_no % sc->aac_max_msix;
2363263340Sachim		do {
2364263340Sachim			vector_no += 1;
2365263340Sachim			if (vector_no == sc->aac_max_msix)
2366263340Sachim				vector_no = 1;
2367263340Sachim			if (sc->aac_rrq_outstanding[vector_no] <
2368263340Sachim				sc->aac_vector_cap)
2369263340Sachim				break;
2370263340Sachim			if (0xffff == first_choice)
2371263340Sachim				first_choice = vector_no;
2372263340Sachim			else if (vector_no == first_choice)
2373263340Sachim				break;
2374263340Sachim		} while (1);
2375263340Sachim		if (vector_no == first_choice)
2376263340Sachim			vector_no = 0;
2377263340Sachim		sc->aac_rrq_outstanding[vector_no]++;
2378263340Sachim		if (sc->aac_fibs_pushed_no == 0xffffffff)
2379263340Sachim			sc->aac_fibs_pushed_no = 0;
2380263340Sachim		else
2381263340Sachim			sc->aac_fibs_pushed_no++;
2382263340Sachim
2383263340Sachim		cm->cm_fib->Header.Handle += (vector_no << 16);
2384263340Sachim	}
2385263340Sachim
2386250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE2) {
2387250963Sachim		/* Calculate the amount to the fibsize bits */
2388250963Sachim		fibsize = (cm->cm_fib->Header.Size + 127) / 128 - 1;
2389250963Sachim		/* Fill new FIB header */
2390250963Sachim		address = cm->cm_fibphys;
2391250963Sachim		high_addr = (u_int32_t)(address >> 32);
2392250963Sachim		if (high_addr == 0L) {
2393250963Sachim			cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB2;
2394250963Sachim			cm->cm_fib->Header.u.TimeStamp = 0L;
2395250963Sachim		} else {
2396250963Sachim			cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB2_64;
2397250963Sachim			cm->cm_fib->Header.u.SenderFibAddressHigh = high_addr;
2398250963Sachim		}
2399250963Sachim		cm->cm_fib->Header.SenderFibAddress = (u_int32_t)address;
2400250963Sachim	} else {
2401250963Sachim		/* Calculate the amount to the fibsize bits */
2402250963Sachim		fibsize = (sizeof(struct aac_fib_xporthdr) +
2403250963Sachim		   cm->cm_fib->Header.Size + 127) / 128 - 1;
2404250963Sachim		/* Fill XPORT header */
2405250963Sachim		pFibX = (struct aac_fib_xporthdr *)
2406250963Sachim			((unsigned char *)cm->cm_fib - sizeof(struct aac_fib_xporthdr));
2407250963Sachim		pFibX->Handle = cm->cm_fib->Header.Handle;
2408250963Sachim		pFibX->HostAddress = cm->cm_fibphys;
2409250963Sachim		pFibX->Size = cm->cm_fib->Header.Size;
2410250963Sachim		address = cm->cm_fibphys - sizeof(struct aac_fib_xporthdr);
2411250963Sachim		high_addr = (u_int32_t)(address >> 32);
2412250963Sachim	}
2413250963Sachim
2414250963Sachim	if (fibsize > 31)
2415250963Sachim		fibsize = 31;
2416250963Sachim	aac_enqueue_busy(cm);
2417250963Sachim	if (high_addr) {
2418250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IQUE64_H, high_addr);
2419250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IQUE64_L, (u_int32_t)address + fibsize);
2420250963Sachim	} else {
2421250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IQUE32, (u_int32_t)address + fibsize);
2422250963Sachim	}
2423250963Sachim	return 0;
2424250963Sachim}
2425250963Sachim
2426250963Sachim/*
2427250963Sachim * New comm. interface: get, set outbound queue index
2428250963Sachim */
2429250963Sachimstatic int
2430250963Sachimaac_src_get_outb_queue(struct aac_softc *sc)
2431250963Sachim{
2432250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2433250963Sachim
2434250963Sachim	return(-1);
2435250963Sachim}
2436250963Sachim
2437250963Sachimstatic void
2438250963Sachimaac_src_set_outb_queue(struct aac_softc *sc, int index)
2439250963Sachim{
2440250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2441250963Sachim}
2442250963Sachim
2443250963Sachim/*
2444250963Sachim * Debugging and Diagnostics
2445250963Sachim */
2446250963Sachim
2447250963Sachim/*
2448250963Sachim * Print some information about the controller.
2449250963Sachim */
2450250963Sachimstatic void
2451250963Sachimaac_describe_controller(struct aac_softc *sc)
2452250963Sachim{
2453250963Sachim	struct aac_fib *fib;
2454250963Sachim	struct aac_adapter_info	*info;
2455250963Sachim	char *adapter_type = "Adaptec RAID controller";
2456250963Sachim
2457250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2458250963Sachim
2459250963Sachim	mtx_lock(&sc->aac_io_lock);
2460250963Sachim	aac_alloc_sync_fib(sc, &fib);
2461250963Sachim
2462250963Sachim	if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) {
2463250963Sachim		fib->data[0] = 0;
2464250963Sachim		if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1))
2465250963Sachim			device_printf(sc->aac_dev, "RequestSupplementAdapterInfo failed\n");
2466250963Sachim		else {
2467250963Sachim			struct aac_supplement_adapter_info *supp_info;
2468250963Sachim
2469250963Sachim			supp_info = ((struct aac_supplement_adapter_info *)&fib->data[0]);
2470250963Sachim			adapter_type = (char *)supp_info->AdapterTypeText;
2471250963Sachim			sc->aac_feature_bits = supp_info->FeatureBits;
2472250963Sachim			sc->aac_support_opt2 = supp_info->SupportedOptions2;
2473250963Sachim		}
2474250963Sachim	}
2475250963Sachim	device_printf(sc->aac_dev, "%s, aacraid driver %d.%d.%d-%d\n",
2476250963Sachim		adapter_type,
2477250963Sachim		AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION,
2478250963Sachim		AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD);
2479250963Sachim
2480250963Sachim	fib->data[0] = 0;
2481250963Sachim	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
2482250963Sachim		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2483250963Sachim		aac_release_sync_fib(sc);
2484250963Sachim		mtx_unlock(&sc->aac_io_lock);
2485250963Sachim		return;
2486250963Sachim	}
2487250963Sachim
2488250963Sachim	/* save the kernel revision structure for later use */
2489250963Sachim	info = (struct aac_adapter_info *)&fib->data[0];
2490250963Sachim	sc->aac_revision = info->KernelRevision;
2491250963Sachim
2492250963Sachim	if (bootverbose) {
2493250963Sachim		device_printf(sc->aac_dev, "%s %dMHz, %dMB memory "
2494250963Sachim		    "(%dMB cache, %dMB execution), %s\n",
2495250963Sachim		    aac_describe_code(aac_cpu_variant, info->CpuVariant),
2496250963Sachim		    info->ClockSpeed, info->TotalMem / (1024 * 1024),
2497250963Sachim		    info->BufferMem / (1024 * 1024),
2498250963Sachim		    info->ExecutionMem / (1024 * 1024),
2499250963Sachim		    aac_describe_code(aac_battery_platform,
2500250963Sachim		    info->batteryPlatform));
2501250963Sachim
2502250963Sachim		device_printf(sc->aac_dev,
2503250963Sachim		    "Kernel %d.%d-%d, Build %d, S/N %6X\n",
2504250963Sachim		    info->KernelRevision.external.comp.major,
2505250963Sachim		    info->KernelRevision.external.comp.minor,
2506250963Sachim		    info->KernelRevision.external.comp.dash,
2507250963Sachim		    info->KernelRevision.buildNumber,
2508250963Sachim		    (u_int32_t)(info->SerialNumber & 0xffffff));
2509250963Sachim
2510250963Sachim		device_printf(sc->aac_dev, "Supported Options=%b\n",
2511250963Sachim			      sc->supported_options,
2512250963Sachim			      "\20"
2513250963Sachim			      "\1SNAPSHOT"
2514250963Sachim			      "\2CLUSTERS"
2515250963Sachim			      "\3WCACHE"
2516250963Sachim			      "\4DATA64"
2517250963Sachim			      "\5HOSTTIME"
2518250963Sachim			      "\6RAID50"
2519250963Sachim			      "\7WINDOW4GB"
2520250963Sachim			      "\10SCSIUPGD"
2521250963Sachim			      "\11SOFTERR"
2522250963Sachim			      "\12NORECOND"
2523250963Sachim			      "\13SGMAP64"
2524250963Sachim			      "\14ALARM"
2525250963Sachim			      "\15NONDASD"
2526250963Sachim			      "\16SCSIMGT"
2527250963Sachim			      "\17RAIDSCSI"
2528250963Sachim			      "\21ADPTINFO"
2529250963Sachim			      "\22NEWCOMM"
2530250963Sachim			      "\23ARRAY64BIT"
2531250963Sachim			      "\24HEATSENSOR");
2532250963Sachim	}
2533250963Sachim
2534250963Sachim	aac_release_sync_fib(sc);
2535250963Sachim	mtx_unlock(&sc->aac_io_lock);
2536250963Sachim}
2537250963Sachim
2538250963Sachim/*
2539250963Sachim * Look up a text description of a numeric error code and return a pointer to
2540250963Sachim * same.
2541250963Sachim */
2542250963Sachimstatic char *
2543250963Sachimaac_describe_code(struct aac_code_lookup *table, u_int32_t code)
2544250963Sachim{
2545250963Sachim	int i;
2546250963Sachim
2547250963Sachim	for (i = 0; table[i].string != NULL; i++)
2548250963Sachim		if (table[i].code == code)
2549250963Sachim			return(table[i].string);
2550250963Sachim	return(table[i + 1].string);
2551250963Sachim}
2552250963Sachim
2553250963Sachim/*
2554250963Sachim * Management Interface
2555250963Sachim */
2556250963Sachim
2557250963Sachimstatic int
2558250963Sachimaac_open(struct cdev *dev, int flags, int fmt, struct thread *td)
2559250963Sachim{
2560250963Sachim	struct aac_softc *sc;
2561250963Sachim
2562250963Sachim	sc = dev->si_drv1;
2563250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2564250963Sachim#if __FreeBSD_version >= 702000
2565250963Sachim	device_busy(sc->aac_dev);
2566250963Sachim	devfs_set_cdevpriv(sc, aac_cdevpriv_dtor);
2567250963Sachim#endif
2568250963Sachim	return 0;
2569250963Sachim}
2570250963Sachim
2571250963Sachimstatic int
2572250963Sachimaac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
2573250963Sachim{
2574250963Sachim	union aac_statrequest *as;
2575250963Sachim	struct aac_softc *sc;
2576250963Sachim	int error = 0;
2577250963Sachim
2578250963Sachim	as = (union aac_statrequest *)arg;
2579250963Sachim	sc = dev->si_drv1;
2580250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2581250963Sachim
2582250963Sachim	switch (cmd) {
2583250963Sachim	case AACIO_STATS:
2584250963Sachim		switch (as->as_item) {
2585250963Sachim		case AACQ_FREE:
2586250963Sachim		case AACQ_READY:
2587250963Sachim		case AACQ_BUSY:
2588250963Sachim			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2589250963Sachim			      sizeof(struct aac_qstat));
2590250963Sachim			break;
2591250963Sachim		default:
2592250963Sachim			error = ENOENT;
2593250963Sachim			break;
2594250963Sachim		}
2595250963Sachim	break;
2596250963Sachim
2597250963Sachim	case FSACTL_SENDFIB:
2598250963Sachim	case FSACTL_SEND_LARGE_FIB:
2599250963Sachim		arg = *(caddr_t*)arg;
2600250963Sachim	case FSACTL_LNX_SENDFIB:
2601250963Sachim	case FSACTL_LNX_SEND_LARGE_FIB:
2602250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB");
2603250963Sachim		error = aac_ioctl_sendfib(sc, arg);
2604250963Sachim		break;
2605250963Sachim	case FSACTL_SEND_RAW_SRB:
2606250963Sachim		arg = *(caddr_t*)arg;
2607250963Sachim	case FSACTL_LNX_SEND_RAW_SRB:
2608250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB");
2609250963Sachim		error = aac_ioctl_send_raw_srb(sc, arg);
2610250963Sachim		break;
2611250963Sachim	case FSACTL_AIF_THREAD:
2612250963Sachim	case FSACTL_LNX_AIF_THREAD:
2613250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD");
2614250963Sachim		error = EINVAL;
2615250963Sachim		break;
2616250963Sachim	case FSACTL_OPEN_GET_ADAPTER_FIB:
2617250963Sachim		arg = *(caddr_t*)arg;
2618250963Sachim	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
2619250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB");
2620250963Sachim		error = aac_open_aif(sc, arg);
2621250963Sachim		break;
2622250963Sachim	case FSACTL_GET_NEXT_ADAPTER_FIB:
2623250963Sachim		arg = *(caddr_t*)arg;
2624250963Sachim	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
2625250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB");
2626250963Sachim		error = aac_getnext_aif(sc, arg);
2627250963Sachim		break;
2628250963Sachim	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2629250963Sachim		arg = *(caddr_t*)arg;
2630250963Sachim	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
2631250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2632250963Sachim		error = aac_close_aif(sc, arg);
2633250963Sachim		break;
2634250963Sachim	case FSACTL_MINIPORT_REV_CHECK:
2635250963Sachim		arg = *(caddr_t*)arg;
2636250963Sachim	case FSACTL_LNX_MINIPORT_REV_CHECK:
2637250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK");
2638250963Sachim		error = aac_rev_check(sc, arg);
2639250963Sachim		break;
2640250963Sachim	case FSACTL_QUERY_DISK:
2641250963Sachim		arg = *(caddr_t*)arg;
2642250963Sachim	case FSACTL_LNX_QUERY_DISK:
2643250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK");
2644250963Sachim		error = aac_query_disk(sc, arg);
2645250963Sachim		break;
2646250963Sachim	case FSACTL_DELETE_DISK:
2647250963Sachim	case FSACTL_LNX_DELETE_DISK:
2648250963Sachim		/*
2649250963Sachim		 * We don't trust the underland to tell us when to delete a
2650250963Sachim		 * container, rather we rely on an AIF coming from the
2651250963Sachim		 * controller
2652250963Sachim		 */
2653250963Sachim		error = 0;
2654250963Sachim		break;
2655250963Sachim	case FSACTL_GET_PCI_INFO:
2656250963Sachim		arg = *(caddr_t*)arg;
2657250963Sachim	case FSACTL_LNX_GET_PCI_INFO:
2658250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO");
2659250963Sachim		error = aac_get_pci_info(sc, arg);
2660250963Sachim		break;
2661250963Sachim	case FSACTL_GET_FEATURES:
2662250963Sachim		arg = *(caddr_t*)arg;
2663250963Sachim	case FSACTL_LNX_GET_FEATURES:
2664250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_FEATURES");
2665250963Sachim		error = aac_supported_features(sc, arg);
2666250963Sachim		break;
2667250963Sachim	default:
2668250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd);
2669250963Sachim		error = EINVAL;
2670250963Sachim		break;
2671250963Sachim	}
2672250963Sachim	return(error);
2673250963Sachim}
2674250963Sachim
2675250963Sachimstatic int
2676250963Sachimaac_poll(struct cdev *dev, int poll_events, struct thread *td)
2677250963Sachim{
2678250963Sachim	struct aac_softc *sc;
2679250963Sachim	struct aac_fib_context *ctx;
2680250963Sachim	int revents;
2681250963Sachim
2682250963Sachim	sc = dev->si_drv1;
2683250963Sachim	revents = 0;
2684250963Sachim
2685250963Sachim	mtx_lock(&sc->aac_io_lock);
2686250963Sachim	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2687250963Sachim		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2688250963Sachim			if (ctx->ctx_idx != sc->aifq_idx || ctx->ctx_wrap) {
2689250963Sachim				revents |= poll_events & (POLLIN | POLLRDNORM);
2690250963Sachim				break;
2691250963Sachim			}
2692250963Sachim		}
2693250963Sachim	}
2694250963Sachim	mtx_unlock(&sc->aac_io_lock);
2695250963Sachim
2696250963Sachim	if (revents == 0) {
2697250963Sachim		if (poll_events & (POLLIN | POLLRDNORM))
2698250963Sachim			selrecord(td, &sc->rcv_select);
2699250963Sachim	}
2700250963Sachim
2701250963Sachim	return (revents);
2702250963Sachim}
2703250963Sachim
2704250963Sachimstatic void
2705250963Sachimaac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg)
2706250963Sachim{
2707250963Sachim
2708250963Sachim	switch (event->ev_type) {
2709250963Sachim	case AAC_EVENT_CMFREE:
2710250963Sachim		mtx_assert(&sc->aac_io_lock, MA_OWNED);
2711250963Sachim		if (aacraid_alloc_command(sc, (struct aac_command **)arg)) {
2712250963Sachim			aacraid_add_event(sc, event);
2713250963Sachim			return;
2714250963Sachim		}
2715250963Sachim		free(event, M_AACRAIDBUF);
2716250963Sachim		wakeup(arg);
2717250963Sachim		break;
2718250963Sachim	default:
2719250963Sachim		break;
2720250963Sachim	}
2721250963Sachim}
2722250963Sachim
2723250963Sachim/*
2724250963Sachim * Send a FIB supplied from userspace
2725250963Sachim */
2726250963Sachimstatic int
2727250963Sachimaac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
2728250963Sachim{
2729250963Sachim	struct aac_command *cm;
2730250963Sachim	int size, error;
2731250963Sachim
2732250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2733250963Sachim
2734250963Sachim	cm = NULL;
2735250963Sachim
2736250963Sachim	/*
2737250963Sachim	 * Get a command
2738250963Sachim	 */
2739250963Sachim	mtx_lock(&sc->aac_io_lock);
2740250963Sachim	if (aacraid_alloc_command(sc, &cm)) {
2741250963Sachim		struct aac_event *event;
2742250963Sachim
2743250963Sachim		event = malloc(sizeof(struct aac_event), M_AACRAIDBUF,
2744250963Sachim		    M_NOWAIT | M_ZERO);
2745250963Sachim		if (event == NULL) {
2746250963Sachim			error = EBUSY;
2747250963Sachim			mtx_unlock(&sc->aac_io_lock);
2748250963Sachim			goto out;
2749250963Sachim		}
2750250963Sachim		event->ev_type = AAC_EVENT_CMFREE;
2751250963Sachim		event->ev_callback = aac_ioctl_event;
2752250963Sachim		event->ev_arg = &cm;
2753250963Sachim		aacraid_add_event(sc, event);
2754250963Sachim		msleep(cm, &sc->aac_io_lock, 0, "aacraid_ctlsfib", 0);
2755250963Sachim	}
2756250963Sachim	mtx_unlock(&sc->aac_io_lock);
2757250963Sachim
2758250963Sachim	/*
2759250963Sachim	 * Fetch the FIB header, then re-copy to get data as well.
2760250963Sachim	 */
2761250963Sachim	if ((error = copyin(ufib, cm->cm_fib,
2762250963Sachim			    sizeof(struct aac_fib_header))) != 0)
2763250963Sachim		goto out;
2764250963Sachim	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
2765250963Sachim	if (size > sc->aac_max_fib_size) {
2766250963Sachim		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
2767250963Sachim			      size, sc->aac_max_fib_size);
2768250963Sachim		size = sc->aac_max_fib_size;
2769250963Sachim	}
2770250963Sachim	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
2771250963Sachim		goto out;
2772250963Sachim	cm->cm_fib->Header.Size = size;
2773250963Sachim	cm->cm_timestamp = time_uptime;
2774250963Sachim	cm->cm_datalen = 0;
2775250963Sachim
2776250963Sachim	/*
2777250963Sachim	 * Pass the FIB to the controller, wait for it to complete.
2778250963Sachim	 */
2779250963Sachim	mtx_lock(&sc->aac_io_lock);
2780250963Sachim	error = aacraid_wait_command(cm);
2781250963Sachim	mtx_unlock(&sc->aac_io_lock);
2782250963Sachim	if (error != 0) {
2783250963Sachim		device_printf(sc->aac_dev,
2784250963Sachim			      "aacraid_wait_command return %d\n", error);
2785250963Sachim		goto out;
2786250963Sachim	}
2787250963Sachim
2788250963Sachim	/*
2789250963Sachim	 * Copy the FIB and data back out to the caller.
2790250963Sachim	 */
2791250963Sachim	size = cm->cm_fib->Header.Size;
2792250963Sachim	if (size > sc->aac_max_fib_size) {
2793250963Sachim		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
2794250963Sachim			      size, sc->aac_max_fib_size);
2795250963Sachim		size = sc->aac_max_fib_size;
2796250963Sachim	}
2797250963Sachim	error = copyout(cm->cm_fib, ufib, size);
2798250963Sachim
2799250963Sachimout:
2800250963Sachim	if (cm != NULL) {
2801250963Sachim		mtx_lock(&sc->aac_io_lock);
2802250963Sachim		aacraid_release_command(cm);
2803250963Sachim		mtx_unlock(&sc->aac_io_lock);
2804250963Sachim	}
2805250963Sachim	return(error);
2806250963Sachim}
2807250963Sachim
2808250963Sachim/*
2809250963Sachim * Send a passthrough FIB supplied from userspace
2810250963Sachim */
2811250963Sachimstatic int
2812250963Sachimaac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
2813250963Sachim{
2814250963Sachim	struct aac_command *cm;
2815250963Sachim	struct aac_fib *fib;
2816250963Sachim	struct aac_srb *srbcmd;
2817250963Sachim	struct aac_srb *user_srb = (struct aac_srb *)arg;
2818250963Sachim	void *user_reply;
2819250963Sachim	int error, transfer_data = 0;
2820250963Sachim	bus_dmamap_t orig_map = 0;
2821250963Sachim	u_int32_t fibsize = 0;
2822250963Sachim	u_int64_t srb_sg_address;
2823250963Sachim	u_int32_t srb_sg_bytecount;
2824250963Sachim
2825250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2826250963Sachim
2827250963Sachim	cm = NULL;
2828250963Sachim
2829250963Sachim	mtx_lock(&sc->aac_io_lock);
2830250963Sachim	if (aacraid_alloc_command(sc, &cm)) {
2831250963Sachim		struct aac_event *event;
2832250963Sachim
2833250963Sachim		event = malloc(sizeof(struct aac_event), M_AACRAIDBUF,
2834250963Sachim		    M_NOWAIT | M_ZERO);
2835250963Sachim		if (event == NULL) {
2836250963Sachim			error = EBUSY;
2837250963Sachim			mtx_unlock(&sc->aac_io_lock);
2838250963Sachim			goto out;
2839250963Sachim		}
2840250963Sachim		event->ev_type = AAC_EVENT_CMFREE;
2841250963Sachim		event->ev_callback = aac_ioctl_event;
2842250963Sachim		event->ev_arg = &cm;
2843250963Sachim		aacraid_add_event(sc, event);
2844250963Sachim		msleep(cm, &sc->aac_io_lock, 0, "aacraid_ctlsraw", 0);
2845250963Sachim	}
2846250963Sachim	mtx_unlock(&sc->aac_io_lock);
2847250963Sachim
2848250963Sachim	cm->cm_data = NULL;
2849250963Sachim	/* save original dma map */
2850250963Sachim	orig_map = cm->cm_datamap;
2851250963Sachim
2852250963Sachim	fib = cm->cm_fib;
2853250963Sachim	srbcmd = (struct aac_srb *)fib->data;
2854250963Sachim	if ((error = copyin((void *)&user_srb->data_len, &fibsize,
2855250963Sachim		sizeof (u_int32_t)) != 0))
2856250963Sachim		goto out;
2857250963Sachim	if (fibsize > (sc->aac_max_fib_size-sizeof(struct aac_fib_header))) {
2858250963Sachim		error = EINVAL;
2859250963Sachim		goto out;
2860250963Sachim	}
2861250963Sachim	if ((error = copyin((void *)user_srb, srbcmd, fibsize) != 0))
2862250963Sachim		goto out;
2863250963Sachim
2864250963Sachim	srbcmd->function = 0;		/* SRBF_ExecuteScsi */
2865250963Sachim	srbcmd->retry_limit = 0;	/* obsolete */
2866250963Sachim
2867250963Sachim	/* only one sg element from userspace supported */
2868250963Sachim	if (srbcmd->sg_map.SgCount > 1) {
2869250963Sachim		error = EINVAL;
2870250963Sachim		goto out;
2871250963Sachim	}
2872250963Sachim	/* check fibsize */
2873250963Sachim	if (fibsize == (sizeof(struct aac_srb) +
2874250963Sachim		srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry))) {
2875250963Sachim		struct aac_sg_entry *sgp = srbcmd->sg_map.SgEntry;
2876250963Sachim		srb_sg_bytecount = sgp->SgByteCount;
2877250963Sachim		srb_sg_address = (u_int64_t)sgp->SgAddress;
2878250963Sachim	} else if (fibsize == (sizeof(struct aac_srb) +
2879250963Sachim		srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry64))) {
2880252778Sachim#ifdef __LP64__
2881250963Sachim		struct aac_sg_entry64 *sgp =
2882250963Sachim			(struct aac_sg_entry64 *)srbcmd->sg_map.SgEntry;
2883250963Sachim		srb_sg_bytecount = sgp->SgByteCount;
2884250963Sachim		srb_sg_address = sgp->SgAddress;
2885250963Sachim		if (srb_sg_address > 0xffffffffull &&
2886250963Sachim			!(sc->flags & AAC_FLAGS_SG_64BIT))
2887250963Sachim#endif
2888250963Sachim		{
2889250963Sachim			error = EINVAL;
2890250963Sachim			goto out;
2891250963Sachim		}
2892250963Sachim	} else {
2893250963Sachim		error = EINVAL;
2894250963Sachim		goto out;
2895250963Sachim	}
2896250963Sachim	user_reply = (char *)arg + fibsize;
2897250963Sachim	srbcmd->data_len = srb_sg_bytecount;
2898250963Sachim	if (srbcmd->sg_map.SgCount == 1)
2899250963Sachim		transfer_data = 1;
2900250963Sachim
2901250963Sachim	if (transfer_data) {
2902250963Sachim		/*
2903250963Sachim		 * Create DMA tag for the passthr. data buffer and allocate it.
2904250963Sachim		 */
2905250963Sachim		if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
2906250963Sachim			1, 0,			/* algnmnt, boundary */
2907250963Sachim			(sc->flags & AAC_FLAGS_SG_64BIT) ?
2908250963Sachim			BUS_SPACE_MAXADDR_32BIT :
2909250963Sachim			0x7fffffff,		/* lowaddr */
2910250963Sachim			BUS_SPACE_MAXADDR, 	/* highaddr */
2911250963Sachim			NULL, NULL, 		/* filter, filterarg */
2912250963Sachim			srb_sg_bytecount, 	/* size */
2913250963Sachim			sc->aac_sg_tablesize,	/* nsegments */
2914250963Sachim			srb_sg_bytecount, 	/* maxsegsize */
2915250963Sachim			0,			/* flags */
2916250963Sachim			NULL, NULL,		/* No locking needed */
2917250963Sachim			&cm->cm_passthr_dmat)) {
2918250963Sachim			error = ENOMEM;
2919250963Sachim			goto out;
2920250963Sachim		}
2921250963Sachim		if (bus_dmamem_alloc(cm->cm_passthr_dmat, (void **)&cm->cm_data,
2922250963Sachim			BUS_DMA_NOWAIT, &cm->cm_datamap)) {
2923250963Sachim			error = ENOMEM;
2924250963Sachim			goto out;
2925250963Sachim		}
2926250963Sachim		/* fill some cm variables */
2927250963Sachim		cm->cm_datalen = srb_sg_bytecount;
2928250963Sachim		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)
2929250963Sachim			cm->cm_flags |= AAC_CMD_DATAIN;
2930250963Sachim		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT)
2931250963Sachim			cm->cm_flags |= AAC_CMD_DATAOUT;
2932250963Sachim
2933250963Sachim		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT) {
2934252778Sachim			if ((error = copyin((void *)(uintptr_t)srb_sg_address,
2935250963Sachim				cm->cm_data, cm->cm_datalen)) != 0)
2936250963Sachim				goto out;
2937250963Sachim			/* sync required for bus_dmamem_alloc() alloc. mem.? */
2938250963Sachim			bus_dmamap_sync(cm->cm_passthr_dmat, cm->cm_datamap,
2939250963Sachim				BUS_DMASYNC_PREWRITE);
2940250963Sachim		}
2941250963Sachim	}
2942250963Sachim
2943250963Sachim	/* build the FIB */
2944250963Sachim	fib->Header.Size = sizeof(struct aac_fib_header) +
2945250963Sachim		sizeof(struct aac_srb);
2946250963Sachim	fib->Header.XferState =
2947250963Sachim		AAC_FIBSTATE_HOSTOWNED   |
2948250963Sachim		AAC_FIBSTATE_INITIALISED |
2949250963Sachim		AAC_FIBSTATE_EMPTY	 |
2950250963Sachim		AAC_FIBSTATE_FROMHOST	 |
2951250963Sachim		AAC_FIBSTATE_REXPECTED   |
2952250963Sachim		AAC_FIBSTATE_NORM	 |
2953250963Sachim		AAC_FIBSTATE_ASYNC;
2954250963Sachim
2955250963Sachim	fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) ?
2956250963Sachim		ScsiPortCommandU64 : ScsiPortCommand;
2957250963Sachim	cm->cm_sgtable = (struct aac_sg_table *)&srbcmd->sg_map;
2958250963Sachim
2959250963Sachim	/* send command */
2960250963Sachim	if (transfer_data) {
2961250963Sachim		bus_dmamap_load(cm->cm_passthr_dmat,
2962250963Sachim			cm->cm_datamap, cm->cm_data,
2963250963Sachim			cm->cm_datalen,
2964250963Sachim			aacraid_map_command_sg, cm, 0);
2965250963Sachim	} else {
2966250963Sachim		aacraid_map_command_sg(cm, NULL, 0, 0);
2967250963Sachim	}
2968250963Sachim
2969250963Sachim	/* wait for completion */
2970250963Sachim	mtx_lock(&sc->aac_io_lock);
2971250963Sachim	while (!(cm->cm_flags & AAC_CMD_COMPLETED))
2972250963Sachim		msleep(cm, &sc->aac_io_lock, 0, "aacraid_ctlsrw2", 0);
2973250963Sachim	mtx_unlock(&sc->aac_io_lock);
2974250963Sachim
2975250963Sachim	/* copy data */
2976250963Sachim	if (transfer_data && (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)) {
2977252778Sachim		if ((error = copyout(cm->cm_data,
2978252778Sachim			(void *)(uintptr_t)srb_sg_address,
2979250963Sachim			cm->cm_datalen)) != 0)
2980250963Sachim			goto out;
2981250963Sachim		/* sync required for bus_dmamem_alloc() allocated mem.? */
2982250963Sachim		bus_dmamap_sync(cm->cm_passthr_dmat, cm->cm_datamap,
2983250963Sachim				BUS_DMASYNC_POSTREAD);
2984250963Sachim	}
2985250963Sachim
2986250963Sachim	/* status */
2987250963Sachim	error = copyout(fib->data, user_reply, sizeof(struct aac_srb_response));
2988250963Sachim
2989250963Sachimout:
2990250963Sachim	if (cm && cm->cm_data) {
2991250963Sachim		if (transfer_data)
2992250963Sachim			bus_dmamap_unload(cm->cm_passthr_dmat, cm->cm_datamap);
2993250963Sachim		bus_dmamem_free(cm->cm_passthr_dmat, cm->cm_data, cm->cm_datamap);
2994250963Sachim		cm->cm_datamap = orig_map;
2995250963Sachim	}
2996250963Sachim	if (cm && cm->cm_passthr_dmat)
2997250963Sachim		bus_dma_tag_destroy(cm->cm_passthr_dmat);
2998250963Sachim	if (cm) {
2999250963Sachim		mtx_lock(&sc->aac_io_lock);
3000250963Sachim		aacraid_release_command(cm);
3001250963Sachim		mtx_unlock(&sc->aac_io_lock);
3002250963Sachim	}
3003250963Sachim	return(error);
3004250963Sachim}
3005250963Sachim
3006250963Sachim/*
3007250963Sachim * Request an AIF from the controller (new comm. type1)
3008250963Sachim */
3009250963Sachimstatic void
3010250963Sachimaac_request_aif(struct aac_softc *sc)
3011250963Sachim{
3012250963Sachim	struct aac_command *cm;
3013250963Sachim	struct aac_fib *fib;
3014250963Sachim
3015250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3016250963Sachim
3017250963Sachim	if (aacraid_alloc_command(sc, &cm)) {
3018250963Sachim		sc->aif_pending = 1;
3019250963Sachim		return;
3020250963Sachim	}
3021250963Sachim	sc->aif_pending = 0;
3022250963Sachim
3023250963Sachim	/* build the FIB */
3024250963Sachim	fib = cm->cm_fib;
3025250963Sachim	fib->Header.Size = sizeof(struct aac_fib);
3026250963Sachim	fib->Header.XferState =
3027250963Sachim        AAC_FIBSTATE_HOSTOWNED   |
3028250963Sachim        AAC_FIBSTATE_INITIALISED |
3029250963Sachim        AAC_FIBSTATE_EMPTY	 |
3030250963Sachim        AAC_FIBSTATE_FROMHOST	 |
3031250963Sachim        AAC_FIBSTATE_REXPECTED   |
3032250963Sachim        AAC_FIBSTATE_NORM	 |
3033250963Sachim        AAC_FIBSTATE_ASYNC;
3034250963Sachim	/* set AIF marker */
3035250963Sachim	fib->Header.Handle = 0x00800000;
3036250963Sachim	fib->Header.Command = AifRequest;
3037250963Sachim	((struct aac_aif_command *)fib->data)->command = AifReqEvent;
3038250963Sachim
3039250963Sachim	aacraid_map_command_sg(cm, NULL, 0, 0);
3040250963Sachim}
3041250963Sachim
3042250963Sachim
3043250963Sachim#if __FreeBSD_version >= 702000
3044250963Sachim/*
3045250963Sachim * cdevpriv interface private destructor.
3046250963Sachim */
3047250963Sachimstatic void
3048250963Sachimaac_cdevpriv_dtor(void *arg)
3049250963Sachim{
3050250963Sachim	struct aac_softc *sc;
3051250963Sachim
3052250963Sachim	sc = arg;
3053250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3054250963Sachim	mtx_lock(&Giant);
3055250963Sachim	device_unbusy(sc->aac_dev);
3056250963Sachim	mtx_unlock(&Giant);
3057250963Sachim}
3058250963Sachim#else
3059250963Sachimstatic int
3060250963Sachimaac_close(struct cdev *dev, int flags, int fmt, struct thread *td)
3061250963Sachim{
3062250963Sachim	struct aac_softc *sc;
3063250963Sachim
3064250963Sachim	sc = dev->si_drv1;
3065250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3066250963Sachim	return 0;
3067250963Sachim}
3068250963Sachim#endif
3069250963Sachim
3070250963Sachim/*
3071250963Sachim * Handle an AIF sent to us by the controller; queue it for later reference.
3072250963Sachim * If the queue fills up, then drop the older entries.
3073250963Sachim */
3074250963Sachimstatic void
3075250963Sachimaac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
3076250963Sachim{
3077250963Sachim	struct aac_aif_command *aif;
3078250963Sachim	struct aac_container *co, *co_next;
3079250963Sachim	struct aac_fib_context *ctx;
3080250963Sachim	struct aac_fib *sync_fib;
3081250963Sachim	struct aac_mntinforesp mir;
3082250963Sachim	int next, current, found;
3083250963Sachim	int count = 0, changed = 0, i = 0;
3084250963Sachim	u_int32_t channel, uid;
3085250963Sachim
3086250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3087250963Sachim
3088250963Sachim	aif = (struct aac_aif_command*)&fib->data[0];
3089250963Sachim	aacraid_print_aif(sc, aif);
3090250963Sachim
3091250963Sachim	/* Is it an event that we should care about? */
3092250963Sachim	switch (aif->command) {
3093250963Sachim	case AifCmdEventNotify:
3094250963Sachim		switch (aif->data.EN.type) {
3095250963Sachim		case AifEnAddContainer:
3096250963Sachim		case AifEnDeleteContainer:
3097250963Sachim			/*
3098250963Sachim			 * A container was added or deleted, but the message
3099250963Sachim			 * doesn't tell us anything else!  Re-enumerate the
3100250963Sachim			 * containers and sort things out.
3101250963Sachim			 */
3102250963Sachim			aac_alloc_sync_fib(sc, &sync_fib);
3103250963Sachim			do {
3104250963Sachim				/*
3105250963Sachim				 * Ask the controller for its containers one at
3106250963Sachim				 * a time.
3107250963Sachim				 * XXX What if the controller's list changes
3108250963Sachim				 * midway through this enumaration?
3109250963Sachim				 * XXX This should be done async.
3110250963Sachim				 */
3111250963Sachim				if (aac_get_container_info(sc, sync_fib, i,
3112250963Sachim					&mir, &uid) != 0)
3113250963Sachim					continue;
3114250963Sachim				if (i == 0)
3115250963Sachim					count = mir.MntRespCount;
3116250963Sachim				/*
3117250963Sachim				 * Check the container against our list.
3118250963Sachim				 * co->co_found was already set to 0 in a
3119250963Sachim				 * previous run.
3120250963Sachim				 */
3121250963Sachim				if ((mir.Status == ST_OK) &&
3122250963Sachim				    (mir.MntTable[0].VolType != CT_NONE)) {
3123250963Sachim					found = 0;
3124250963Sachim					TAILQ_FOREACH(co,
3125250963Sachim						      &sc->aac_container_tqh,
3126250963Sachim						      co_link) {
3127250963Sachim						if (co->co_mntobj.ObjectId ==
3128250963Sachim						    mir.MntTable[0].ObjectId) {
3129250963Sachim							co->co_found = 1;
3130250963Sachim							found = 1;
3131250963Sachim							break;
3132250963Sachim						}
3133250963Sachim					}
3134250963Sachim					/*
3135250963Sachim					 * If the container matched, continue
3136250963Sachim					 * in the list.
3137250963Sachim					 */
3138250963Sachim					if (found) {
3139250963Sachim						i++;
3140250963Sachim						continue;
3141250963Sachim					}
3142250963Sachim
3143250963Sachim					/*
3144250963Sachim					 * This is a new container.  Do all the
3145250963Sachim					 * appropriate things to set it up.
3146250963Sachim					 */
3147250963Sachim					aac_add_container(sc, &mir, 1, uid);
3148250963Sachim					changed = 1;
3149250963Sachim				}
3150250963Sachim				i++;
3151250963Sachim			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
3152250963Sachim			aac_release_sync_fib(sc);
3153250963Sachim
3154250963Sachim			/*
3155250963Sachim			 * Go through our list of containers and see which ones
3156250963Sachim			 * were not marked 'found'.  Since the controller didn't
3157250963Sachim			 * list them they must have been deleted.  Do the
3158250963Sachim			 * appropriate steps to destroy the device.  Also reset
3159250963Sachim			 * the co->co_found field.
3160250963Sachim			 */
3161250963Sachim			co = TAILQ_FIRST(&sc->aac_container_tqh);
3162250963Sachim			while (co != NULL) {
3163250963Sachim				if (co->co_found == 0) {
3164250963Sachim					co_next = TAILQ_NEXT(co, co_link);
3165250963Sachim					TAILQ_REMOVE(&sc->aac_container_tqh, co,
3166250963Sachim						     co_link);
3167250963Sachim					free(co, M_AACRAIDBUF);
3168250963Sachim					changed = 1;
3169250963Sachim					co = co_next;
3170250963Sachim				} else {
3171250963Sachim					co->co_found = 0;
3172250963Sachim					co = TAILQ_NEXT(co, co_link);
3173250963Sachim				}
3174250963Sachim			}
3175250963Sachim
3176250963Sachim			/* Attach the newly created containers */
3177250963Sachim			if (changed) {
3178250963Sachim				if (sc->cam_rescan_cb != NULL)
3179250963Sachim					sc->cam_rescan_cb(sc, 0,
3180250963Sachim				    	AAC_CAM_TARGET_WILDCARD);
3181250963Sachim			}
3182250963Sachim
3183250963Sachim			break;
3184250963Sachim
3185250963Sachim		case AifEnEnclosureManagement:
3186250963Sachim			switch (aif->data.EN.data.EEE.eventType) {
3187250963Sachim			case AIF_EM_DRIVE_INSERTION:
3188250963Sachim			case AIF_EM_DRIVE_REMOVAL:
3189250963Sachim				channel = aif->data.EN.data.EEE.unitID;
3190250963Sachim				if (sc->cam_rescan_cb != NULL)
3191250963Sachim					sc->cam_rescan_cb(sc,
3192250963Sachim					    ((channel>>24) & 0xF) + 1,
3193250963Sachim					    (channel & 0xFFFF));
3194250963Sachim				break;
3195250963Sachim			}
3196250963Sachim			break;
3197250963Sachim
3198250963Sachim		case AifEnAddJBOD:
3199250963Sachim		case AifEnDeleteJBOD:
3200250963Sachim		case AifRawDeviceRemove:
3201250963Sachim			channel = aif->data.EN.data.ECE.container;
3202250963Sachim			if (sc->cam_rescan_cb != NULL)
3203250963Sachim				sc->cam_rescan_cb(sc, ((channel>>24) & 0xF) + 1,
3204250963Sachim				    AAC_CAM_TARGET_WILDCARD);
3205250963Sachim			break;
3206250963Sachim
3207250963Sachim		default:
3208250963Sachim			break;
3209250963Sachim		}
3210250963Sachim
3211250963Sachim	default:
3212250963Sachim		break;
3213250963Sachim	}
3214250963Sachim
3215250963Sachim	/* Copy the AIF data to the AIF queue for ioctl retrieval */
3216250963Sachim	current = sc->aifq_idx;
3217250963Sachim	next = (current + 1) % AAC_AIFQ_LENGTH;
3218250963Sachim	if (next == 0)
3219250963Sachim		sc->aifq_filled = 1;
3220250963Sachim	bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib));
3221250963Sachim	/* modify AIF contexts */
3222250963Sachim	if (sc->aifq_filled) {
3223250963Sachim		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3224250963Sachim			if (next == ctx->ctx_idx)
3225250963Sachim				ctx->ctx_wrap = 1;
3226250963Sachim			else if (current == ctx->ctx_idx && ctx->ctx_wrap)
3227250963Sachim				ctx->ctx_idx = next;
3228250963Sachim		}
3229250963Sachim	}
3230250963Sachim	sc->aifq_idx = next;
3231250963Sachim	/* On the off chance that someone is sleeping for an aif... */
3232250963Sachim	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
3233250963Sachim		wakeup(sc->aac_aifq);
3234250963Sachim	/* Wakeup any poll()ers */
3235250963Sachim	selwakeuppri(&sc->rcv_select, PRIBIO);
3236250963Sachim
3237250963Sachim	return;
3238250963Sachim}
3239250963Sachim
3240250963Sachim/*
3241250963Sachim * Return the Revision of the driver to userspace and check to see if the
3242250963Sachim * userspace app is possibly compatible.  This is extremely bogus since
3243250963Sachim * our driver doesn't follow Adaptec's versioning system.  Cheat by just
3244250963Sachim * returning what the card reported.
3245250963Sachim */
3246250963Sachimstatic int
3247250963Sachimaac_rev_check(struct aac_softc *sc, caddr_t udata)
3248250963Sachim{
3249250963Sachim	struct aac_rev_check rev_check;
3250250963Sachim	struct aac_rev_check_resp rev_check_resp;
3251250963Sachim	int error = 0;
3252250963Sachim
3253250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3254250963Sachim
3255250963Sachim	/*
3256250963Sachim	 * Copyin the revision struct from userspace
3257250963Sachim	 */
3258250963Sachim	if ((error = copyin(udata, (caddr_t)&rev_check,
3259250963Sachim			sizeof(struct aac_rev_check))) != 0) {
3260250963Sachim		return error;
3261250963Sachim	}
3262250963Sachim
3263250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n",
3264250963Sachim	      rev_check.callingRevision.buildNumber);
3265250963Sachim
3266250963Sachim	/*
3267250963Sachim	 * Doctor up the response struct.
3268250963Sachim	 */
3269250963Sachim	rev_check_resp.possiblyCompatible = 1;
3270250963Sachim	rev_check_resp.adapterSWRevision.external.comp.major =
3271250963Sachim	    AAC_DRIVER_MAJOR_VERSION;
3272250963Sachim	rev_check_resp.adapterSWRevision.external.comp.minor =
3273250963Sachim	    AAC_DRIVER_MINOR_VERSION;
3274250963Sachim	rev_check_resp.adapterSWRevision.external.comp.type =
3275250963Sachim	    AAC_DRIVER_TYPE;
3276250963Sachim	rev_check_resp.adapterSWRevision.external.comp.dash =
3277250963Sachim	    AAC_DRIVER_BUGFIX_LEVEL;
3278250963Sachim	rev_check_resp.adapterSWRevision.buildNumber =
3279250963Sachim	    AAC_DRIVER_BUILD;
3280250963Sachim
3281250963Sachim	return(copyout((caddr_t)&rev_check_resp, udata,
3282250963Sachim			sizeof(struct aac_rev_check_resp)));
3283250963Sachim}
3284250963Sachim
3285250963Sachim/*
3286250963Sachim * Pass the fib context to the caller
3287250963Sachim */
3288250963Sachimstatic int
3289250963Sachimaac_open_aif(struct aac_softc *sc, caddr_t arg)
3290250963Sachim{
3291250963Sachim	struct aac_fib_context *fibctx, *ctx;
3292250963Sachim	int error = 0;
3293250963Sachim
3294250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3295250963Sachim
3296250963Sachim	fibctx = malloc(sizeof(struct aac_fib_context), M_AACRAIDBUF, M_NOWAIT|M_ZERO);
3297250963Sachim	if (fibctx == NULL)
3298250963Sachim		return (ENOMEM);
3299250963Sachim
3300250963Sachim	mtx_lock(&sc->aac_io_lock);
3301250963Sachim	/* all elements are already 0, add to queue */
3302250963Sachim	if (sc->fibctx == NULL)
3303250963Sachim		sc->fibctx = fibctx;
3304250963Sachim	else {
3305250963Sachim		for (ctx = sc->fibctx; ctx->next; ctx = ctx->next)
3306250963Sachim			;
3307250963Sachim		ctx->next = fibctx;
3308250963Sachim		fibctx->prev = ctx;
3309250963Sachim	}
3310250963Sachim
3311250963Sachim	/* evaluate unique value */
3312250963Sachim	fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff);
3313250963Sachim	ctx = sc->fibctx;
3314250963Sachim	while (ctx != fibctx) {
3315250963Sachim		if (ctx->unique == fibctx->unique) {
3316250963Sachim			fibctx->unique++;
3317250963Sachim			ctx = sc->fibctx;
3318250963Sachim		} else {
3319250963Sachim			ctx = ctx->next;
3320250963Sachim		}
3321250963Sachim	}
3322250963Sachim
3323250963Sachim	error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t));
3324250963Sachim	mtx_unlock(&sc->aac_io_lock);
3325250963Sachim	if (error)
3326250963Sachim		aac_close_aif(sc, (caddr_t)ctx);
3327250963Sachim	return error;
3328250963Sachim}
3329250963Sachim
3330250963Sachim/*
3331250963Sachim * Close the caller's fib context
3332250963Sachim */
3333250963Sachimstatic int
3334250963Sachimaac_close_aif(struct aac_softc *sc, caddr_t arg)
3335250963Sachim{
3336250963Sachim	struct aac_fib_context *ctx;
3337250963Sachim
3338250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3339250963Sachim
3340250963Sachim	mtx_lock(&sc->aac_io_lock);
3341250963Sachim	for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3342250963Sachim		if (ctx->unique == *(uint32_t *)&arg) {
3343250963Sachim			if (ctx == sc->fibctx)
3344250963Sachim				sc->fibctx = NULL;
3345250963Sachim			else {
3346250963Sachim				ctx->prev->next = ctx->next;
3347250963Sachim				if (ctx->next)
3348250963Sachim					ctx->next->prev = ctx->prev;
3349250963Sachim			}
3350250963Sachim			break;
3351250963Sachim		}
3352250963Sachim	}
3353250963Sachim	if (ctx)
3354250963Sachim		free(ctx, M_AACRAIDBUF);
3355250963Sachim
3356250963Sachim	mtx_unlock(&sc->aac_io_lock);
3357250963Sachim	return 0;
3358250963Sachim}
3359250963Sachim
3360250963Sachim/*
3361250963Sachim * Pass the caller the next AIF in their queue
3362250963Sachim */
3363250963Sachimstatic int
3364250963Sachimaac_getnext_aif(struct aac_softc *sc, caddr_t arg)
3365250963Sachim{
3366250963Sachim	struct get_adapter_fib_ioctl agf;
3367250963Sachim	struct aac_fib_context *ctx;
3368250963Sachim	int error;
3369250963Sachim
3370250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3371250963Sachim
3372250963Sachim	mtx_lock(&sc->aac_io_lock);
3373250963Sachim	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
3374250963Sachim		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3375250963Sachim			if (agf.AdapterFibContext == ctx->unique)
3376250963Sachim				break;
3377250963Sachim		}
3378250963Sachim		if (!ctx) {
3379250963Sachim			mtx_unlock(&sc->aac_io_lock);
3380250963Sachim			return (EFAULT);
3381250963Sachim		}
3382250963Sachim
3383250963Sachim		error = aac_return_aif(sc, ctx, agf.AifFib);
3384250963Sachim		if (error == EAGAIN && agf.Wait) {
3385250963Sachim			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
3386250963Sachim			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
3387250963Sachim			while (error == EAGAIN) {
3388250963Sachim				mtx_unlock(&sc->aac_io_lock);
3389250963Sachim				error = tsleep(sc->aac_aifq, PRIBIO |
3390250963Sachim					       PCATCH, "aacaif", 0);
3391250963Sachim				mtx_lock(&sc->aac_io_lock);
3392250963Sachim				if (error == 0)
3393250963Sachim					error = aac_return_aif(sc, ctx, agf.AifFib);
3394250963Sachim			}
3395250963Sachim			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
3396250963Sachim		}
3397250963Sachim	}
3398250963Sachim	mtx_unlock(&sc->aac_io_lock);
3399250963Sachim	return(error);
3400250963Sachim}
3401250963Sachim
3402250963Sachim/*
3403250963Sachim * Hand the next AIF off the top of the queue out to userspace.
3404250963Sachim */
3405250963Sachimstatic int
3406250963Sachimaac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr)
3407250963Sachim{
3408250963Sachim	int current, error;
3409250963Sachim
3410250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3411250963Sachim
3412250963Sachim	current = ctx->ctx_idx;
3413250963Sachim	if (current == sc->aifq_idx && !ctx->ctx_wrap) {
3414250963Sachim		/* empty */
3415250963Sachim		return (EAGAIN);
3416250963Sachim	}
3417250963Sachim	error =
3418250963Sachim		copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib));
3419250963Sachim	if (error)
3420250963Sachim		device_printf(sc->aac_dev,
3421250963Sachim		    "aac_return_aif: copyout returned %d\n", error);
3422250963Sachim	else {
3423250963Sachim		ctx->ctx_wrap = 0;
3424250963Sachim		ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3425250963Sachim	}
3426250963Sachim	return(error);
3427250963Sachim}
3428250963Sachim
3429250963Sachimstatic int
3430250963Sachimaac_get_pci_info(struct aac_softc *sc, caddr_t uptr)
3431250963Sachim{
3432250963Sachim	struct aac_pci_info {
3433250963Sachim		u_int32_t bus;
3434250963Sachim		u_int32_t slot;
3435250963Sachim	} pciinf;
3436250963Sachim	int error;
3437250963Sachim
3438250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3439250963Sachim
3440250963Sachim	pciinf.bus = pci_get_bus(sc->aac_dev);
3441250963Sachim	pciinf.slot = pci_get_slot(sc->aac_dev);
3442250963Sachim
3443250963Sachim	error = copyout((caddr_t)&pciinf, uptr,
3444250963Sachim			sizeof(struct aac_pci_info));
3445250963Sachim
3446250963Sachim	return (error);
3447250963Sachim}
3448250963Sachim
3449250963Sachimstatic int
3450250963Sachimaac_supported_features(struct aac_softc *sc, caddr_t uptr)
3451250963Sachim{
3452250963Sachim	struct aac_features f;
3453250963Sachim	int error;
3454250963Sachim
3455250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3456250963Sachim
3457250963Sachim	if ((error = copyin(uptr, &f, sizeof (f))) != 0)
3458250963Sachim		return (error);
3459250963Sachim
3460250963Sachim	/*
3461250963Sachim	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
3462250963Sachim	 * ALL zero in the featuresState, the driver will return the current
3463250963Sachim	 * state of all the supported features, the data field will not be
3464250963Sachim	 * valid.
3465250963Sachim	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
3466250963Sachim	 * a specific bit set in the featuresState, the driver will return the
3467250963Sachim	 * current state of this specific feature and whatever data that are
3468250963Sachim	 * associated with the feature in the data field or perform whatever
3469250963Sachim	 * action needed indicates in the data field.
3470250963Sachim	 */
3471250963Sachim	 if (f.feat.fValue == 0) {
3472250963Sachim		f.feat.fBits.largeLBA =
3473250963Sachim		    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
3474250963Sachim		f.feat.fBits.JBODSupport = 1;
3475250963Sachim		/* TODO: In the future, add other features state here as well */
3476250963Sachim	} else {
3477250963Sachim		if (f.feat.fBits.largeLBA)
3478250963Sachim			f.feat.fBits.largeLBA =
3479250963Sachim			    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
3480250963Sachim		/* TODO: Add other features state and data in the future */
3481250963Sachim	}
3482250963Sachim
3483250963Sachim	error = copyout(&f, uptr, sizeof (f));
3484250963Sachim	return (error);
3485250963Sachim}
3486250963Sachim
3487250963Sachim/*
3488250963Sachim * Give the userland some information about the container.  The AAC arch
3489250963Sachim * expects the driver to be a SCSI passthrough type driver, so it expects
3490250963Sachim * the containers to have b:t:l numbers.  Fake it.
3491250963Sachim */
3492250963Sachimstatic int
3493250963Sachimaac_query_disk(struct aac_softc *sc, caddr_t uptr)
3494250963Sachim{
3495250963Sachim	struct aac_query_disk query_disk;
3496250963Sachim	struct aac_container *co;
3497250963Sachim	int error, id;
3498250963Sachim
3499250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3500250963Sachim
3501250963Sachim	mtx_lock(&sc->aac_io_lock);
3502250963Sachim	error = copyin(uptr, (caddr_t)&query_disk,
3503250963Sachim		       sizeof(struct aac_query_disk));
3504250963Sachim	if (error) {
3505250963Sachim		mtx_unlock(&sc->aac_io_lock);
3506250963Sachim		return (error);
3507250963Sachim	}
3508250963Sachim
3509250963Sachim	id = query_disk.ContainerNumber;
3510250963Sachim	if (id == -1) {
3511250963Sachim		mtx_unlock(&sc->aac_io_lock);
3512250963Sachim		return (EINVAL);
3513250963Sachim	}
3514250963Sachim
3515250963Sachim	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
3516250963Sachim		if (co->co_mntobj.ObjectId == id)
3517250963Sachim			break;
3518250963Sachim		}
3519250963Sachim
3520250963Sachim	if (co == NULL) {
3521250963Sachim			query_disk.Valid = 0;
3522250963Sachim			query_disk.Locked = 0;
3523250963Sachim			query_disk.Deleted = 1;		/* XXX is this right? */
3524250963Sachim	} else {
3525250963Sachim		query_disk.Valid = 1;
3526250963Sachim		query_disk.Locked = 1;
3527250963Sachim		query_disk.Deleted = 0;
3528250963Sachim		query_disk.Bus = device_get_unit(sc->aac_dev);
3529250963Sachim		query_disk.Target = 0;
3530250963Sachim		query_disk.Lun = 0;
3531250963Sachim		query_disk.UnMapped = 0;
3532250963Sachim	}
3533250963Sachim
3534250963Sachim	error = copyout((caddr_t)&query_disk, uptr,
3535250963Sachim			sizeof(struct aac_query_disk));
3536250963Sachim
3537250963Sachim	mtx_unlock(&sc->aac_io_lock);
3538250963Sachim	return (error);
3539250963Sachim}
3540250963Sachim
3541250963Sachimstatic void
3542250963Sachimaac_container_bus(struct aac_softc *sc)
3543250963Sachim{
3544250963Sachim	struct aac_sim *sim;
3545250963Sachim	device_t child;
3546250963Sachim
3547250963Sachim	sim =(struct aac_sim *)malloc(sizeof(struct aac_sim),
3548250963Sachim		M_AACRAIDBUF, M_NOWAIT | M_ZERO);
3549250963Sachim	if (sim == NULL) {
3550250963Sachim		device_printf(sc->aac_dev,
3551250963Sachim	    	"No memory to add container bus\n");
3552250963Sachim		panic("Out of memory?!");
3553250963Sachim	};
3554250963Sachim	child = device_add_child(sc->aac_dev, "aacraidp", -1);
3555250963Sachim	if (child == NULL) {
3556250963Sachim		device_printf(sc->aac_dev,
3557250963Sachim	    	"device_add_child failed for container bus\n");
3558250963Sachim		free(sim, M_AACRAIDBUF);
3559250963Sachim		panic("Out of memory?!");
3560250963Sachim	}
3561250963Sachim
3562250963Sachim	sim->TargetsPerBus = AAC_MAX_CONTAINERS;
3563250963Sachim	sim->BusNumber = 0;
3564250963Sachim	sim->BusType = CONTAINER_BUS;
3565250963Sachim	sim->InitiatorBusId = -1;
3566250963Sachim	sim->aac_sc = sc;
3567250963Sachim	sim->sim_dev = child;
3568250963Sachim	sim->aac_cam = NULL;
3569250963Sachim
3570250963Sachim	device_set_ivars(child, sim);
3571250963Sachim	device_set_desc(child, "Container Bus");
3572250963Sachim	TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, sim, sim_link);
3573250963Sachim	/*
3574250963Sachim	device_set_desc(child, aac_describe_code(aac_container_types,
3575250963Sachim			mir->MntTable[0].VolType));
3576250963Sachim	*/
3577250963Sachim	bus_generic_attach(sc->aac_dev);
3578250963Sachim}
3579250963Sachim
3580250963Sachimstatic void
3581250963Sachimaac_get_bus_info(struct aac_softc *sc)
3582250963Sachim{
3583250963Sachim	struct aac_fib *fib;
3584250963Sachim	struct aac_ctcfg *c_cmd;
3585250963Sachim	struct aac_ctcfg_resp *c_resp;
3586250963Sachim	struct aac_vmioctl *vmi;
3587250963Sachim	struct aac_vmi_businf_resp *vmi_resp;
3588250963Sachim	struct aac_getbusinf businfo;
3589250963Sachim	struct aac_sim *caminf;
3590250963Sachim	device_t child;
3591250963Sachim	int i, error;
3592250963Sachim
3593250963Sachim	mtx_lock(&sc->aac_io_lock);
3594250963Sachim	aac_alloc_sync_fib(sc, &fib);
3595250963Sachim	c_cmd = (struct aac_ctcfg *)&fib->data[0];
3596250963Sachim	bzero(c_cmd, sizeof(struct aac_ctcfg));
3597250963Sachim
3598250963Sachim	c_cmd->Command = VM_ContainerConfig;
3599250963Sachim	c_cmd->cmd = CT_GET_SCSI_METHOD;
3600250963Sachim	c_cmd->param = 0;
3601250963Sachim
3602250963Sachim	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3603250963Sachim	    sizeof(struct aac_ctcfg));
3604250963Sachim	if (error) {
3605250963Sachim		device_printf(sc->aac_dev, "Error %d sending "
3606250963Sachim		    "VM_ContainerConfig command\n", error);
3607250963Sachim		aac_release_sync_fib(sc);
3608250963Sachim		mtx_unlock(&sc->aac_io_lock);
3609250963Sachim		return;
3610250963Sachim	}
3611250963Sachim
3612250963Sachim	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
3613250963Sachim	if (c_resp->Status != ST_OK) {
3614250963Sachim		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
3615250963Sachim		    c_resp->Status);
3616250963Sachim		aac_release_sync_fib(sc);
3617250963Sachim		mtx_unlock(&sc->aac_io_lock);
3618250963Sachim		return;
3619250963Sachim	}
3620250963Sachim
3621250963Sachim	sc->scsi_method_id = c_resp->param;
3622250963Sachim
3623250963Sachim	vmi = (struct aac_vmioctl *)&fib->data[0];
3624250963Sachim	bzero(vmi, sizeof(struct aac_vmioctl));
3625250963Sachim
3626250963Sachim	vmi->Command = VM_Ioctl;
3627250963Sachim	vmi->ObjType = FT_DRIVE;
3628250963Sachim	vmi->MethId = sc->scsi_method_id;
3629250963Sachim	vmi->ObjId = 0;
3630250963Sachim	vmi->IoctlCmd = GetBusInfo;
3631250963Sachim
3632250963Sachim	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3633250963Sachim	    sizeof(struct aac_vmi_businf_resp));
3634250963Sachim	if (error) {
3635250963Sachim		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
3636250963Sachim		    error);
3637250963Sachim		aac_release_sync_fib(sc);
3638250963Sachim		mtx_unlock(&sc->aac_io_lock);
3639250963Sachim		return;
3640250963Sachim	}
3641250963Sachim
3642250963Sachim	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
3643250963Sachim	if (vmi_resp->Status != ST_OK) {
3644250963Sachim		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
3645250963Sachim		    vmi_resp->Status);
3646250963Sachim		aac_release_sync_fib(sc);
3647250963Sachim		mtx_unlock(&sc->aac_io_lock);
3648250963Sachim		return;
3649250963Sachim	}
3650250963Sachim
3651250963Sachim	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
3652250963Sachim	aac_release_sync_fib(sc);
3653250963Sachim	mtx_unlock(&sc->aac_io_lock);
3654250963Sachim
3655250963Sachim	for (i = 0; i < businfo.BusCount; i++) {
3656250963Sachim		if (businfo.BusValid[i] != AAC_BUS_VALID)
3657250963Sachim			continue;
3658250963Sachim
3659250963Sachim		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
3660250963Sachim		    M_AACRAIDBUF, M_NOWAIT | M_ZERO);
3661250963Sachim		if (caminf == NULL) {
3662250963Sachim			device_printf(sc->aac_dev,
3663250963Sachim			    "No memory to add passthrough bus %d\n", i);
3664250963Sachim			break;
3665250963Sachim		};
3666250963Sachim
3667250963Sachim		child = device_add_child(sc->aac_dev, "aacraidp", -1);
3668250963Sachim		if (child == NULL) {
3669250963Sachim			device_printf(sc->aac_dev,
3670250963Sachim			    "device_add_child failed for passthrough bus %d\n",
3671250963Sachim			    i);
3672250963Sachim			free(caminf, M_AACRAIDBUF);
3673250963Sachim			break;
3674250963Sachim		}
3675250963Sachim
3676250963Sachim		caminf->TargetsPerBus = businfo.TargetsPerBus;
3677250963Sachim		caminf->BusNumber = i+1;
3678250963Sachim		caminf->BusType = PASSTHROUGH_BUS;
3679250963Sachim		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
3680250963Sachim		caminf->aac_sc = sc;
3681250963Sachim		caminf->sim_dev = child;
3682250963Sachim		caminf->aac_cam = NULL;
3683250963Sachim
3684250963Sachim		device_set_ivars(child, caminf);
3685250963Sachim		device_set_desc(child, "SCSI Passthrough Bus");
3686250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
3687250963Sachim	}
3688250963Sachim}
3689250963Sachim
3690250963Sachim/*
3691250963Sachim * Check to see if the kernel is up and running. If we are in a
3692250963Sachim * BlinkLED state, return the BlinkLED code.
3693250963Sachim */
3694250963Sachimstatic u_int32_t
3695250963Sachimaac_check_adapter_health(struct aac_softc *sc, u_int8_t *bled)
3696250963Sachim{
3697250963Sachim	u_int32_t ret;
3698250963Sachim
3699250963Sachim	ret = AAC_GET_FWSTATUS(sc);
3700250963Sachim
3701250963Sachim	if (ret & AAC_UP_AND_RUNNING)
3702250963Sachim		ret = 0;
3703250963Sachim	else if (ret & AAC_KERNEL_PANIC && bled)
3704250963Sachim		*bled = (ret >> 16) & 0xff;
3705250963Sachim
3706250963Sachim	return (ret);
3707250963Sachim}
3708250963Sachim
3709250963Sachim/*
3710250963Sachim * Once do an IOP reset, basically have to re-initialize the card as
3711250963Sachim * if coming up from a cold boot, and the driver is responsible for
3712250963Sachim * any IO that was outstanding to the adapter at the time of the IOP
3713250963Sachim * RESET. And prepare the driver for IOP RESET by making the init code
3714250963Sachim * modular with the ability to call it from multiple places.
3715250963Sachim */
3716250963Sachimstatic int
3717250963Sachimaac_reset_adapter(struct aac_softc *sc)
3718250963Sachim{
3719250963Sachim	struct aac_command *cm;
3720250963Sachim	struct aac_fib *fib;
3721250963Sachim	struct aac_pause_command *pc;
3722263340Sachim	u_int32_t status, reset_mask, waitCount, max_msix_orig;
3723263340Sachim	int msi_enabled_orig;
3724250963Sachim
3725250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3726263340Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
3727250963Sachim
3728250963Sachim	if (sc->aac_state & AAC_STATE_RESET) {
3729250963Sachim		device_printf(sc->aac_dev, "aac_reset_adapter() already in progress\n");
3730250963Sachim		return (EINVAL);
3731250963Sachim	}
3732250963Sachim	sc->aac_state |= AAC_STATE_RESET;
3733250963Sachim
3734250963Sachim	/* disable interrupt */
3735263340Sachim	AAC_ACCESS_DEVREG(sc, AAC_DISABLE_INTERRUPT);
3736250963Sachim
3737250963Sachim	/*
3738250963Sachim	 * Abort all pending commands:
3739250963Sachim	 * a) on the controller
3740250963Sachim	 */
3741250963Sachim	while ((cm = aac_dequeue_busy(sc)) != NULL) {
3742250963Sachim		cm->cm_flags |= AAC_CMD_RESET;
3743250963Sachim
3744250963Sachim		/* is there a completion handler? */
3745250963Sachim		if (cm->cm_complete != NULL) {
3746250963Sachim			cm->cm_complete(cm);
3747250963Sachim		} else {
3748250963Sachim			/* assume that someone is sleeping on this
3749250963Sachim			 * command
3750250963Sachim			 */
3751250963Sachim			wakeup(cm);
3752250963Sachim		}
3753250963Sachim	}
3754250963Sachim
3755250963Sachim	/* b) in the waiting queues */
3756250963Sachim	while ((cm = aac_dequeue_ready(sc)) != NULL) {
3757250963Sachim		cm->cm_flags |= AAC_CMD_RESET;
3758250963Sachim
3759250963Sachim		/* is there a completion handler? */
3760250963Sachim		if (cm->cm_complete != NULL) {
3761250963Sachim			cm->cm_complete(cm);
3762250963Sachim		} else {
3763250963Sachim			/* assume that someone is sleeping on this
3764250963Sachim			 * command
3765250963Sachim			 */
3766250963Sachim			wakeup(cm);
3767250963Sachim		}
3768250963Sachim	}
3769250963Sachim
3770250963Sachim	/* flush drives */
3771250963Sachim	if (aac_check_adapter_health(sc, NULL) == 0) {
3772250963Sachim		mtx_unlock(&sc->aac_io_lock);
3773250963Sachim		(void) aacraid_shutdown(sc->aac_dev);
3774250963Sachim		mtx_lock(&sc->aac_io_lock);
3775250963Sachim	}
3776250963Sachim
3777250963Sachim	/* execute IOP reset */
3778250963Sachim	if (sc->aac_support_opt2 & AAC_SUPPORTED_MU_RESET) {
3779250963Sachim		AAC_MEM0_SETREG4(sc, AAC_IRCSR, AAC_IRCSR_CORES_RST);
3780250963Sachim
3781250963Sachim		/* We need to wait for 5 seconds before accessing the MU again
3782250963Sachim		 * 10000 * 100us = 1000,000us = 1000ms = 1s
3783250963Sachim		 */
3784250963Sachim		waitCount = 5 * 10000;
3785250963Sachim		while (waitCount) {
3786250963Sachim			DELAY(100);			/* delay 100 microseconds */
3787250963Sachim			waitCount--;
3788250963Sachim		}
3789250963Sachim	} else if ((aacraid_sync_command(sc,
3790250963Sachim		AAC_IOP_RESET_ALWAYS, 0, 0, 0, 0, &status, &reset_mask)) != 0) {
3791250963Sachim		/* call IOP_RESET for older firmware */
3792250963Sachim		if ((aacraid_sync_command(sc,
3793250963Sachim			AAC_IOP_RESET, 0, 0, 0, 0, &status, NULL)) != 0) {
3794250963Sachim
3795250963Sachim			if (status == AAC_SRB_STS_INVALID_REQUEST)
3796250963Sachim				device_printf(sc->aac_dev, "IOP_RESET not supported\n");
3797250963Sachim			else
3798250963Sachim				/* probably timeout */
3799250963Sachim				device_printf(sc->aac_dev, "IOP_RESET failed\n");
3800250963Sachim
3801250963Sachim			/* unwind aac_shutdown() */
3802250963Sachim			aac_alloc_sync_fib(sc, &fib);
3803250963Sachim			pc = (struct aac_pause_command *)&fib->data[0];
3804250963Sachim			pc->Command = VM_ContainerConfig;
3805250963Sachim			pc->Type = CT_PAUSE_IO;
3806250963Sachim			pc->Timeout = 1;
3807250963Sachim			pc->Min = 1;
3808250963Sachim			pc->NoRescan = 1;
3809250963Sachim
3810250963Sachim			(void) aac_sync_fib(sc, ContainerCommand, 0, fib,
3811250963Sachim				sizeof (struct aac_pause_command));
3812250963Sachim			aac_release_sync_fib(sc);
3813250963Sachim
3814250963Sachim			goto finish;
3815250963Sachim		}
3816250963Sachim	} else if (sc->aac_support_opt2 & AAC_SUPPORTED_DOORBELL_RESET) {
3817250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, reset_mask);
3818263340Sachim		/*
3819263340Sachim		 * We need to wait for 5 seconds before accessing the doorbell
3820263340Sachim		 * again, 10000 * 100us = 1000,000us = 1000ms = 1s
3821250963Sachim		 */
3822250963Sachim		waitCount = 5 * 10000;
3823250963Sachim		while (waitCount) {
3824263340Sachim			DELAY(100);		/* delay 100 microseconds */
3825250963Sachim			waitCount--;
3826250963Sachim		}
3827250963Sachim	}
3828250963Sachim
3829250963Sachim	/*
3830250963Sachim	 * Initialize the adapter.
3831250963Sachim	 */
3832263340Sachim	max_msix_orig = sc->aac_max_msix;
3833263340Sachim	msi_enabled_orig = sc->msi_enabled;
3834263340Sachim	sc->msi_enabled = FALSE;
3835250963Sachim	if (aac_check_firmware(sc) != 0)
3836250963Sachim		goto finish;
3837250963Sachim	if (!(sc->flags & AAC_FLAGS_SYNC_MODE)) {
3838263340Sachim		sc->aac_max_msix = max_msix_orig;
3839263340Sachim		if (msi_enabled_orig) {
3840263340Sachim			sc->msi_enabled = msi_enabled_orig;
3841263340Sachim			AAC_ACCESS_DEVREG(sc, AAC_ENABLE_MSIX);
3842263340Sachim		}
3843263340Sachim		mtx_unlock(&sc->aac_io_lock);
3844263340Sachim		aac_init(sc);
3845263340Sachim		mtx_lock(&sc->aac_io_lock);
3846250963Sachim	}
3847250963Sachim
3848250963Sachimfinish:
3849250963Sachim	sc->aac_state &= ~AAC_STATE_RESET;
3850263340Sachim	AAC_ACCESS_DEVREG(sc, AAC_ENABLE_INTERRUPT);
3851250963Sachim	aacraid_startio(sc);
3852250963Sachim	return (0);
3853250963Sachim}
3854